进程回收
在Unix/Linux下进行多进程编程很常见,一般来讲,进程终止自身有两种方式:
- 传递参数并调用exit函数
- 在main函数中执行return语句返回一个值
向exit传递的参数以及从main返回的值最终都会传递给操作系统,操作系统此时并不会销毁该进程,直到把这些值传递给产生该进程的父进程,处于这种状态下的进程称为僵尸进程。
如何向父进程传递值呢?操作系统不会主动把这些值传给父进程,在有父进程通过wait/waitpid调用主动来取时,操作系统才会传递该值。换言之,如果父进程未主动要求获得子进程的结束状态,操作系统将一直保留,并让子进程长时间处于僵尸状态。
如果在进程退出时,其父进程已经结束,那么init进程将自动接手该进程为子进程。
小结
- 子进程在退出后将处于僵尸状态直到父进程调用wait/waitpid,除非父进程忽略SIGCHLD信号。
- 如果父进程未设置忽略SIGCHLD信号,且没调wait/waitpid就退出,那么它的子进程(活动的和僵死的)将由init进程接管。
- 如果忽略SIGCHLD信号,子进程退出时的状态信息将被丢弃,即自动回收,因此不会产生僵尸进程,此时父进程不能再调wait/waitpid去获取子进程信息。
等待子进程接口
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
说明:
- 调用wait将使当前进程阻塞,直到有当前进程的子进程退出为止,wait(&status)相当于waitpid(-1, &status, 0)。
- 默认情况下,调用waitpid将使当前进程阻塞,直到进程ID为pid的子进程退出为止,可通过options参数修改等待属性。
- 子进程退出时的返回值包含在statloc所指的内存空间,但还包含其他信息,需用宏分离。
- WIFEXITED(status): 子进程是否正常终止。
- WEXITSTATUS(status): 子进程正常退出时的返回值,范围是0~255。
信号回收子进程
如果父进程较忙,不能一直阻塞或者轮询去等子进程退出,可以通过信号做到事件驱动。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
void WaitChild(int sig)
{
int status;
pid_t pid = waitpid(-1, &status, WNOHANG);
if (WIFEXITED(status))
printf("process %d exited with %d\n", pid, WEXITSTATUS(status));
}
int main()
{
pid_t pid;
struct sigaction act;
act.sa_handler = WaitChild;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD, &act, 0);
pid = fork();
if (pid == 0) {
puts("this is child process 1");
sleep(3);
return 12;
} else {
printf("child1 id: %d\n", pid);
pid = fork();
if (pid == 0) {
puts("thils is child process 2");
sleep(5);
exit(5);
} else {
printf("child2 id: %d\n", pid);
for(;;sleep(3));
}
}
return 0;
}