C | 三种特殊进程:孤儿进程,僵尸进程,守护进程
一、孤儿进程
孤儿进程就是父进程优先于子进程结束。
子进程失去父进程之后,会认为1号进程(init进程:负责初始化硬件,回收资源)是自己的父进程。1号进程负责回收和管理子进程。如果很多子进程都认为1号进程是自己的父进程,那么1号进程的负担会很大。所以,在编写代码时尽量让父进程回收完子进程的资源之后再结束。(也有可能存在一个专门回收子进程的进程)。
孤儿进程是没有危害的。
//孤儿进程示例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,const char *argv[])
{
pid_t pid = fork();
if (-1 == pid) {
perror("fork");
return -1;
}
if (0 == pid) {
//子进程未结束,父进程已经结束,子进程变成孤儿进程
int i = 10;
while(i--);
} else if (pid > 0) {
//父进程
exit(0);
}
return 0;
}
二、僵尸进程
僵尸进程就是子进程优先于父进程结束,但父进程没有回收子进程的资源。换句话说,子进程的任务已经完成了,但资源得不到回收,子进程就变成了僵尸进程。
僵尸进程是有危害的。
//僵尸进程示例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,const char *argv[])
{
pid_t pid = fork();
if (-1 == pid) {
perror("fork");
return -1;
}
if (0 == pid) {
//子进程结束,父进程未结束,资源没有被回收,变成僵尸进程
exit(0);
} else if (pid > 0) {
//父进程
while(1) {
sleep(1);
}
}
return 0;
}
回收僵尸进程:
方式1:阻塞回收任意一个子进程资源 wait
注意:如果父进程调用wait来回收子进程的资源,会阻塞等待回收,降低父进程的工作效率。
//使用 wait 函数回收僵尸进程示例:
if (0 == pid) {
//子进程结束
exit(0);
} else if (pid > 0) {
//父进程
sleep(6); //此时子进程是僵尸进程
wait(NULL); //子进程资源被回收
}
方式2:非阻塞回收任意一个子进程资源 waitpid
注意:如果父进程调用waitpid来回收子进程资源,需要频繁调用函数去查看子进程是否结束。
//使用 waitpid 函数回收僵尸进程示例:
if (0 == pid) {
//子进程结束
exit(0);
} else if (pid > 0) {
//父进程
while(1) {
sleep(1); //此时子进程是僵尸进程
waitpid(-1,NULL,WNOHANG); //子进程资源被回收
}
}
方式3:信号 signal
使用信号回收僵尸进程优于 wait 函数和 waitpid 函数。
//使用 信号 回收僵尸进程示例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include<stdlib.h>
#include <signal.h>
void myfun(int signum)
{
pid_t pid = wait(NULL);
}
int main(int argc, const char *argv[])
{
//注册信号
if (SIG_ERR == signal(SIGCHLD,myfun)) {
perror("signal");
return -1;
}
pid_t pid = fork();
if (-1 == pid) {
perror("fork");
return -1;
}
if (0 == pid) {
sleep(6);
exit(0);//子进程结束,父进程没有结束,产生僵尸进程,发送一个SIGCHLD的信号
} else if (pid > 0) {
while(1) {
sleep(1);
}
}
return 0;
}
三、守护进程
守护进程是一种特殊的进程机制,是一个后台进程。init进程就是一个守护进程。
后台进程:只允许向终端写入数据,不允许向终端读取数据。一旦对终端输入进行获取,后台进程会立即终止。
守护进程要脱离终端的管理,脱离会话的管理。
守护进程一般用于:服务器、http、tftp、ftp。
创建守护进程的步骤:
1.创建一个孤儿进程。(fork)
2.创建一个新的会话,孤儿进程变成会话首进程。(setsid)
3.修改默认工作目录文件。 (chdir)
4.给予最高文件权限。 (umask)
5.关闭不需要的文件描述符。 (close)
进程在启动初期会自动创建三个文件描述符(0、1、2)。
0:标准输入: stdin
1:标准输出: stdout
2:标准错误输出:stderr
//创建守护进程示例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
//1.创建一个孤儿进程
pid_t pid = fork();
if (-1 == pid) {
perror("fork");
return -1;
}
if (0 == pid) {
//2.创建一个会话,并设置为会话首进程
if (-1 ==setsid()) {
perror("setsid");
return -1;
}
//3.修改工作目录
if (-1 == chdir("/")) {
perror("chdir");
return -1;
}
//4.修改文件掩码,给予文件最高权限
umask(0);
//5.关闭文件描述符
close(0);
close(1);
close(2);
//执行守护事件
FILE * fp = fopen("./1.txt",'a');
if (NULL == fp) {
perror("fopen");
return -1;
}
fprintf(fp,"Hello world\n");
}
return 0;
}