【Linux】进程的控制①之进程创建与进程退出

一 、进程的创建

1、fork函数

fork函数功能:从已经存在的进程中创建一个新进程。新进程为子进程,原进程为父进程。

fork函数创建进程过后,父子进程代码和数据是共享的。在前面也讲过。

2.函数的返回值

如果进程创建成功,给父进程返回子进程的pid(进程标识符),给当前的进程返回0,创建失败返回-1.返回值类型是pid_t类型。

下面我们来写一段代码手动创建一个进程:

 #include<stdio.h>
  2 
  3 #include <sys/types.h>
  4 #include <unistd.h>
  5 
  6 
  7 int main()
  8 {
  9   pid_t id = fork();//创建子进程
 10   if(id == 0)
 11   {
 12     //子进程
 13     printf("i am a child process,mypid:%d  myppid:%d \n",getpid(),getppid());
 14     sleep(1);
 15   }
 16   //父进程
 17 
 18     printf("i am a father process,mypid:%d  myppid:%d \n",getpid(),getppid());                                                                                                             
 19     sleep(1);
 20 
 21 
 22   return 0;
 23 }

创建进程过后,父进程与子进程是两个独立的进程,但是代码和数据共享,如果当其中一个数据改变或者写入数据的时候,会发生写实拷贝。父子进程的代码区域是共享的,但是每个进程维护自己的数据区域。也就是说子进程会继承父进程的大部分属性进程的pcb,进程的进程地址空间,进程的页表等等,这些使得父子进程几乎共同拥有数据区和代码区。

fork函数的返回值由一个变量接收,但是却保存两个值原因:

①return 可以返回两次,使得父子进程读到的id有两个值

进程调用 fork ,当控制转移到内核中的 fork 代码后,内核做:
  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度
  • fork 之前父进程独立执行, fork 之后,父子两个执行流分别执行。注意, fork 之后,谁先执行完全由调度器 决定。
那么这里就很好理解,我们知道的一个函数当执行到return的时候,这个函数的主要工作或者主要逻辑就已经完成了,fork也是函数,在执行返回pid之前,就已经以调用进程也就是父进程为模版创建好了子进程,将子进程对应的pcb放入了运行队列里面,等待调度,那么此时,父子进程共享代码,分别执行一次return,就可以返回两次,而父子进程的细微差异性让return的值不同,那么当我们使用同一个变量来接收,为什么一块地址会保存两个值呢,这就是第二个原因,写实拷贝和进程的地址空间所做的工作。

②写实拷贝,使得父子进程读到的值都对自身有意义

我们创建进程,就是为了让进程独立的去帮助我们完成工作,那么代码是只读的,父子进程共享代码执行没有问题,可以通过if语句来执行不同的功能,但是如果,两个进程要对代码中或者程序中的数据进行修改,那么势必会影响互相的工作,但是进程是互相独立的呀,怎么能够让彼此乱改数据呢,所以,为了防止这种情况,我们的操作系统内部就存在一种解决机制,我们称为写实拷贝:

当父子进程执行时,有一方要修改数据,就在内存重新开辟空间,然后修改子进程的页表映射关系,让父子进程拥有不同的数据区域,自此以后,两个进程就互相有自己的数据区域,随便修改互相不影响。这就是为什么我们的一个返回值接收变量里面可以存储两个值,并且两个值都有意义的原因。但是我们发现父子进程打印的这个id变量的地址都是一样的,这是因为我们弟弟进程有进程地址空间存在,我们用户所看到的地址,就是打印出来这些,并不是真正的地址,而是虚拟地址,操作系统中存在页表,建立了虚拟内存到实际内存空间的映射关系,由于子进程继续了父进程1页表也相应的继承了页表的映射关系,但是只修改了虚拟内存到实际内存的映射,所以父子进程打印出来的虚拟地址是一样的。

下面我们要来说一下关于写实拷贝的几个问题

a.为什么要写实拷贝,而不是在创建子进程的时候直接就将空间给子进程开辟好?

我们为什么非要等到子进程要进行数据写入了操作系统才给我们的子进程去开辟空间,建立映射呢?直接在创建的时候就做好这些工作呢?那么我们说,子进程创建出来是为了帮助我们完成工作,但是这个工作需不需要新的数据,会不会产生新的数据,要不要访问内存等等都还是不确定的,那么如果子进程不使用,操作系统提前给子进程分配内存,甚至单独给子进程拷贝一份代码,造成资源浪费,所以,当子进程在尝试写入的时候,此时会发生缺页中断,操作系统介入,创建内存,建立映射。其次,如果我们在创建子进程时就给子进程分配空间,那么创建的过程也会变长,因为除了拷贝进程的pcb、页表、进程地址空间还要拷贝进程数据,fork函数的成本增加,效率变得低下。

b.为什么给子进程分配空间的时候,老的数据要拷贝过来,而不是直接写入新的数据,开辟空白空间?

当我们的子进程要进行数据写入的视乎,此时子进程要写入新的数据了,操作系统给子进程拷贝老的数据过来干什么,多此一举。直接给空间就好了,写实,给子进程开辟空间没有问题,但是为什么要将父进程数据进行拷贝?首先,我们要理解写入数据的本质就是对数据进行增删查改,写入数据不一定能够做到对原来的数据进行完全覆盖,如果只开辟空间,怎么能够知道原来的数据是多少,就比如我们子进程要对父进程的某一个数值进行++操作,不知道原来的数据怎么行。所以拷贝也是操作系统为了增加确定性的策略,能在应用层规避很多问题。

c.写实拷贝是如何做到的?怎么做到当子进程需要,操作系统就过来开辟空间的?

页表除了有虚拟地址和实际地址外,实际上还有这每一个映射条目对应的权限。

有时当我们写代码的时候,要对内存进行数据写入的时候,此时程序会报错不不让我们写入,就是因为有权限在限制。

比如执行以下代码:

char * str = "hello world";
*str = 'H';

 代码的本意是想将原先字符串的首字母改成大写,但是我们执行带啊时候:

是异常的,可以编译通过,但是运行挂掉。我们在学习语言的时候我们说,字符串常量是具有常属性的,在字符常量区不能够对常量数据进行更改。那么为什么对我们的常量区就不可以改呢,换句话说常量区是怎么维护你的常性的。

char* str = "hello world";

str里面保存的地址是虚拟地址,当我们要对这个地址空间里面的内容进行修改,也就是赋值,那么必然要伴随着虚拟地址到物理地址的转换,赋值的本质就是写入数据,当写入时,转换映射的条目位的权限为只读,所以也就不可以修改。 那么,我们在写代码的时候有时候会在这些不可以改变的常量前去加const来修饰,不是因为我们加了const使得它不可修改,而是其本身的映射权限就是不可修改,我们加上const,只是为了提前帮我们发现问题,就是说编译器在编译的时候,如果遇到这个const修饰的值被修改,那么编译器就知道这个值不可修改,就报错,而不是等到进程运行起来了崩溃再去找原因,这是一种防御性策略。

 那么我们的子进程写实拷贝的过程粗略过程是怎么样的呢:

首先,代码区域为只读权限没有问题,而对于数据区域,在创建子进程的时候,操作系统会将父子进程数据映射在页表中的相应权限修改为r,都是只可以读。当子进程或者父进程尝试进行写入数据的时候,由于权限不允许,此时就会出问题报错,操作系统就会过来查看,此时操作系统对报错进行种类判断,发生缺页中断,那么之前将数据区域的映射条目权限设置为r就是为了触发报错。让os过来,知道我们的进程要写入数据,此时就可以根据进程需要及时为进程开辟内存建立映射,将映射条件由r改为rw,此时进程可以写入数据,而操作系统也就完成了写实拷贝。

3.一般情况下创建子进程的情形:

循环查看当前运行进程的指令:

  while :; do ps axj | head -1 && ps axj | grep 可执行程序名 | grep -v grep; sleep 1; done

4.fork调用失败原因 

  • 系统中有太多的进程
  • 实际用户的进程数超过了限制

二、进程退出

1 函数返回值与进程退出码

我们写C语言或者c++的时候,总喜欢在main函数结束时写上:

                             return 0;

这样的语句,那么可以不可以return 1,return 2 呢?首先我们来说一下为什么会有return 0,main函数也是一个函数,会有返回值,也会被调用,当我们的代码运行起来变成进程,我们自然而然会关心,这个进程运行的结果,我们编写代码很多时候是希望代码给我们返回一个结果的,那么我们或者操作系统怎么知道这个进程运行得怎么样,运行的效果好不好,我们的bash怎么知道当前运行的指令对不对。所以main函数的返回值,作为程序执行的最后一条语句,也叫作进程的退出码,可以由用户自定义,由main函数的返回值来判断进程的执行情况,所以这个返回值叫做进程的退出码,为0表示成功,非0表示失败。当我们程序执行失败,我们最关心的东西是错误的原因是什么,所以非0的数字:1,2,3.。。。。不同的数字代表了不同的错误原因。这个数字由系统去判别。 

可以使用一个命令来查看最近一个进程退出时的退出码:

echo $?:记录最忌一次进程退出时候的退出码

 

那么作为用户我们是不懂数字背后的意义,计算机知道的话,我们想看见错误码对应的原因:

①我们可以使用系统自带的 方法,将错误码:ernum进行转化为错误原因:

C 库函数 char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。strerror 生成的错误字符串取决于开发平台和编译器。 

写一段代码:

25 #include<string.h>
 26 int main()
 27 {
 28 
 29   int i = 200;
 30   for(i = 0;i<200;i++)
 31   {
 32     printf("error[%d]:%s\n",i,strerror(i));                                                                                                                                                
 33   }                          
 34   return 0;                  
 35 }                            
~
  

 当前操作系统有133个错误,错误码的个数由系统决定

②可以自己定义我们的错误原因:

结论:mian函数return返回时,表示进程已经退出,return后跟的数字是进程退出码,可以自行设置退出码的字符串意义。

③普通函数的return

void Print()
{

printf("%d\n");
return 0;
}

普通函数的return 仅仅表示函数调用完毕。所以除了进程退出还有函数退出,函数退出时,调用这个函数的地方肯定是想要知道调用这个函数的执行情况,成功还是失败,失败原因是什么,所以一般情况下,函数都会有返回值,但是不是所有函数都能够用函数返回值来表示执行结果成功或者执行情况。

比如fopen函数:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/580987.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PCB设计:差分线

1、差分线的定义 差分线是两条平行的、等长的走线,传输相位差180度的同一个信号,是一根传输“+信号”,一根传输“-信号”。两个信号相减得到2倍强度的有用信号。而两根信号线上的干扰信号相减之后也就没了。 2、差分线的优势 差分信号和普通的单端信号走线相比,最明显的优…

ftp: connect :连接被拒绝(win10)

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 使用命令测试显示&#xff1a;“FTP拒绝连接” 二、原因分析 第一时间查看ftp服务启动了没哟&#xff0c;相关服务有2个&#xff0c;一个是【Microsoft FTP Service】一个是【IIS 管理服务】&#xf…

为什么堆排序的时间复杂度是O(N*logN)?

目录 前言&#xff1a; 堆排序&#xff08;以排升序为例&#xff09; 步骤&#xff08;用大根堆&#xff0c;倒这排&#xff0c;排升序&#xff09;&#xff1a; 1.先把要排列的数组建立成大根堆 2.堆顶元素&#xff08;82&#xff09;和最后一个元素交换&#xff08;2&…

【详细讲解CentOS常用的命令】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

SpringMVC进阶(过滤器解决中文乱码,处理json以及文件上传下载)

文章目录 1.中文乱码处理1.引出问题1.恢复原来取消掉的属性绑定2.启动服务器&#xff0c;引出问题 2.自定义中文乱码过滤器1.MyCharacterFilter.java2.web.xml配置过滤器&#xff08;这个解决乱码的过滤器放到最前面&#xff09;3.结果展示 3.使用Spring过滤器处理&#xff08;…

外面收费999元的虚拟小众产品,0门槛,单号100-300元

近日&#xff0c;我抽出时间参加了一些线下聚会&#xff0c;与互联网行业的朋友们进行了面对面的交流。有时候&#xff0c;独自努力不如与他人分享想法&#xff0c;因为别人的建议可能会让你豁然开朗&#xff0c;解决遇到的难题。在互联网行业中&#xff0c;交流和沟通至关重要…

【WinForm】怎样以管理员身份运行安装程序

当开发的程序需要安装时&#xff0c;有时默认安装会遇到访问被拒绝的问题。为了解决这个问题&#xff0c;需要以管理员身份运行程序。 文章目录 应用程序UAC清单管理员身份 安全调试ClickOne 应用程序 打开项目属性,找到应用程序下的清单, 设置好如下图 UAC清单 再找到项目…

python 调试 c++源码

1. gdb常用调试命令概览和说明 2. 编译c库设置Debug模式 cmake设置debug 在CMake中设置debug模式通常意味着启用调试信息和优化。以下是一个简单的CMakeLists.txt文件示例&#xff0c;展示了如何设置项目以便在Debug模式下构建&#xff1a; cmake_minimum_required(VERSION 3…

深度学习| 注意力机制

注意力机制 为什么需要注意力机制Seq2Seq问题Transfomer Attention注意力机制分类软硬注意力注意力域 为什么需要注意力机制 这个可以从NLP的Seq2Seq问题来慢慢理解。 Seq2Seq问题 Seq2Seq&#xff08;Sequence to Sequence&#xff09;&#xff1a;早期很多模型中&#xff…

Linux逻辑方式合并物理磁盘

在日常生活中&#xff0c;我们总是遇到一个文件太大&#xff0c;以至于我们的两个磁盘都装不下&#xff0c;这时我们就需要将两块物理磁盘逻辑化的连接在一起&#xff0c;把物理磁盘使用逻辑化的方法合并在一起&#xff0c;形成卷组&#xff0c;使得磁盘空间可以公用&#xff1…

Leetcode—1672. 最富有客户的资产总量【简单】

2024每日刷题&#xff08;120&#xff09; Leetcode—1672. 最富有客户的资产总量 实现代码 class Solution { public:int maximumWealth(vector<vector<int>>& accounts) {int ans 0;for(vector<vector<int>>::iterator it accounts.begin();…

西门子PLC常见疑难解答在这都能找到(含PLC无线通讯实例)

问&#xff1a;在WINCC中可以创建多少个消息类型&#xff1f; 答&#xff1a;WINCC中最多可以创建16个消息类型。 问&#xff1a;博途WINCC与经典版WINCC兼容嘛&#xff1f; 答&#xff1a;经典wincc和博图wincc Professional版本都不兼容 不能安装到一起的哦&#xff01; …

探讨mfc100u.dll丢失的解决方法,修复mfc100u.dll有效方法解析

mfc100u.dll丢失是一个比较常见的情况&#xff0c;由于你电脑的各种操作&#xff0c;是有可能引起dll文件的缺失的&#xff0c;而mfc100u.dll就是其中的一个重要的dll文件&#xff0c;它的确实严重的话是会导致程序打不开&#xff0c;系统错误的。今天我们就来给大家科普一下mf…

命令执行漏洞【2】vulhub远程命令执行漏洞复现

1.vulhub安装启动靶场环境 &#xff08;1&#xff09;s2-061开启靶场 &#xff08;2&#xff09;s2-059开启靶场 2.漏洞复现 &#xff08;1&#xff09;s2-061漏洞复现 github获取漏洞利用工具 开始利用 &#xff08;2&#xff09;s2-059漏洞复现 在linux特有临时目录/tmp下…

968.监控二叉树 树上最小支配集

法一: 动态规划 一个被支配的节点只会有三种状态 1.它本身有摄像头 2.他没有摄像头, 但是它的父节点有摄像头 3.他没有摄像头, 但是它的子节点有摄像头 我们 dfs(node,state) 记录在node节点时(以node为根的子树),状态为state下的所有最小摄像头 // 本身有摄像头就看左右孩子…

投影连接Samba服务

目录 0.创建【Samba服务】 1.下载・安装一个能连接【Samba服务】的播放器 2.配置语言 3.配置服务器连接 0.创建【Samba服务】 Linux&#xff08;Ubuntu&#xff09;中创建【samba】服务&#xff0c;用于和Windows系统之间共享文件_ubuntu samba-CSDN博客 1.下载・安装一…

《深入理解mybatis原理》 MyBatis的架构设计以及实例分析

《深入理解mybatis原理》 MyBatis的架构设计以及实例分析 MyBatis是目前非常流行的ORM框架&#xff0c;它的功能很强大&#xff0c;然而其实现却比较简单、优雅。本文主要讲述MyBatis的架构设计思路&#xff0c;并且讨论MyBatis的几个核心部件&#xff0c;然后结合一个select查…

Matlab进阶绘图第51期—带填充等高线的三维特征渲染散点图

带填充等高线的三维特征渲染散点图是填充等高线图与特征渲染三维散点图的组合。 其中&#xff0c;填充等高线图与特征渲染的三维散点图的颜色用于表示同一个特征。 由于填充等高线图无遮挡但不直观&#xff0c;特征渲染的三维散点图直观但有遮挡&#xff0c;而将二者组合&…

【C++杂货铺】二叉搜索树

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 二叉搜索树的概念 &#x1f4c1; 二叉搜索树的操作 &#x1f4c2; 二叉搜索树的查找 &#x1f4c2; 二叉搜索树的插入 &#x1f4c2; 二叉搜书树的删除 &#x1f4c1; 二叉搜索树的应用 &#x1f4c1; 二叉搜索树的…

vue3 h5模板

vue3的h5模板 基于vue3tsvantrem的h5模板 觉得帮到你了就给个start
最新文章