【Linux之进程间通信】
项目代码获取:https://gitee.com/chenshao777/linux-processes.git
(麻烦点个免费的Star哦,您的Star就是我的写作动力!)
05.僵尸进程
僵尸进程: 指的是进程终止后,资源没有得到回收,状态为 Z+
怎么创造一个僵尸进程出来呢,很简单,
通过 fork 函数创建一个子进程
父进程执行while(1)死循环,
子进程不循环,运行几条语句后直接退出,
例如:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>/*僵尸进程 & 子进程给父进程发信号(通过kill(getppid(), sig)) & 子进程调用exit(),发送SIGCHLD信号给父进程命令: ./z+process现象:父进程正常运行,收到一次子进程的SIGUSR1信号子进程不调用exit()函数结束,则变成僵尸进程Z+子进程调用exit()函数结束,则向父进程发送SIGCHLD信号,父进程信号处理函数调用wait()函数回收子进程资源
*/
void handler(int arg)
{printf("信号处理handler,arg = %d\n", arg); //arg是信号IDif(arg == SIGINT)printf("你按了 ctrl+c \n");if(arg == SIGUSR1)printf("父进程收到子进程信号\n");if(arg == SIGCHLD){printf("子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己\n");wait(NULL); //回收子进程资源}
}int main(int argc, char *argv[])
{signal(SIGINT, handler); //SIGINT(ctrl+c)信号处理pid_t pid;pid = fork();if(pid > 0){signal(SIGUSR1, handler); //接收子进程的信号// signal(SIGCHLD, handler); //接收子进程结束的信号printf("父进程\n");for(int i = 0;i < 10;i++){printf("父 %d\n",i);sleep(1);}}if(pid == 0){printf("子进程运行,接着睡眠2秒\n");sleep(2);printf("给父进程发信号\n");kill(getppid(), SIGUSR1);sleep(1);printf("子进程自己隔屁了,子进程状态变化,自动向父进程发送SIGCHLD信号\n");}return 0;
}
本代码结合了 signal 信号处理函数,signal 函数的介绍可以借鉴一下我上一篇博客的第三小节 【Linux之进程间通信】04.Linux进程间的信号通信
运行结果:
hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ gcc 04.z+process.c -o z+process
hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ ./z+process
父进程
父 0
子进程运行,接着睡眠2秒
父 1
给父进程发信号
父 2
信号处理handler,arg = 10
父进程收到子进程信号
父 3
子进程自己隔屁了,子进程状态变化,自动向父进程发送SIGCHLD信号
父 4
父 5
父 6
父 7
父 8
父 9
子进程执行完语句直接结束运行,我们运行一下 ps -axj 命令,查看一下该进程的状态
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND4070 30199 30199 4070 pts/1 30199 S+ 1000 0:00 ./z+process30199 30200 30199 4070 pts/1 30199 Z+ 1000 0:00 [z+process] <defunct>2282 30207 30207 2282 pts/0 30207 R+ 1000 0:00 ps -axj
第一行是父进程信息,第二行是子进程信息
父进程是S+睡眠状态(可能正好在执行sleep函数)
子进程是Z+僵尸进程状态
处理僵尸进程:
首先我们要知道两个知识点:
1.wait()函数的作用:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经退出的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;
如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
2.子进程的运行状态只要发生变化,都会自动向父进程发送一个SIGCHLD信号,所以父进程对SIGCHLD信号设置处理函数,在处理函数中使用wait()函数进行子进程的资源回收.
signal(SIGCHLD, handler); //接收子进程结束的信号
上面的代码虽然说子进程退出是向父进程发送了SIGCHLD信号,但是该信号默认被忽略
---------------------------------------------
所以处理僵尸进程的方法:
1.在主进程中设置SIGCHLD信号处理函数
2.在信号处理函数中执行 wait(NULL),对退出的子进程进行资源回收,则僵尸进程不存在了。
具体操作:
将上面代码中
// signal(SIGCHLD, handler); //接收子进程结束的信号
的注释取消,再次运行,查看运行结果和进程状态:
hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ gcc 04.z+process.c -o z+process
hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ ./z+process
父进程
父 0
子进程运行,接着睡眠2秒
父 1
给父进程发信号
信号处理handler,arg = 10
父进程收到子进程信号
父 2
父 3
子进程自己隔屁了,子进程状态变化,自动向父进程发送SIGCHLD信号
信号处理handler,arg = 17
子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己
父 4
父 5
父 6
父 7
父 8
父 9
运行结果多了一句 “子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己”
说明子进程退出后主进程接收到了 SIGCHLD 信号,执行了 handler 函数中的以下代码
if(arg == SIGCHLD){printf("子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己\n");wait(NULL); //回收子进程资源
}
由于运行了 wait(NULL); 进程状态中也没有了僵尸进程,只有S+状态的主进程
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND4070 30533 30533 4070 pts/1 30533 S+ 1000 0:00 ./z+process2282 30535 30535 2282 pts/0 30535 R+ 1000 0:00 ps -axj
扩展知识:
如果父进程提前结束,而子进程还在运行,则子进程退出时谁来给子进程回收资源呢?
答:如果子进程还在运行,父进程却提前退出,则PID为1的init进程会自动接管,成为该子进程的父进程,等子进程终止后回收资源 (Linux内核真牛)