利用fork()函数来创建子进程,若不主动获取子进程的结束状态值,就会出现僵尸进程。
fork函数的定义如下:
fork函数是一个系统调用函数,用于创建一个新的进程,新进程是原进程的一个副本。它在父进程中返回新创建的子进程的进程ID,在子进程中返回0。这样,通过fork函数的调用,一个进程可以变成两个几乎完全相同的进程,每个进程都可以继续执行fork调用之后的代码。
注意:
fork函数创建的子进程和父进程拥有相同的父进程,即它们的父进程是同一个进程。子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程的上下文、代码段、进程堆栈、内存信息、打开的文件描述符、符号控制设定、进程优先级、进程组号、当前工作目录等。但是子进程和父进程是两个独立的进程,它们有不同的进程ID,可以独立运行,互不干扰。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
void read_childproc(int sig)
{
int status;
pid_t id=waitpid(-1, &status, WNOHANG);
if(WIFEXITED(status))
{
printf("Removed proc id: %d \n", id);
printf("Child send: %d \n", WEXITSTATUS(status));
}
}
int main(int argc, char *argv[])
{
pid_t pid;
struct sigaction act;
act.sa_handler=read_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
sigaction(SIGCHLD, &act, 0);
pid=fork();
if(pid==0)
{
puts("Hi! I'm child process");
sleep(10);
return 12;
}
else
{
printf("Child proc id: %d \n", pid);
pid=fork();
if(pid==0)
{
puts("Hi! I'm child process");
sleep(10);
exit(24);
}
else
{
int i;
printf("Child proc id: %d \n", pid);
for(i=0; i<5; i++)
{
puts("wait...");
sleep(5);
}
}
}
return 0;
}
/*
root@my_linux:/home/swyoon/tcpip# gcc remove_zombie.c -o zombie
root@my_linux:/home/swyoon/tcpip# ./zombie
Hi! I'm child process
Child proc id: 9529
Hi! I'm child process
Child proc id: 9530
wait...
wait...
Removed proc id: 9530
Child send: 24
wait...
Removed proc id: 9529
Child send: 12
wait...
wait...
*/
由上述代码可以看出,子进程为变成僵尸进程。
Sleep()函数的中断时机:
当进程被信号唤醒时,`sleep`函数不会立即执行完毕。`sleep`函数会在进程被唤醒后继续执行剩余的时间,直到睡眠时间结束或者被其他信号打断。
`sleep`函数会将进程挂起,直到指定的时间到达或者被信号中断。当进程被信号唤醒时,它会检查是否有未处理的信号,并根据信号的处理方式来处理。如果信号的处理方式是默认的,进程会终止或者执行默认操作。如果信号的处理方式是忽略或者捕获,进程会继续执行剩余的时间。
需要注意的是,`sleep`函数是一个可中断的睡眠函数。如果进程在睡眠过程中收到了某些信号(如`SIGALRM`),则会被信号中断并提前结束睡眠。在这种情况下,`sleep`函数会返回剩余的睡眠时间。
注意:输出的顺序不唯一,这跟操作系统有关。
子进程从调用fork()后开始执行,子进程和父进程同时进行。