守护进程
守护进程(daemon)是生存期长的一种进程。常常在系统引导装入时启动,仅在系统关闭时终止。没有控制终端。1. 父进程ID为0的为内核进程,名字出现在方括号中的为内核守护进程。
2. 图示版本的Linux使用名为kthreadd的特殊内核进程来创建其他内核进程。
3. 对于需要在进程上下文执行工作但却不被用户层进程上下文调用的每一个内核组件,通常有它自己的内核守护进程。如:
- kswapd内存换页守护进程
- flush可以在可用内存达到设置的最小阈值时将脏页冲洗至磁盘
编程规则
- 调用umask将文件模式创建屏蔽字设置为一个已知指(通常为0).
- 调用fork,然后使父进程exit。这样做实现了以下几点:
- 如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止会让shell认为这条命令已经执行完毕。
- 虽然子进程继承了父进程的进程组ID, 但获得了一个新的进程ID,这就保证了子进程不是一个进程组的组长进程。这是调用setsid的先决条件
- 调用setsid创建一个新会话。该调用使三件事发生:a.该进程成为新会话的会话首进程(session leader, 会话首进程是创建该会话的进程),b.成为一个新进程组的组长进程,c.没有控制终端。
- 将当前工作目录更改为根目录。以防工作在挂载的文件系统中导致该文件系统不能被卸载。某些守护进程可能会把当前工作目录更改到某个指定位置,并在此位置进行它们的全部工作。
- 关闭不再需要的文件描述符。可以使用open_max函数或getrlimit函数来判断最高文件描述符值,并关闭直到该值的所有描述符。
- 某些守护进程打开/dev/null使其具有文件描述符0、1和2,这样,任何一个有关标准输入,输出,错误的库例程都不会产生任何效果。
#include <stdio.h>
#include <stdlib.h> /*exit*/
#include <signal.h> /*sigacton*/
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/stat.h> /*umask*/
#include <unistd.h>
void deamonize(const char *cmd)
{
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
# 1
umask(0);
if (getrlimit(RLIMIT_NOFILE, &rl) < 0){
fprintf(stderr, "getrlimit error!\n");
exit(1);
}
# 2
if ((pid = fork()) < 0){
fprintf(stderr, "fork error!\n");
exit(1);
} else if (pid != 0)
exit(0);
# 3
setsid();
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0){
fprintf(stderr, "can't ignore SIGHUP\n");
exit(1);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "fork error!\n");
exit(1);
} else if (pid != 0)
exit(0);
# 4
if (chdir("/") < 0) {
fprintf(stderr, "chdir error!\n");
exit(1);
}
# 5
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for (i = 0; i < rl.rlim_max; i ++)
close(i);
# 6
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
openlog(cmd, LOG_CONS, LOG_DAEMON);
if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
exit(1);
}
}
int main(int argc, char const *argv[])
{
deamonize("deamon");
sleep(100);
return 0;
}