写在前面
本文主要介绍一些系统编程中控制进程运行的函数
在C语言中,通常使用 exit()
或 _exit()
来结束当前进程,而return
则用于结束当前函数。
exit
作用:退出当前进程
函数原型:void exit(int value);
头文件:
- #include <stdlib.h>
参数:
- value:返回给父进程的参数(低 8 位有效),至于这个参数是多少根据需要来填写。
_exit()
作用:结束调用此函数的进程。
函数原型:void _exit(int value);
头文件:
- #include <unistd.h>
exit()
和 _exit()
函数功能和用法是一样的,无非时所包含的头文件不一样,还有的区别就是:exit()属于标准库函数,_exit()属于系统调用函数。
从上图可知,通过exit()
结束的进程会刷新缓冲区,而通过_exit()
退出的则不会
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
printf("Hello world");//没有换行符,不会刷新缓冲区
exit(0); //会输出上述文字
//_exit(0); //不会输出上述文字
while(1); //不让子进程结束
return 0;
}
return
的话就不做过多解释了,C语言里面应该很好理解。
一个进程在终止时会关闭所有的文件描述符,释放在用户空间分配的内存,但它的 PCB 还保留着,内核在其中保存了一些信息:如果是正常终止则保存退出状态,如果是异常终止则保存导致进程终止的是哪个信号。这个进程的父进程可以调用wait
与waitpid
函数获取这些信息,然后彻底清除掉这个进程。
wait函数
作用:
- 阻塞等待子进程退出
- 回收子进程残留资源
- 获取子进程退出状态(退出原因)
函数原型:pid_t wait(int *status);
头文件:
- #include <sys/types.h>
- #include <sys/wait.h>
参数:
- status:传出参数,回收进程的状态。
如果参数 status 的值不是 NULL,wait() 就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的。
这个退出信息在一个 int 中包含了多个字段,直接使用这个值是没有意义的,我们需要用宏定义取出其中的每个字段。
获取子进程正常终止值:
WIFEXITED(status)
--> 为真 -->调用WEXITSTATUS(status)
--> 得到 子进程 退出值。
获取导致子进程异常终止信号:
WIFSIGNALED(status)
--> 为真 -->调用WTERMSIG(status)
--> 得到 导致子进程异常终止的信号编号。
只有第一个宏为真时,调用第二个宏才有意义
返回值:
- 成功:返回回收的子进程pid
- 失败:返回 -1, 设置errno
waitpid
从本质上讲,系统调用 waitpid()
和 wait()
的作用是完全相同的,但 waitpid()
多出了两个可由用户控制的参数 pid
和 options
,从而为我们编程提供了另一种更灵活的方式。
函数原型:pid_t waitpid(pid_t pid, int *status, int options);
参数:
- pid:指定回收某一个子进程pid,可选项如下:
- -1:代表任意回收一个子进程
-
0:待回收的子进程的pid
- 0:同组的子进程
- status:指定回收某一个子进程pid
- options:指定回收的方式
- WNOHANG: 非阻塞回收
- NULL:阻塞回收
返回值:
-
0 : 表成功回收的子进程 pid
- =0 : 函数调用时, 参3 指定了WNOHANG, 并且,没有子进程结束。
- -1: 失败。errno
案例
通过wait函数回收子进程
int main(){
pid_t pid;
pid = fork();
if(pid < 0){
perror("fork");
exit(0);
} else if(pid == 0) {
int i = 0;
for(i = 0;i < 5;i++){
printf("I am child pid is %d\n",getpid());
sleep(1);
}
exit(2);
} else {
int status;
pid_t pid1 = wait(&status); //阻塞回收
if(WIFEXITED(status)){
printf("son process return %d pid is %d\n", WEXITSTATUS(status), pid1);
}
printf("This is parent\n");
}
return 0;
}
从上图的执行结果可以得出,当使用wait
函数回收子进程时,一直阻塞等到子进程结束,在继续执行父进程,并且通过宏函数获取到了status
返回的进程退出状态。
通过waitpid
指定回收第三个创建的子进程
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
pid_t pid, tmppid;
int i = 0;
for(i = 0;i < 5;i++){
pid = fork();
if(pid == 0)
break;
if(i == 2)
tmppid = pid;
}
if(i == 5){
sleep(5);
int status;
pid_t pid1;
//waitpid(-1, &status, 0); // 和 wait() 没区别,0:阻塞
//waitpid(pid, &status, 0); // 指定等待进程号为 pid 的子进程, 0 阻塞
pid1 = waitpid(tmppid, &status, WNOHANG);//// WNOHANG:不阻塞
printf("This is parent waitpid is %d\n", pid1);
} else {
sleep(i);
printf("I am %dth child pid is %d\n", i+1, getpid());
}
return 0;
}
补充
wait、waitpid 一次调用,回收一个子进程,想要回收多个必须使用 while 循环
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
pid_t pid;
int i = 0;
for(i = 0;i < 5;i++){
pid = fork();
if(pid == 0)
break;
}
if(i == 5){
pid_t pid1;
while( pid1 = wait(NULL)){
if(pid1 == -1)
break;
printf("This is parent waitpid is %d\n", pid1);
}
} else {
printf("I am %dth child pid is %d\n", i+1, getpid());
}
return 0;
}