daemon

守护进程

特点:后台运行,没有控制终端。

常见的系统守护进程:

keventd:为在内核中运行 计划执行的函数 提供进程上下文。

kapmd:对计算机系统中具有的高级电源管理提供支持。

kswapd(pageout daemon):页面调出守护进程,它通过将脏页面以低速写到磁盘上,从而使这些页面在需要时仍可回收使用。即页面回收。

bdflush:将脏缓冲区从缓冲池(cache buffer)中冲洗到磁盘上。即cache回收。

Kupdated:每隔一定时间间隔,将脏页面冲洗到磁盘上,以便系统在失效时减少丢失的数据。

portmap:端口映射,将RPC(Remote procedure call,远程过程调用)程序号映射为网络端口号。

syslogd:记录系统日志

xinetd:侦听系统网络接口,以便取得来自网络的对各种网络服务进程的请求。

nfsd,lockd,rpciod提供对网络文件系统的支持。

cron:在指定的日期和时间执行指定的命令。

大多数守护进程都以超级用户(用户id为0)特权运行。没有一个守护进程具有控制终端,其终端名设置为问号(?),终端前台进程组ID设置为-1.

父进程ID为0的各进程通常为内核进程,进程1通常是init。

 

 

编写守护进程的步骤:

1. 设置文件模式创建屏蔽字;

  umask(0);清除文件模式创建屏蔽字

2. fork;

  调用fork(),接着让父进程退出

3. setsid;

  setsid()设置session leader and process group leader

4. chdir;

  chdir("/"),更改当前工作目录为根目录

5. close fd;

  关闭从父进程进程的文件描述符

6. 打开/dev/null使其具有fd 0 1 2;

  禁掉交互

禁掉fd 0,1,2后,进程出错怎样跟踪呢,可以通过syslog记录守护进程的信息。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <sys/resource.h>
 6 #include <unistd.h>
 7 #include <fcntl.h>
 8 #include <string.h>
 9 
10 #define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
11 
12 int main(int argc, char** argv)
13 {
14     int i, fd0, fd1, fd2, fd;
15     pid_t pid;
16     pid_t sid;
17     struct rlimit rl;
18     char acCwd[128]={0};
19 
20     //1. clear file creation mask
21     umask(0);
22     //2. fork()
23     pid = fork();    
24     if (pid < 0)
25     {
26         printf("fork error\n");
27         exit(1);
28     }
29     else if (pid != 0)
30         exit(0);
31     
32     printf("child process ID:%d\n", getpid());
33     //3. setsid()
34     sid = setsid();
35     printf("child process session ID:%d\n", sid);
36     //4. chdir()
37     printf("cwd real len:%d", getcwd(acCwd, 128));
38     printf("\npath:%s\n", acCwd);
39     if (chdir("/") < 0)
40     {
41         printf("can't change current work dir\n");
42         exit(1);
43     }
44     memset(acCwd, '\0', 128);
45     getcwd(acCwd, 128);
46     printf("after chdir, current work dir:%s\n", acCwd);
47     
48     //5. close all open file descriptor
49     if (0 != getrlimit(RLIMIT_NOFILE, &rl))    
50     {
51         printf("get max number of fd failed\n");
52         exit(1);
53     }
54 
55     if (rl.rlim_max == RLIM_INFINITY)
56         rl.rlim_max = 1024;
57     for (i = 0; i < rl.rlim_max; ++i)
58         close(i);
59     
60     //6. attach file descriptors 0 1 2 to /dev/null
61     //way 1:
62     fd0 = open("/dev/null", O_RDWR);
63     fd1 = dup(0);
64     fd2 = dup(0);
65     
66     //way 2:
67     //fd = open("/dev/null", O_RDWR);
68     //dup2(fd, 1);
69     //dup2(fd, 2);
70 
71         //7.initialize log file
72         openlog("frank-daemon", LOG_CONS, LOG_DAEMON);
73         if (fd0 != 0 | fd1 != 1 || fd2 != 2)
74        {
75             syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0 , fd1, fd2);
76             exit(1);
77        }
78 
79 
80     while(1)
81         sleep(999);
82 
83     return 0;
84 }        //这一段代码存在的一个隐含问题是,子进程先执行而父进程并没有结束。所以需要有方式保证让父进程先终止

 

第二次fork()是可选的,其作用是确保不会打开控制终端。

因为打开一个控制终端的条件是该进程必须是会话组长(session leader),而通过二次fork后,确保第二次fork出来的子进程不是session leader.

 下面个函数是调用两次fork函数的daemon初始化函数:

 1 void daemonize2(const char* cmd)
 2 {
 3     int i, fd0, fd1, fd2, fd;
 4     pid_t pid;
 5     pid_t sid;
 6     struct rlimit rl;
 7 
 8     pr_ids("xxxxxxxx");
 9     //1. clear file creation mask
10     umask(0);
11     //2. fork()
12     pid = fork();    
13     if (pid < 0)
14     {
15         printf("fork error\n");
16         exit(1);
17     }
18     else if (pid > 0)//parent
19     {
20         pr_ids("parent");
21         exit(0);
22     }
23 
24     sleep(1);//sleep的作用是让父进程先终止
25     printf("child process ID:%d\n", getpid());
26     //3. setsid()
27     sid = setsid();
28     printf("child process session ID:%d\n", sid);
29     pr_ids("child");
30 
31     //ensure future opens won't allocate controlling TTYs
32     pid = fork();
33     if (pid < 0)
34     {
35         printf("fork error\n");
36         exit(1);
37     }
38     else if (pid > 0)//parent
39     {
40         exit(0);
41     }
42     sleep(1);//sleep的作用是让父进程先终止
43 
44     pr_ids("grandchild");
45 
46     //4. chdir()
47     if (chdir("/") < 0)
48     {
49         printf("can't change current work dir\n");
50         exit(1);
51     }
52 
53     //5. close all open file descriptor
54     if (0 != getrlimit(RLIMIT_NOFILE, &rl))    
55     {
56         printf("get max number of fd failed\n");
57         exit(1);
58     }
59 
60     if (rl.rlim_max == RLIM_INFINITY)
61         rl.rlim_max = 1024;
62     for (i = 0; i < rl.rlim_max; ++i)
63         close(i);
64 
65     //6. attach file descriptors 0 1 2 to /dev/null
66     //way 1:
67     fd0 = open("/dev/null", O_RDWR);
68     fd1 = dup(0);
69     fd2 = dup(0);
70     
71     //way 2:
72     //fd = open("/dev/null", O_RDWR);
73     //dup2(fd, 1);
74     //dup2(fd, 2);
75     
76     //7. initialize the log file
77     openlog(cmd, LOG_CONS|LOG_PID, LOG_DAEMON);
78     if (fd0 != 0 | fd1 != 1 || fd2 != 2)
79     {
80         syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0 , fd1, fd2);
81         exit(1);
82     }
83     
84     pr_ids("grandchild");
85 }

 

 

保证父进程先终止的运行结果:

没有保证父进程先终止的运行结果:

 

posted @ 2017-04-12 21:52  suonikeyinsu  Views(352)  Comments(0Edit  收藏  举报