进程等待&进程替换
1.进程等待
1.1进程等待的必要性
- 子进程退出,父进程如果不管不顾,就可能造成僵尸进程的问题,进而造成内存泄漏
- 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程
- 最后,父进程派给子进程的任务完成的如何,我们需要知道。如:子进程运行完成,结果对还是不对,或者是否正常退出
- 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
—————————————————————————————————-------
- 普遍情况是让子进程先退出:
- 如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源
- 如果子进程先退出,父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。
1.1.1如何避免僵尸进程呢?
- 父进程通过 wait 和 waitpid 等函数等待子进程结束,这会导致父进程挂起
- 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收
- 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号
1.2进程等待的方法
1.2.1 wait
- 返回值:
成功返回被等待进程pid,失败返回-1。 - 参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
1.2.2 waitpid()
1.2.2.1(pid_t pid)参数(要等待的子进程的进程号)
1.2.2.2(int* status)参数(退出的子进程的状态)
- status是一个整型指针,其实在传参的时候,该参数是一个输出型参数
- wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
- 如果传递NULL,表示不关心子进程的退出状态信息。
- 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程
- status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):
1.2.2.3(int options)参数(设置当前的waitpid是阻塞的还是非阻塞的)
WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。
:若正常结束,则返回该子进程的ID。
- WNOHANG 非阻塞 如果在掉用非阻塞的waitpid接口的时候,如果等待的子进程没有退出,waitpid也不会等待,直接返回,执行后面逻辑
- 非阻塞需要搭配循环去使用: 如果非阻塞的接口在调用waitpid时,没有等待到子进程退出,则循环去调用waitpid接口
1.2.3 wait和waitpid的区别
—————————————————————————————
- wait等待的是任意一个子进程的退出(wait是一个父进程假设有很多子进程,任意一个退出,都会处理后调用返回)
- waitpid可以等待指定的子进程,也可以等待任意一个子进程,通过第一个参数确定(第一个参数pid== -1则表示等待任意)
—————————————————————————————
- wait是一个阻塞等待(wait如果没有子进程退出,则会一直等待)
- waitpid可以默认阻塞,也可以设置为非阻塞,通过第三个参数确定(第三个参数 option== 0表示默认阻塞;option==WNOHANG 则表示非阻塞)
总结:
waitpid(pid大于0,status,0)相当于wait接口
wait接口实现其实就是调用waitpid实现。
2.进程替换
2.1替换原理(也会发生写时拷贝)
- fork创建子进程:1.让子进程和执行父进程一部分代码:代码共享 2.让子进程执行和父进程完全不同的事:调用exce函数,代码也要发生写时拷贝
- 当进程调用另外一种exce函数时,该进程的用户空间和代码和数据将完全被新程序替换,从新程序启动的例程开始执行调用exce函数并不是创建新进程,所以调用exce前后该进程的id并没有改变
通过进程PCB当中的内存指针,找到进程虚拟地址空间当中的数据段和代码段,通过页表映射将数据段和代码段映射到新的程序的物理内存上,通俗讲,使用新的程序将之前的数据段和代码段进行更新。
程序替换是在当前进程pcb并不退出的情况下,替换当前进程正在运行的程序为新的程序(加载另一个程序在内存中,更新页表信息,初始化虚拟地址空间)
2.2替换函数(操作系统提供的六大接口)
- 操作系统只提供了
execve一个调用接口
,其余的五个函数都是execve的封装
2.3函数解释
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
- 如果调用出错则返回-1
- 所以exec函数只有出错的返回值而没有成功的返回值,因为成功的话,所有数据和代码都会被覆盖
2.4命名理解
file:可执行程序的名称,同时这个可执行程序要能在PATH环境变量当中找到,否则程序替换就失败了;同时也可以传递一个带有路径的可执行程序
argv:要给可执行程序传递的参数,第一个参数是该程序的名称, 以NULL结尾
- 总结:
- l(list):表示参数采用列表
- v(vector):参数采用数组
- p(path):有p就自动搜索环境变量PATH
- e(env):表示自己维护环境变量
2.5复习插眼(为了日后复习回忆知识):
-
1.excel(“/usr/bin/ls”,“ls”,“-l”,‘-a",NULL)之后的代码无法打印出来,因为替换成功,如果替换不成功(地址不对的情况),就会打印之后的程序
-
2.Makefile默认只生成一个可执行程序,默认是自顶向下扫描遇到的第一个目标
-
3.exec系列函数调用系统的程序,也可以调用自己的程序
-
4.传入一串字符串,通过子进程分析字符串,父进程利用while循环不断的创建子进程,创建一个子进程就等待它分析完毕,分析完毕由于子进程其它的代码被替换以致结束,而父进程外边还有while循环,所以这样生成一个不断循环的过程