赞
踩
书接上回,我们继续来讲解进程控制的后面内容!
定义:进程等待是指父进程等待子进程结束,从而回收子进程资源
我们知道如果子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏,这种情况出现在父进程先退出的情况,但是如果子进程先退出,为什么就不会出现僵尸进程呢?
这就是我们这里要讲的进程等待!!!
联系之前我们学的内容,fork之后,我们是不知道父子进程谁先运行的,但是我们通过进程等待可以知道一定是子进程先结束的,其实进程等待也分为两种:
这是默认的进程等待方式,即父进程在子进程未结束的时候会一直等待而什么都不会执行,直到子进程返回pid
如果子进程pid:
>0:表示为等待成功
<0:表示为等待失败
下面我们来看看等待函数:
头文件:<sys/wait.h>
下面我们来重点看看三个参数:
第一个参数pid:表示为子进程的pid
第二个参数status:注意要传的是地址,类型为int,这个我们后面会重点讲解‘
第三个参数options:表示是什么类型等待,0---表示为阻塞等待
重点:status:
之前我们讲解过退出码和退出信号,status的后十六位中前8为表示为退出码,后八位中后7位表示为退出信号,还有一位表示为core dumpling标志(这个我们之后再讲)
下面我们来讲解如何使用三个函数:
- //头文件:
- #include <sys/wait.h>
-
- pid_t wait(int* status);
-
- //返回值:
- //成功返回被等待进程pid,失败返回-1
- //参数:
- //输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
- //头文件:
- #include <sys/wait.h>
-
- pid_ t waitpid(pid_t pid, int *status, int options);
-
- //返回值:
- //当正常返回的时候waitpid返回收集到的子进程的进程ID;
- //如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
- //如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
- //参数:
- //pid:
- Pid=-1,等待任一个子进程。与wait函数等效。
- Pid>0.等待其进程ID与pid相等的子进程。
- status:
- WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
- WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
- options:
- WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
- 程的ID

- //头文件:
- #include <sys/wait.h>
-
- pid_ t waitpid(pid_t pid, int *status, int options);
- //返回值:
- //当正常返回的时候waitpid返回收集到的子进程的进程ID;
- //如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
- //如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
- //参数:
- pid:
- Pid=-1,等待任一个子进程。与wait等效。
- Pid>0.等待其进程ID与pid相等的子进程。
- status:
- WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
- WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
- options:
- WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
- 程的ID

注意点:
1.如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
2.如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
3.如果不存在该子进程,则立即出错返回
与阻塞等待不同,非阻塞等待过程中,父进程并不会像之前一样,什么都不做,而是继续执行后序代码
对于非阻塞等待我们可以将第三个参数options设置为WNOHANG
下面我们回到status:
对于其低16位有以下组合方式:
原理:
什么是进程替换???
实际上就是通过新的数据和代码替换原来的数据和代码
下面我们来回答一下几个问题:
1.进程替换后会不会创建新的进程???
实际上是不会的,而是通过原来的PCB和页表来修改再到CPU上处理
2.既然进程替换不会创建新的进程而是用原来的进程,那么我们导入新的数据和代码时,是先处理好PCB和页表还是先导入好数据呢???
实际上我们只要知道进程替换的本质是加载,那么我们会发现必须要处理好PCB和页表
3.进程的pid会不会改变???
进程都没有改变,其pid肯定也不会改变
下面我们来认识下面几种进程替换函数:
解释:
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值。
我们发现他们的共同点都是exec开头后面不一样,所以便于记忆我们结合下表;
总结:
l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量
注意我们可以替换到系统指令,其实也是可以替换成我们自己写的程序
下面我们结合进程等待来一起来学习:
- #include <iostream>
- #include <unistd.h>
- #include <sys/wait.h>
- #include <stdlib.h>
- #include <sys/types.h>
-
- using namespace std;
-
-
- int main()
- {
- pid_t id=fork();
- if(id==0)
- {
- cout<<"exec begin\n";
- execl("/usr/bin/ls","ls","-a",NULL);
- cout<<"exec end\n";
- exit(1);
- }
- else
- {
- pid_t pid=waitpid(id,nullptr,0);
- if(pid>0)
- {
- cout<<id<<endl;
- cout<<"wait sucess\n";
- exit(1);
- }
-
- }
- return 0;
- }

该代码我们将进程替换和进程等待结合学习,输出结果:
实际上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节
下面我们写一个图看:
愿意的话你也可以写一个程序去测试下进程替换不仅可以替换成系统的进程,而且写成自己的程序也行,但是注意:替换后的进程不会在输出之前进程后序的内容
感谢大家的支持!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。