子进程是通过父进程创建的,子进程也可以继续创建子进程。父进程无法预测子进程到底什么时候结束,当一个子进程结束之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。
僵尸进程 :
子进程结束了,但是父进程没有调用wait()或者waitpid()取得子进程的终止状态,这个子进程将变成一个僵尸进程。
利用命令ps,可以看到有标记为Z的进程就是僵尸进程。
僵尸进程危害:
系统中的进程数量是有限的,虽然僵尸进程占用的资源和内存都比较少,但是它却占领着数字,可能会导致系统无法再创建新的进程,因此及时清除僵尸进程很重要!
避免僵尸进程:
1、通过信号机制,子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。
- 两次fork()
原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程。
处理僵尸进程:
把父进程杀掉。父进程死后,僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵尸进程。它产生的所有僵尸进程也跟着消失。
构造僵尸进程,子进程退出,父进程未退出,未wait
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main ()
{
pid_t fpid;
fpid = fork();//返回的是子进程id,在子进程中,这个值为0
if (fpid < 0)
printf("error in fork!");
else if (fpid == 0) {
printf("i am the child process, my process id is %d parentid %d \n",getpid(), getppid());
printf("the child process exit\n");
exit(0);
}
else {
printf("i am the parent process, my process id is %d my child process %d\n",getpid(), fpid);
sleep(60);
}
return 0;
}
使用信号解决
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include <sys/types.h>
#include <sys/wait.h>
void sig_child(int signo)
{
pid_t pid;
int stat;
while ((pid = waitpid(-1, &stat, WNOHANG))>0)
printf("child %d exit\n", pid);
}
int main ()
{
pid_t fpid;
signal(SIGCHLD,sig_child);
fpid = fork();//返回的是子进程id,在子进程中,这个值为0
if (fpid < 0)
printf("error in fork!");
else if (fpid == 0) {
printf("i am the child process, my process id is %d parentid %d \n",getpid(), getppid());
printf("the child process exit\n");
exit(0);
}
else {
printf("i am the parent process, my process id is %d my child process %d\n",getpid(), fpid);
sleep(60);
}
return 0;
}
两次fork解决,第二个子进程为孤儿进程
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main ()
{
pid_t fpid;
fpid = fork();//返回的是子进程id,在子进程中,这个值为0
if (fpid < 0)
printf("error in fork!\n");
else if (fpid == 0) {
printf("i am the first child process, i will fork\n");
fpid = fork();
if(0 > fpid){
printf("error in fork!\n");
}else if (fpid == 0){
printf("i am the second child process\n");
sleep(30);
printf("i am the second child process, my process id is %d parentid %d \n",getpid(), getppid());
}else{
printf("i am the first child process, i will exit\n");
sleep(1);
exit(0);
}
}
else {
printf("i am the parent process, my process id is %d my child process %d\n",getpid(), fpid);
if (waitpid(fpid, NULL, 0) != fpid){
perror("waitepid error:");
exit(1);
}
exit(0);
}
return 0;
}
孤儿进程:父进程先与子进程结束,子进程就由Init(进程号为1)来接管他,成为他的父进程,此时称这个进程为孤儿进程,其状态收集工作由init进程负责。
孤儿进程并不会有什么危害。
构造孤儿进程,父进程退出,子进程未退出
#include<stdio.h>
#include<unistd.h>
int main ()
{
pid_t fpid;
fpid = fork();//返回的是子进程id,在子进程中,这个值为0
if (fpid < 0)
printf("error in fork!");
else if (fpid == 0) {
printf("i am the child process, my process id is %d parentid %d \n",getpid(), getppid());
sleep(2);
//这里父进程id为1 ps -fu$USER查看也是如此
printf("i am the child process, my process id is %d parentid %d \n",getpid(), getppid());
sleep(20);
}
else {
printf("i am the parent process, my process id is %d my child process %d\n",getpid(), fpid);
sleep(1);
}
return 0;
}
守护进程(Daemon)是在一类脱离终端在后台执行的程序, 通常以 d 结尾, 随系统启动, 其父进程 (ppid) 通常是 init 进程。