孤儿进程、僵尸进程和守护进程
孤儿进程
定义
当fork出来的子进程的父进程结束,但是子进程并未结束时,子进程会成为孤儿进程;孤儿进程会被init进程接管,init进程负责孤儿进程的释放。在Ubuntu18.04系统中,孤儿进程被“/lib/systemd/systemd --user”进程领养。
代码例子
#include <iostream>
#include <unistd.h>
int main(void)
{
pid_t pid = fork();
if (pid == 0) {
while (true) {
std::cout << "I'm child process, parent process id is " << getppid() << std::endl;
sleep(1);
}
} else if(pid > 0) {
sleep(9);
std::cout << "I'm parent process,id is " << getpid() << std::endl;
}
return 0;
}
僵尸进程
定义
进程终止,父进程尚未回收,子进程资源残留与内核,变成僵尸进程。僵尸进程无法用kill命令杀掉。只能杀掉其父进程,之后僵尸进程会被init进程领养。
僵尸进程的避免
- 父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
- 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收。
- 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。
- 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。
代码例子
#include <iostream>
#include <unistd.h>
int main(void)
{
pid_t pid = fork();
if (pid == 0) {
std::cout << "child process start, parent process id is " << getppid() << std::endl;
sleep(9);
std::cout << "child process quit, parent process id is " << getppid() << std::endl;
} else if(pid > 0) {
while (true) {
sleep(1);
std::cout << "I'm parent process,id is " << getpid() << std::endl;
}
}
return 0;
}
守护进程
定义
Daemon独立于控制终端,周期性的执行某种任务,或等待处理某些发生的事件,一般采用以d结尾的名字。
代码例子
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(void)
{
pid_t pid = fork();
if (pid == 0) {
exit(0);
}
//创建新会话
pid = setsid();
if (pid == -1) {
std::cout << "setsid error" << std::endl;
return -1;
}
//改变工作目录,一般改为根目录
int ret = chdir("/home/yzz/");
if (ret == -1) {
std::cout << "chdir error" << std::endl;
return -1;
}
//设置umask
umask(0022);
//关闭文件描述符0
close(STDIN_FILENO);
int fd = open("dev/null", O_RDWR);
if (fd == -1) {
std::cout << "open error" << std::endl;
return -1;
}
//重定向STDOUT,STDERR
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
//模拟Daemon业务
while (true) {}
return 0;
}