1- fork and wait
例子1:
父进程
----》 子进程1
----》 子进程2
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid_1, pid_2;
if(0 == (pid_1 = fork())) {
printf("I'm child1. my pid = %d, pid_father = %d\n", getpid(), getppid());
} else {
if(0 == (pid_2 = fork())) {
printf("I'm child2. my pid = %d, pid_father = %d\n", getpid(), getppid());
} else {
printf("I'm father. my pid = %d, first wait = %d, second wait = %d, pid_1 = %d, pid_2 = %d\n", getpid(), wait(NULL), wait(NULL), pid_1, pid_2);
}
}
return 0;
}
可以看到,两个子进程具有相同的父进程,而父进程的两次wait()分别得到了两个子进程的pid。
fork()在子进程中返回0,而在父进程中返回子进程的pid。pid_1和pid_2的值说明了这点
执行结果:
I'm child1. my pid = 17507, pid_father = 17506
I'm child2. my pid = 17508, pid_father = 17506
I'm father. my pid = 17506, first wait = 17508, second wait = 17507, pid_1 = 17507, pid_2 = 17508
例子2: 共享内存, 子子进程11 与父进程通信
父进程
----》 子进程1----》 子子进程11
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <stdlib.h>
int main(void)
{
int shmid = 0;
int *shmbuf = NULL;
if((shmid = shmget(777, sizeof(int), 0666 | IPC_CREAT)) < 0) {
perror("shmget");
exit(-1);
}
if((shmbuf = shmat(shmid, NULL, 0)) < 0) {
perror("shmat");
exit(-1);
}
if(0 == fork()) {
if(0 == fork()) {
printf("I'm the child of child1. my pid = %d, pid_father = %d\n", getpid(), getppid());
*shmbuf = getpid();
} else {
printf("I'm child1. my pid = %d, pid_father = %d, pid_my_child = %d\n", getpid(), getppid(), wait(NULL));
}
} else {
printf("I'm father. my pid = %d, pid_my_child = %d, ", getpid(), wait(NULL));
printf("pid_child_of_child1 = %d\n", *shmbuf);
if(shmdt(shmbuf) < 0) {
perror("shmdt");
exit(-1);
}
if(shmctl(shmid, IPC_RMID, NULL) < 0) {
perror("shmctl");
exit(-1);
}
}
return 0;
}
结果:
I'm the child of child1. my pid = 17521, pid_father = 17520
I'm child1. my pid = 17520, pid_father = 17519, pid_my_child = 17521
I'm father. my pid = 17519, pid_my_child = 17520, pid_child_of_child1 = 17521
例子3
#include <sys/types.h>
#include <wait.h>
int wait(int *status)
函数功能是:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
1,WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。
2, WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
int count = 0;
pid_t pid = fork();
int status = -1;
if(pid<0)
{
printf("fork error for %m\n",errno );
}else if(pid>0)
{
printf("this is parent ,pid = %d\n",getpid() );
wait(&status);//父进程执行到此,马上阻塞自己,直到有子进程结束。当发现有子进程结束时,就会回收它的资源。
}else
{
printf("this is child , pid = %d , ppid = %d\n",getpid(),getppid() );
int i;
for (i = 0; i < 10; i++) {
count++;
sleep(1);
printf("count = %d\n", count) ;
}
exit(5);
}
printf("child exit status is %d\n", WEXITSTATUS(status));//status是按位存储的状态信息,需要调用相应的宏来还原一下
printf("end of program from pid = %d\n",getpid() );
}
运行结果:
this is parent ,pid = 17576
this is child , pid = 17577 , ppid = 17576
count = 1
count = 2
count = 3
count = 4
count = 5
count = 6
count = 7
count = 8
count = 9
count = 10
child exit status is 5
end of program from pid = 17576
2- 孤儿与僵尸
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
2.1问题及危害
僵尸进程:
如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
孤儿进程:
孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上, init循环回收, 因此孤儿进程并不会有什么危害。
任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
2.2-僵尸进程解决办法
子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
static void sig_child(int signo);
int main()
{
pid_t pid;
//创建捕捉子进程退出信号
signal(SIGCHLD,sig_child);
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
else if (pid == 0)
{
printf("I am child process,pid id %d.I am exiting.\n",getpid());
exit(0);
}
printf("I am father process.I will sleep two seconds\n");
//等待子进程先退出
sleep(2);
//输出进程信息
system("ps -o pid,ppid,state,tty,command");
printf("father process is exiting.\n");
return 0;
}
static void sig_child(int signo)
{
pid_t pid;
int stat;
//处理僵尸进程
while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
printf("child %d terminated.\n", pid);
}
REF:https://blog.csdn.net/Eunice_fan1207/article/details/81387417