做服装找工作网站,淄博网站制作定制视觉,wordpress标签logo,采集图片wordpress插件前言#xff1a;接着前面进程终止#xff0c;话不多说我们进入Linux进程等待的学习#xff0c;如果你还不了解进程终止建议先了解#xff1a;
Linux进程终止 本篇主要内容#xff1a; 什么是进程等待 为什么要进行进程等待 如何进程等待 进程等待 1. 进程等待的概念2. 进…前言接着前面进程终止话不多说我们进入Linux进程等待的学习如果你还不了解进程终止建议先了解
Linux进程终止 本篇主要内容 什么是进程等待 为什么要进行进程等待 如何进程等待 进程等待 1. 进程等待的概念2. 进程等待必要性3. 进程等待的方法3.1 wait方法3.2 waitpid方法 4. 获取子进程status获取子进程退出信息 5. waitpid的第三个参数options6. 总结拓展 1. 进程等待的概念
首先在开始之前我们提个问题到底什么是进程等待 进程等待的概念 我们通常说的进程等待其实是通过wait/waitpid的方式让父进程(一般)对子进程进行资源回收的等待过程父进程必须等待这个子进程结束后,处理它的代码和数据 2. 进程等待必要性
在了解完进程等待的概念后新的问题出现了我们为什么要进行进程等待进程等待的必要性是什么 进程等待必要性 若子进程退出,而父进程对它不管不顾,就可能造成‘僵尸进程’的问题进而造成内存泄漏。进程一旦变成僵尸状态那就刀枪不入“杀人不眨眼”的kill -9 也无能为力谁也没有办法杀死一个已经死去的进程。父进程创建子进程的目的是为了让子进程协助自己完成任务的而父进程需要知道子进程将任务完成得如何。这就需要通过进程等待的方式获取子进程的退出信息。 3. 进程等待的方法
3.1 wait方法
我们可以通过系统调用来等待进程wait函数
wait等待任意一个子进程的退出如果等待成功他将返回子进程的pid失败则返回-1 我们就用一段代码来看看wait: #includestdio.h#includeunistd.h#includestdlib.h#includesys/types.h#includesys/wait.hvoid Worker(){int cnt 5;while(cnt){printf(i am child, pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid(), cnt--);sleep(1);}}int main(){ pid_t id fork();if(id 0){// childWorker();exit(0);}else{sleep(10);// fatherpid_t rid wait(NULL);if(rid id){printf(wait success, pid: %d\n, getpid());}}return 0;} 进程等待wait函数 我们通过视频发现进程等待是可以回收子进程僵尸状态的 然后我们将父进程sleep()取消看看在子进程退出之前父进程在干什么 #includestdio.h#includeunistd.h#includestdlib.h#includesys/types.h#includesys/wait.hvoid Worker(){int cnt 5;while(cnt){printf(i am child, pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid(), cnt--);sleep(1);}}int main(){ pid_t id fork();if(id 0){// childWorker();exit(0);}else{// fatherprintf(wait before:\n);pid_t rid wait(NULL);printf(wait after:\n);if(rid id){printf(wait success, pid: %d\n, getpid());}sleep(10);}return 0;}观察父进程等待过程 通过这个视频我们又能发现两个进程一起运行但是在子进程没有退出之前父进程一直在wait上等待并且并没有出现子进程僵尸状态而是直接回收了。 结论如果子进程根本就没有退出父进程必须在wait上进行阻塞等待。直到子进程僵尸wait自动回收返回。 3.2 waitpid方法
waitpid和wait都是等待进程。waitpid可以指定等待一个进程且有三个参数 4. 获取子进程status
父进程想要知道子进程的退出信息也就是退出码和退出信号就要用到输出型参数status wait和waitpid都有一个status参数该参数是一个输出型参数由操作系统填充。如果传递NULL表示不关心子进程的退出状态信息。否则操作系统会根据该参数将子进程的退出信息反馈给父进程。 int main(){ pid_t id fork();if(id 0){// childWorker();exit(10); // 设置成10方便观察现象}else{// fatherprintf(wait before:\n);int status 0;pid_t rid waitpid(id, status, 0);printf(wait after:\n);if(rid id){printf(wait success, pid: %d, status: %d\n, getpid(), status);}}return 0;}我明明将exit的退出结果设置成10但是为什么他的status会是2560呢 其实status不能简单的当作整形来看待可以当作位图来看待具体细节如下图只研究status低16比特位 因此我们在研究status时不能整体使用status!!! 获取子进程退出信息
因为我们知道了status不能整体使用因此我们要进行位操作 exit sig: status0x7f //获取信号exit code: (status8)0xff //获取退出结果 当我们的程序异常了exit code 将无任何意义exit sig : 0则代表没有收到信号手动杀掉子进程也会获取到信号 但是如果我们每次提取退出信息都要使用繁琐的位运算这很不方便因此系统给我们做了一个简单的封装 status WIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码 int main()
{pid_t id fork();if(id 0){// childWorker();exit(1);}else{// sleep(10);// fatherprintf(wait before:\n);//pid_t rid wait(NULL); int status 0; pid_t rid waitpid(id, status, 0); printf(wait after:\n);if(rid id) { // 不能对status整体使用//printf(wait success, pid: %d, rpid: %d, exit sig: %d, exit code: %d\n,getpid(), rid, status0x7f, (status8)0xff);if(WIFEXITED(status)){printf(child process normal quit, exit code: %d\n, WEXITSTATUS(status));} else{ printf(child process quit exept!!!\n);}}} return 0;
} 当我们要获取多个进程的调度信息时我们给每个进程都要一个编号我们来观察一下进程是怎样调度的
void Worker(int number)
{int cnt 5;while(cnt){printf(i am child, pid: %d, ppid: %d, cnt: %d, number: %d\n, getpid(), getppid(), cnt--, number);sleep(1);}
}
const int n 10;int main()
{for(int i 0; i n; i){pid_t id fork();if(id 0) { Worker(i); exit(i);} } // 等待多个子进程 for(int i 0; i n; i) { int status 0; pid_t rid waitpid(-1, status, 0); //pid 0, -1:等待任意一个进程if(rid 0){printf(wait child %d success, exit code: %d\n, rid, WEXITSTATUS(status));}}return 0;} 观察进程调度顺序 我们发现明明是按顺序创建的进程但是在调度时却没有顺序可言终止的时候也没有顺序因为进程在调度完全由调度器说的算所以进程调度的先后我们并不确定这点在前面我们也提到过。 5. waitpid的第三个参数options
在使用waitpid的第三个参数时前面我们提到设为0则是默认阻塞等待状态必须等待子进程的退出当时如果我们要做自己的事我们就不能使用0而是使用WNOHANG options WNOHANG若pid指定的子进程没有结束则waitpid()函数返回0不予以等待。若正常结束则返回该子进程的ID。0默认的阻塞等待状态 **父进程在非阻塞等待时因为子进程没有结束就跑去做自己的事情了但是又要继续等待所以往往伴随着重复调轮询也就是基于非阻塞轮询的等待方案 ** 我们来直接验证一下非阻塞等待
void Worker(int cnt)
{printf(i am child, pid: %d, cnt: %d\n, getpid(), cnt);sleep(1);
}
int main()
{pid_t id fork();if(id 0){// 子进程int cnt 5;while(cnt){Worker(cnt);sleep(1);cnt--;}exit(1);}// 父进程while(1){int status 0;pid_t rid waitpid(id, status, WNOHANG);if(rid 0) {// 等待成功printf(wait success, child quit, exit code: %d, exit sig: %d\n, (status8)0xff, status0x7f);break;}else if(rid 0){// 等待成功但子进程并未退出printf(wait again, child alive, do other thing\n); sleep(1);}else{// 等待失败printf(wait failed\n);break;} } return 0;} 我们可以看到非阻塞等待可以让我们的父进程在等待时做自己的事 6. 总结拓展
拓展一父进程如何得知子进程的退出信息 父进程调用wait()/waitpid()来获取子进程的退出信息调用的接口就传入了一个status参数而父进程中存在着一个statusp的指针。
而子进程在退出时操作系统就会将退出信号和退出码写到子进程的PCD中
int exit_code;
int exit_signal而退出信号和退出码将会写到这两个变量中 当我们调用系统调用时只需要将这两个变量组合写入到变量里
*statusp (exit code8)| exit siganl这样父进程就获取到了子进程的退出信息 拓展二我们为什么不用全局变量获取子进程的退出信息而用系统调用? 这个就是因为进程具有独立性父进程无法直接获得子进程的退出信息 总结
进程等待确实非常有用它既可以回收僵尸进程避免造成内存泄漏也能让父进程能够获取到子进程的退出信息进程等待我们就先了解这么多进程控制马上就到了我们的最后一步——进程替换让我们来期待下一篇
谢谢大家支持本篇到这里就结束了