网网站设计网,网站构建培训,h5长图用什么软件做,网站开发 前端目录 前言
一、进程等待
二、如何进行进程等待
1.wait
2.waitpid
2.1第二个参数
2.2第三个参数
3. 等待多个进程
三、为什么不用全局变量获取子进程的退出信息 前言
前面我们花了大量的时间去学习进程的退出#xff0c;退出并不难#xff0c;但更深入的学习能为本…目录 前言
一、进程等待
二、如何进行进程等待
1.wait
2.waitpid
2.1第二个参数
2.2第三个参数
3. 等待多个进程
三、为什么不用全局变量获取子进程的退出信息 前言
前面我们花了大量的时间去学习进程的退出退出并不难但更深入的学习能为本章进程等待打好基础因此没看过的小伙伴可以先学习进程退出。
一、进程等待 之前讲过子进程退出父进程一直在运行不对子进程进行回收就可能造成‘僵尸进程’的问题进而造成内存泄漏。 另外进程一旦变成僵尸状态那就刀枪不入“杀人不眨眼”的kill -9 也无能为力因为谁也没有办法 杀死一个已经死去的进程。 父进程派给子进程的任务完成的如何我们需要知道。如子进程运行完成结果对还是不对 或者是否正常退出。 父进程通过进程等待的方式可以获取子进程退出的信息。虽然不是一定要获取但是得有这个功能 二、如何进行进程等待
1.wait
我们看看2号手册中的wait函数他可以等待任意一个子进程的退出参数是int类型的指针等待成功返回子进程的pid失败返回-1。 我们使用如下代码进行进程等待这里wait的参数先给NULL代表不关心子进程退出的状态后续会再提到。子进程运行5秒后变成僵尸状态父进程先休眠10秒再去调用wait函数。
#includestdio.h
#includeunistd.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hvoid Work()
{int cnt 5;while(cnt){printf(我是子进程, pid: %d, ppid: %d, cnt: %d\n,getpid(),getppid(),cnt--);sleep(1);}
}int main()
{pid_t id fork();if(id 0){//childWork();exit(0);}else{sleep(10);//fatherpid_t rid wait(NULL);if(rid id){printf(等待成功,pid: %d\n,getpid());}}return 0;
}我们写了一个脚本来监控进程的运行情况代码如下注意myprocess是我设置的进程名 while :; do ps ajx | head -1 ps ajx | grep myprocess | grep -v grep; sleep 1; echo ###################; done
结果发现0-5秒中父子进程正常运行5-10秒中子进程变成了僵尸状态父进程此时在sleep并没有回收子进程10秒后父进程sleep结束wait函数等到了子进程于是将子进程回收了同时父进程也运行完毕。 由此我们可以得知 进程等待能回收子进程僵尸状态。 还有一个结论在父进程进行等待的时候如果子进程还没有处理完那么父进程必须在wait上进行阻塞等待直到子进程僵尸wait自动回收再继续执行后续代码。这可以通过打印的方式查看。就类似于scanf需要等待用户输入一样用户不输入就一直在这里阻塞着直到输入后才继续往后执行。 一般而言父子进程谁先运行我们不知道但能知道一般都是父进程最后退出多进程由父进程发起也由父进程统一回收 2.waitpid
wait是等待任意一个子进程而waitpid可以等待指定的那一个第一个参数传等待子进程的pid代表等待这个进程传-1代表等待任意进程。 第二个参数和wait的参数一样第三个参数也先不管设置为0代表默认阻塞等待。 将上面的代码从wait修改为waitpid因为我们只fork了一次只创建了一个子进程因此如下修改即可。 2.1第二个参数
重点我们得讲解一下第二个参数 status 他是输出型参数我们可以随便定义一个int变量将变量的值传递给第二个参数waitpid会将子进程退出码和信号写到这个变量里。
这里我们将子进程的退出码设置为10看看打印出来的status值为多少。 发现status为2560这似乎不像退出码。他是通过下面这个图片的方式得来的int整形32位只用低16位其中高8位代表退出码低8位代表终止信号 正常终止看高八位即可因为未收到信号因此低8位为0。 被信号所杀看低八位其中第7位不看他代表core dump标志暂时不考虑只看0-6位。 那么2560的二进制为 0000 1010 0000 0000 如果右移8位也就是只看高8位即0000 1010即为10我们退出码也就是10。 公式为 *status exit_code8| exit_signal status不能直接使用如下经过右移操作和与操作就可以得到准确的退出码和信号了。 执行一下没有问题 小总结一下 当一个进程异常了收到信号 那么退出码就无意义了通过信号码是否非零来判断是否收到信号手动杀死子进程也能得到相应信号 虽然我们会通过位运算来得到退出码与信号但这样也比较麻烦linux系统提供了如下两个接口帮我们处理status。 WIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出 WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码 如下查看是否正常退出退出码为多少。 结果也符合预期 2.2第三个参数 0即阻塞等待 WNOHANG:若pid指定的子进程没有结束则waitpid()函数返回0不予以等待。若正常结束则返回该子进程的ID。非阻塞式等待 讲个小故事 张三约翠花去电竞酒店打麻将 他已经到翠花楼下了现在在等待翠花先来一起出发。 此时张三有两个方法一个是打电话询问翠花在干嘛什么时候下楼打完翠花说等一下她化个妆于是张三就在楼下开一把金铲铲之战过了半个小时又打翠花说在穿鞋了等一小下于是张三挂断电话去刷抖音过一会再打电话翠花说已经下楼了刚刚准备出门又上了个厕所张三没有什么脾气谁叫我想约人家呢于是挂断电话又去看看淘宝要买点什么最后再打电话翠花此时终于到达了于是两个人开开心心的去打麻将了。 另一个方法也是打电话询问翠花在干嘛什么时候下楼翠花也说等一下还在化妆但是张三今天电话不挂就一直等翠花时刻知道翠花在干嘛直到翠花下楼一起去打麻将。 在这个故事中 张三父进程 翠花子进程 打电话调用系统接口 第一个方法等待的条件不满足wait/waitpid不阻塞而是立即返回可以做自己占据时间并不多的事情。这是非阻塞式调用即非阻塞轮询方案进行进程等待该方案往往要进行重复调用。返回值0等待成功子进程已退出返回值0等待成功子进程未退出返回值0等待失败 第二个方法翠花不结束电话不挂机即阻塞式调用。子进程不退出wait/waitpid不返回。 代码如下waitpid第三个参数为 WNOHANG 借此观看非阻塞轮询等待。
#includestdio.h
#includeunistd.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hvoid Work(int number)
{printf(我是子进程, pid: %d, ppid: %d, number: %d\n,getpid(),getppid(),number);
}int main()
{pid_t id fork();if(id 0){//childint number 5;while(number){Work(number);number--;sleep(1);}exit(10);}//father int status 0;while(1){pid_t rid waitpid(id,status,WNOHANG);if(rid0){//等待成功子进程退出了printf(等待子进程成功子进程退出码: %d,退出信号: %d\n,WEXITSTATUS(status),status0x7F);break;}else if(rid 0){//等待成功但子进程没有退出printf(等待成功子进程还没推出父进程做其他事情去了\n);sleep(2);}else{printf(等待失败\n);break;}}return 0;
}
运行结果如下父进程间歇性询问子进程是否完成没完成就做自己的事情待会再来询问。 3. 等待多个进程
我们使用for循坏来fork多个进程同时给每个进程编号创建顺序从0-9。waitpid第一个参数为-1代表等待任意的子进程。
虽然我们也可以用数组的方式将子进程的pid放到数组里但是这样就只能一个一个进程的等待比如最先会等待退出码为0进程如果该进程不结束父进程会一直等待那么后续的进程永远不会被回收就会造成内存泄漏的问题。 1: myprocess.c ? ? ?? buffers
#includesys/types.h
#includesys/wait.hvoid Work(int number)
{int cnt 2;while(cnt){printf(我是子进程, pid: %d, ppid: %d, cnt: %d, number: %d\n,getpid(),getppid(),cnt--,number);sleep(1);}
}const int n 10;int main()
{int i 0;for(;in;i){pid_t id fork();if(id 0){//childWork(i);exit(i);}}//fork的子进程已近全部退出了下面是父进程执行的代码for(i0;in;i){int status;pid_t rid waitpid(-1,status,0); //-1:任意一个子进程退出 if(rid0) {printf(等待子进程 %d 成功, 退出码: %d\n,rid, WEXITSTATUS(status));}}return 0;
}
看看运行的情况发现调度运行与终止都是没有规律的谁先谁后我们不确定我们只知道肯定是父进程先创建并最后退出。 三、为什么不用全局变量获取子进程的退出信息
刚刚我们提到 可以用数组获取子进程的pid然后传值进行等待虽然效果不一定很好但这也算是一个解决办法为什么不用全局变量获取子进程的退出信息而是采用写入的方式进行传参获取呢
这是因为进程之间具有独立性父进程无法直接获取子进程的退出信息比如status我们设置为0父子进程看到的status值就都为0此时我们获取到了子进程的退出码将子进程的退出码写入status变量此时会发生写时拷贝子进程看到的是我自己写的新值而父进程看到的还是0。父子进程代码共享但数据不一定相同。
而父进程通过fork返回的id是子进程的pid子进程返回的id为0已经写时拷贝过了因此可以获取。