守护进程(Daemon)是一类在后台运行的特殊进程,它们通常不与任何终端或用户直接交互,而是执行特定的系统任务或等待系统或网络事件的发生。守护进程是操作系统中不可或缺的一部分,它们负责执行各种后台任务,如系统日志记录、文件系统管理、网络服务等。
创建一个守护进程的步骤:
1、让程序在后台执行
fork出一个子进程,父进程退出
int main(){if(::fork()){return 0;}return 0;
}
2、创建一个新会话摆脱原来的登录会话、进程组和控制终端的影响
int main(){if(::fork()){return 0;}int sid=::setsid();return 0;
}
3、禁⽌进程重新打开控制终端
进程已经成为⼀个⽆终端的会话组⻓,但是它可以重新申请打开⼀个终端。为了避免这种情况发⽣,可以通过使进程不再是会话组⻓来实现。
目的:避免终端对该程序的影响
int main(){if(::fork()){return 0;}int sid=::setsid();if(::fork()){return 0;}return 0;
}
4、关闭不再需要的⽂件描述符
目的:避免资源浪费
int main(){if(::fork()){return 0;}int sid=::setsid();if(::fork()){return 0;}for(int fd=0;fd<syscall(_SC_OPEN_MAX);++fd){close(fd);}return 0;
}
5、将当前⽬录更改为根⽬录
int main(){if(::fork()){return 0;}int sid=::setsid();if(::fork()){return 0;}for(int fd=0;fd<syscall(_SC_OPEN_MAX);++fd){close(fd);}chdir("/");return 0;
}
6、屏蔽字清零
目的:避免⼦进程从⽗进程继承的⽂件创建屏蔽字可能会拒绝某些许可权的影响
int main(){if(::fork()){return 0;}int sid=::setsid();if(::fork()){return 0;}for(int fd=0;fd<syscall(_SC_OPEN_MAX);++fd){close(fd);}chdir("/");umask(0);return 0;
}
7、处理SIGCHLD信号
Linux 系统中,当一个子进程结束时,它会发送 SIGCHLD
信号给父进程。如果父进程没有处理这个信号,子进程不会立即从系统中消失,而是变成一个僵尸进程(zombie process)。僵尸进程会保留在系统中,直到父进程读取其状态信息,这通常是通过 wait()
或 waitpid()
系统调用完成的。
僵尸进程会占用系统资源,比如进程表中的条目,尽管它们不再消耗 CPU 或内存资源。如果服务器进程频繁地生成子进程,且每个子进程都需要父进程去处理 SIGCHLD
信号,这确实会增加父进程的负担,影响服务器的并发性能。
为了避免这种情况,服务器进程可以忽略 SIGCHLD
信号,这样子进程结束时就不会变成僵尸进程。这可以通过设置 SIGCHLD
信号的处理行为为 SIG_IGN
来实现。
int main(){if(::fork()){return 0;}int sid=::setsid();if(::fork()){return 0;}for(int fd=0;fd<syscall(_SC_OPEN_MAX);++fd){close(fd);}chdir("/");umask(0);signal(SIGCHLD, SIG_IGN);return 0;
}