了解Linux对进程的创建到终止

一、进程创建

fork函数

在Linux中,fork函数是OS提供的系统调用接口,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程.

#include <unistd.h>

pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id,出错返回-1

进程调用fork,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程。
  • 将父进程部分数据结构内容拷贝至子进程。
  • 添加子进程到系统进程列表当中。
  • fork返回,开始调度器调度。
    在这里插入图片描述当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以开始它们自己的旅程,看如下程序。

在这里插入图片描述在这里插入图片描述
有没有发现Before打印了一次,After输出了两次,这个时候说明了进程创建成功了
在这里插入图片描述

fork函数的返回值

子进程返回0,
父进程返回的是子进程的pid。

那为什么,子进程返回的是0呢,而父进程fork后返回的是子进程的pid呢?
因为一个进程的子进程可以多于一个,没有一个函数使一个进程可以获得其所有子进程的进程id 。 对子进程来说,之所以fork返回0给它,是因为它随时可以调用 getpid ()来获取自己的pid;

再来看一个样例:
在这里插入图片描述

在这里插入图片描述

  • 注意: fork之后,父进程和子进程谁先执行完全由调度器决定。

有没有发现 fork有两个返回值,父进程和子进程的id不一样,但是奇怪的是他们两个的地址是一样的,同一块地址储存两个不一样的值?
一下子出现了许多问题,来我们一个一个解答:

为什么fork函数有两个返回值?

父进程调用OS提供的fork函数后,fork函数内部将会进行一系列操作,给子进程PCB、进程地址空间、页表,创建完后,此时fork结束时,要进行return语句已经不是只有父进程了,而是两个执行流来执行return,fork的返回值就有两个了。

虚拟地址空间

上面的图中,发现父进程与子进程的id是同一块地址,但是值不同,这一块储存的内存是物理内存吗?
答案是,它一定不是物理内存。
关于什么是虚拟地址空间就不细讲,通俗的来讲两个id指向的虽然是同一个虚拟地址空间,但是会通过页表映射出的物理内存不一样

在这里插入图片描述
上面的表不严谨,纯粹就是为了更好的理解,实际更加复杂,这样的就能解释id为什么指向的是同一地址,值却不一样

写时拷贝

进入fork到return语句时,子进程的各个工作都准备好了,子进程和父进程的数据和代码是共享的,当子进程要对数据更改时,OS会把父进程的数据拷贝一份,放到新的物理内存,在进行修改,这就是写时拷贝,发生在数据区中.

在这里插入图片描述
这样的操作优点:

  • 可以使父子进程不会互相影响->进程具有独立性.多进程运行,需要独享各种资源,多进程运行期间互不干扰,不能让子进程的修改影响到父进程。

  • 对于操作系统来说,还没有对子进程数据修改时,父子进程的数据区是同一份,可以高效的使用内存空间。

既然数据区会发生写时拷贝,那么代码区也会发生写时拷贝,一般是在进行程序替换时,代码区会发生写时拷贝。

进程终止

进程退出情况

一个进程退出会有三种情况:

  1. 代码运行正常结束,结果正确。
  2. 代码运行正常结束,结果错误。
  3. 代码运行出现异常提前结束,结果对错意义

进程退出码

如何判断进程结果是否错误,我们只有看进程的退出码,那什么是退出码呢?
Linux 下进程的退出包括了正常退出和异常退出
正常退出包括:
A) main() 函数中通过 return 返回;
B) 调用 exit() 或者 _exit() 退出。
异常退出包括了 :
A) abort() 函数;B) 收到了信号退出。

不管是哪种退出方式,系统最终都会执行内核中的同一代码,并将进程的退出方式以返回码的方式保存下来。
在这里插入图片描述
通过echo $? 来查看进程退出码:

在这里插入图片描述
一般以0表示代码成功执行完毕,以非0表示代码执行过程中出现错误,这就是为什么我们都在main函数的最后返回0的原因。非0代表其他含义
在这里插入图片描述
在这里插入图片描述
可以通过strerror来看看退出码分别对应的信息:
在这里插入图片描述
在这里插入图片描述

还有好多没有显示出来,我打印出来的共有134条退出码对应的错误信息,不同环境下相同的退出码的字符串含义可能不同。

正常退出

return退出

一般常见在main函数中使用return退出进程。

exit()与_exit()函数

使用exit函数退出进程也是我们常用的方法,exit函数可以在代码中的任何地方退出进程,并且exit函数在退出进程前会做一系列工作:

  1. 执行用户通过atexit或on_exit定义的清理函数。
  2. 关闭所有打开的流,所有的缓存数据均被写入。
  3. 调用_exit函数终止进程。

在这里插入图片描述
_exit()与exit()是包含关系

在这里插入图片描述

修改下上面的代码
在这里插入图片描述

异常退出

两种情况:

情况一:向进程发生信号导致进程异常退出。

在进程运行过程中向进程发生kill -9信号使得进程异常退出,或是使用Ctrl+C使得进程异常退出等。

情况二:代码错误导致进程运行时异常退出。

例如,代码当中存在野指针问题使得进程运行时异常退出,或是除0操作使得进程运行时异常退出等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值