Linux系统编程:进程回收wait / waitpid 详解
本文摘要:探讨了Linux进程管理的核心机制,包括进程创建、执行和退出。重点分析了fork()创建子进程的两种执行方式:继承父进程或通过exec系列函数加载新程序。详细讲解了进程退出的三种方式(正常退出、异常退出和信号终止),并深入解析了wait()和waitpid()函数对僵尸进程的回收机制,包括阻塞/非阻塞模式、状态码获取方法(WIFEXITED/WEXITSTATUS等宏)。通过代码示例展示
目录
1、进程的执行
进程分为两种情况:
(1)做与父进程类似的事情
(2)做与父进程完全不同的事情,也就是运行新程序,即fork + exec。
那么exec又分为以下三种(具体如下链接)https://blog.csdn.net/2301_77695365/article/details/158569303?fromshare=blogdetail&sharetype=blogdetail&sharerId=158569303&sharerefer=PC&sharesource=2301_77695365&sharefrom=from_link
exec l
exec v
exec l p
exec v p
exec l e
exec v p e
exec函数都是用新进程的各个段替代了当前进程的各个段。
我们又学习了myshell函数:shell程序,是一个命令行解释器,负责解释运行输入的命令。
2、进程退出
(1)正常退出
- 从main函数返回
- exit:会调用退出清理函数(atexit),处理IO缓存
- _exit:不处理IO缓存
(2)异常退出
- abort
- signal:发信号
3、僵尸态的回收
wait函数
注意
- wait函数是一个阻塞操作,它在 等 当前进程的任意子进程结束。
- wait一次 只能 处理一个子进程
- wait(int *wstatus);
- exit(int status);
子进程的status这个参数在子进程函数结束后会传给父进程,而父进程可以通过wait函数或者waitpdi的wstatus来获得。
注意
要获得退出状态值,需要使用宏才能获取
- WIFEXITED(wstatus):判断子进程是否是正常结束
- WEXITSTATUS(wstatus):获取子进程退出状态
- WIFSIGNALED(wstatus):判断子进程是否是信号结束的
- WTERMSIG:获取结束的信号编号
使用宏的示例
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> int main(int argc, const char *argv[]) { printf("----test---fork---\n"); pid_t pid = fork(); if(pid<0) { perror("fork fail"); return -1; } //父子进程都做 printf("main pid:%d\n",pid); //父进程单独做的 if(pid>0) { while(1) { printf("father pid:%d\n",getpid()); sleep(1); int wstatus; if(wait(&wstatus)<0)//回收资源 { perror("wait fail"); return -1; } //判断子进程是否是正常结束,因为这个参数在子进程结束后是传给父进程 //而父进程可以通过wait函数或者waitpid的wstatus来获得 if(WIFEXITED(wstatus)) { //WEXITSTATUS获得取值码 printf("child exit status:%d\n",WEXITSTATUS(wstatus)); } //判断子进程是否是信号结束 if(WIFSIGNALED(wstatus)) { //WTERMSIG获取结束的信号编号 printf("child exit signal:%d\n",WTERMSIG(wstatus)); } } } //子进程单独做的 else if(pid==0) { int i=0; while(i<5) { printf("child pid:%d\n",getpid()); sleep(1); i++; } //子进程立刻停止循环,终止运行; //向父进程传递退出码99 //父进程可以通过 waitpid() + WEXITSTATUS() 获取这个99 //判断子进程的退出状态。 exit(99); } return 0; }
waitpid函数
-
pid_t waitpid(pid_t pid, int *wstatus, int options);
功能
- 等待子进程结束
参数
- @pid

- @wstatus:和wait的参数含义一样,可以用来保存子进程退出状态值
- @options:选项为0表示waitpdi阻塞调用,选项为WNOHANG,表示非阻塞调用
返回值
- 阻塞调用成功返回 对应子进程的pid,
- 非阻塞被调用,如果子进程状态未改变,返回0
- 失败返回-1
wait & waitpid
- waitpid实现了一种非阻塞调用,可以指定某个具体的pid
- wait(&status)等价于waitpid(-1,&status,0) 都是阻塞调用
阻塞调用

非阻塞调用

waitpid非阻塞调用指定具体pid
waitpid(1000,&status,0)阻塞等待pid为1000的子进程的状态改变
waitpid(1000,&status,WNOHANG)非阻塞等待pid为1000的子进程的状态改变
完整代码
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> int main(int argc, const char *argv[]) { printf("----test---fork---\n"); pid_t pid = fork(); if(pid<0) { perror("fork fail"); return -1; } //父子进程都做 printf("main pid:%d\n",pid); //父进程单独做的 if(pid>0) { while(1) { printf("father pid:%d\n",getpid()); sleep(1); int wstatus; #if 0 // 阻塞调用 //wait(&wstatus)等价于waitpid(-1,&(wstatus),0),这两个都是阻塞调用 if(waitpid(-1,&wstatus,0)<0)//回收资源 { perror("waitpid fail"); return -1; } //判断子进程是否是正常结束,因为这个参数在子进程结束后是传给父进程 //而父进程可以通过wait函数或者waitpid的wstatus来获得 if(WIFEXITED(wstatus)) { //WEXITSTATUS获得取值码 printf("child exit status:%d\n",WEXITSTATUS(wstatus)); } //判断子进程是否是信号结束 if(WIFSIGNALED(wstatus)) { //WTERMSIG获取结束的信号编号 printf("child exit signal:%d\n",WTERMSIG(wstatus)); } #endif //非阻塞调用 pid_t ret_pid = waitpid(-1,&wstatus,WNOHANG);//回收资源 if(ret_pid<0) { perror("waitpid fail"); return -1; } printf("ret_pid:%d\n",ret_pid); if(ret_pid > 0) { if(WIFEXITED(wstatus)) { printf("child exit status:%d\n",WEXITSTATUS(wstatus)); } if(WIFSIGNALED(wstatus)) { printf("child exit signal:%d\n",WTERMSIG(wstatus)); } } } } //子进程单独做的 else if(pid==0) { int i=0; while(i<5) { printf("child pid:%d\n",getpid()); sleep(1); i++; } //子进程立刻停止循环,终止运行; //向父进程传递退出码99 //父进程可以通过 waitpid() + WEXITSTATUS() 获取这个99 //判断子进程的退出状态。 exit(99); } return 0; }
进程小结
1、进程的创建:fork
2、进程的执行
- 做与父进程类似的事情:网络
- 执行一个新程序:fork+exec
3、进程的退出
- exit
- _exit
4、退出状态
- 孤儿进程
- 僵尸进程

更多推荐



所有评论(0)