深度解析Linux进程管理

0 摘要

上一次的文章https://blog.csdn.net/CZHLNN/article/details/114534969?spm=1001.2014.3001.5501已经介绍了进程的概念,那么这篇文章我打算从进程的整个生命周期去讲解进程控制 ----> 进程的创建、进程终止、进程等待、进程程序替换这个四个方面去解析。

1 进程创建

1.1 fork 函数

在 Linux 中fork函数是非常重要的函数,它从已经存在的进程中创建一个新的进程。新进程为子进程,而原进程为父进程。函数的特性如下:

在这里插入图片描述
下面是一段在一个进程中创建子进程的示例代码
在这里插入图片描述
运行结果如下:
在这里插入图片描述

子进程创建的过程其实可以这样描述:从调用系统调用 fork 后就有了子进程,fork 创建子进程是以父进程为模板的,创建成功后给子进程返回0给当前进程(父进程)返回子进程的进程 id,内核所做的工作是:

  • 分配新的内存块和描述进程的数据结构给子进程
  • 将父进程部分数据结构内容拷贝到子进程
  • 添加子进程到系统进程列表中
  • fork 返回,开始调度器调度

注意:fork 之前父进程独立执行,fork之后,父子两个执行流分别运行。且 fork 之后,谁先执行完全由调度器决定。

1.2 写时拷贝

通常,父子进程代码共享,同时在父子进程都不写入数据的时候,数据也是共享的,当任意一方执行写入数据的操作的时候,便以写时拷贝的方式各自拷贝一份写入数据大小的副本。具体看下图。
在这里插入图片描述

当父进程创建子进程时,OS 将代码和数据都设置为只读,一旦父/子进程想要写入数据,就会触发写时拷贝,拷贝要写入的数据到内存的其他位置,就如上图右边的部分,写时拷贝技术也保证了进程间的独立性。

1.3 fork 常规用法

  • 一个父进程希望复制自己,使父子进程同时执行不同的代码段(逻辑)。例如,父进程等待客户端请求,生成子进程来处理请求。
  • 一个进程要执行一个不同的程序。例如子进程从frok 返回后,调用 exec 函数。

1.4 fork 调用失败的原因

  • 系统中有太多的进程
  • 实际用户的进程数达到了上限

1.5 总结

如何理解进程创建?如何理解 fork ?
在这里插入图片描述

2 进程终止

2.1 进程退出情况

  • 代码运行完毕,结果正确
    在这里插入图片描述

  • 代码运行完毕,结果不正确
    在这里插入图片描述

  • 代码异常终止
    在这里插入图片描述
    在这里插入图片描述

2.2 进程常见退出方法

正常终止(可以通过echo $? 查看进程退出码)
在C/C++ 的函数设计中 0 一般代表是正确的

  • 从main 函数中返回 return 0;
  • 调用 exit
  • 调用_exit

return 和 exit 的区别

  • exit:终止整个进程,任何地方调用,都会终止,且 exit(数字),数字是进程的退出码
  • return:叫终止函数。只有在main 函数里才是终止函数,在其他的函数是代表返回值main 中 的 return 数字,数字是进程的退出码

exit 和 _exit的区别:

  • exit 会刷新缓冲区里面的数据,而_exit 不会。

异常退出

  • Ctrl + c ,信号终止,即让OS把这个进程干掉,后面的信号的文章会谈到的。

3 进程等待

3.1 进程等待的必要性

  • 首先,子进程退出,父进程如果不管不顾的话,就可能造成"僵尸进程"的问题,进而造成内存泄露。
  • 其次,进程一旦变成僵尸状态,那就刀枪不入,“杀入不眨眼” 的 kill -9 也无能为力,因为谁也无法杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
  • 父进程要通过进程等待的方式,获取子进程的退出信息,触发OS去回收子进程的资源。即阻塞等待:阻塞自己,等待子进程执行完。

3.2 进程等待的方法

3.2.1 wait

在这里插入图片描述
返回值

  • 成功返回被等待进程的pid,失败返回-1

参数

  • 输出型参数,获取子进程的退出状态,不关心则可以设置为NULL

在这里插入图片描述

3.2.1 waitpid

在这里插入图片描述
pid_t waitpid(pid_t, int * status, int options);

  • 返回值:

当正常返回的时候,waitpid 返回收集到的子进程的进程ID;
如果设置了WNOHANG,而调用中,waitpid发现此时此刻没有已经退出的子进程可收集,则返回0
如果调用中出错,这时errno会被设置成相应的值以提示错误所在;

  • 参数

pid :
pid = -1,等待任意一个子进程,与wait 等效
pid > 0,等待其进程id 与 参数pid相等的子进程
status:
此参数为输出参数,子进程退出变成僵尸进程,父进程调用 waitpid 一方面读取子进程的退出码和退出的信号信息,所以要传入一个输出型参数,int * status,这个status中的值其实是在子进程的PCB中的,通过对status的分析,可以得到子进程的退出状态信息,代码跑完结果是对的,代码跑完结果是不对的,进程异常终止。
我们可以看看内核进程PCB源码里面的退出码和信号信息:
在这里插入图片描述
WIFEXITED(status):若为正常终止子进程(包括:代码跑完结果是正确的,代码跑完结果是不正确的),则为真,是用来查看进程是否是正常退出的。
WEXITSTATUS(status):若上面的表达式为真(非0),提出子进程退出码,是用来提取子进程的退出码的。

options:

WNOHANG:若 pid 指定的子进程没有结束,则waitpid()函数返回0,不给予等待。若正常结束则返回子进程的的id,用于非阻塞式等待。

获取子进程status

  • wait 和 waitpid ,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息,否则操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status 不能简单的当做整型来看待,可以当做位图来看待,具体细节如下图(只研究低16比特位):
    在这里插入图片描述
    waitpid的使用示例:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

4 进程程序替换

4.1 替换原理

fork创建子进程后执行的是和父进程相同的程序(但是有可能是非常有可能执行不同的代码分支),子进程往往要调用 exec 函数以执行另一个程序。当进程调用一种 exec 函数时,该进程的用户空间代码和数据完全被新程序替换,发生写时拷贝,调用 exec 不创建新的进程,所以调用exec前后该进程id并未改变。

在这里插入图片描述

4.2 如何替换

通过调用 exec 开头的函数完成替换
在这里插入图片描述函数解释

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回 -1。
  • 所以exec函数只有出错的返回值没有成功的返回值。

命名理解

  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : 有 p 自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量
    在这里插入图片描述
int main()
{

 char *const argv[] = {"ps", "-ef", NULL};
 char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
 
 execl("/bin/ps", "ps", "-ef", NULL);
 
 // 带l的需要使用列表传参,并以NULL结尾
 // 带p的,可以使用环境变量PATH,无需写全路径
 execlp("ps", "ps", "-ef", NULL);
 
 // 带e的,需要自己组装环境变量
 // 带v的需要输赢数组传参,数组以NULl结尾
 execle("/bin/ps", "ps", "-ef", NULL, envp);
 execv("/bin/ps", argv);
 
 // 带p的,可以使用环境变量PATH,无需写全路径
 execvp("ps", argv);
 
 //带e的需要自己组装环境变量
 execv("/bin/ps",argv,envp);
 
 exit(0);
 }

事实上,只有execve是真正的系统调用,其他五个函数最终都调用 execve

在这里插入图片描述
在这里插入图片描述
在后面的文章里我会写一个迷你shell解析器,敬请期待。*

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值