守护进程

什么是守护进程?

  守护进程是运行于后台、常驻内存的特殊进程,独立于控制终端。它的父进程是init进程,它是一个孤儿进程,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都被丢到了/dev/null中。守护进程一般用作服务器进程,如httpd,syslogd等。

 

进程、进程组、会话、控制终端之间的联系

  因为守护进程的创建需要改变这几个环境参数,所以需要了解它们的关系:

  

  进程组:每个进程都会属于一个进程组(process group),每个进程组中可以包含多个进程。进程组会有一个进程组领导进程 (process group leader),领导进程的PID成为进程组的ID (process group ID, PGID),以识别进程组。

  会话:其实叫做会话期(session),它包括了期间的所有进程组,会话期起始于login,终止于logout。一般login是shell终端,所以shell终端又是会话期的第一个进程。建立一个新的会话用setsid(),setsid()会建立一个会话同时设置PGID。

  控制终端(tty):一般指shell终端,或者什么也没有。

 

实例:创造一个daemon,每隔十秒向/mydaemon.log文件写入当前时间共三次)

 1 void mydaemon(void)
 2 {    
 3     pid_t pid;
 4     int fd, i, nfiles;
 5     struct rlimit rl;
 6 
 7     pid = fork();
 8     if(pid < 0)
 9         ERROR_EXIT("First fork failed!");
10 
11     if(pid > 0)
12         exit(EXIT_SUCCESS);// father exit
13 
14     if(setsid() == -1)
15         ERROR_EXIT("setsid failed!");
16 
17     pid = fork();
18     if(pid < 0)
19         ERROR_EXIT("Second fork failed!");
20 
21     if(pid > 0)// father exit
22         exit(EXIT_SUCCESS);
23     #ifdef RLIMIT_NOFILE
24     /* 关闭从父进程继承来的文件描述符 */
25     if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
26         ERROR_EXIT("getrlimit failed!");
27     nfiles = rl.rlim_cur = rl.rlim_max;
28     setrlimit(RLIMIT_NOFILE, &rl);
29     for(i=3; i<nfiles; i++)
30         close(i);
31    #endif
32     /* 重定向标准的3个文件描述符 */
33     if(fd = open("/dev/null", O_RDWR) < 0)
34         ERROR_EXIT("open /dev/null failed!");
35     for(i=0; i<3; i++)
36         dup2(fd, i);
37    if(fd > 2) close(fd);
38     /* 改变工作目录和文件掩码常量 */
39     chdir("/");
40     umask(0);
41 }

  分析如下:(来源于博主会飞的小丑

 

  

  执行情况如下:

#define ERROR_EXIT(m)\
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}\
while(0)

int main(int argc, char **argv)
{
    time_t t;
    int fd, i;
    mydaemon();
    fd = open("./mydaemon.log", O_RDWR|O_CREAT, 0644);
    if(fd < 0)
        ERROR_EXIT("open /mydaemon.log failed!");
    for(i=0; i<3; i++)
    {
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd, buf, strlen(buf));
        sleep(10);
    }
    close(fd);
    return 0;
}

  运行结果如下:

  

  有图可知,在open /mydaemon.log文件没有权限,而切换到root权限后执行成功,文件的内容也是每10秒间隔写入一次时间。因为该博主当时创建的mydaemon程序的工作目录已经切换到了根目录,所以普通用户没有在根目录下创建文件的权限。如果这里将文件创建在当前目录的话就不用切换到root权限。

   

函数daemon()  

  其实在linux下已经有函数daemon函数用于创建一个后台程序了,所以上面的工作已经被加入了函数库,直接使用轮子

#include <unistd.h>
int daemon(int nochdir, int noclose);

DESCRIPTION
  The daemon() function is for programs wishing to detach themselves from the controlling terminal and run in the background as system daemons.
  If nochdir is zero, daemon() changes the process's current working directory to the root directory ("/"); otherwise,the current working directory is left unchanged.
  If noclose is zero, daemon() redirects standard input, standard output and standard error to /dev/null; otherwise, no changes are mad to these file descriptors.

  通过man手册的描述可知,函数daemon接收两个参数:

  nochdir:如果是0,将当前工作目录切换到根目录"/",否则工作目录不改变。

  noclose:如果是0,将0,1,2重定向到/dev/null,否则不变。

mydeamon与daemon  

  其实可以将mydeamon函数稍加修改符合daemon函数

 1 void mydaemon(int nochdir, int noclose)
 2 {    
 3     pid_t pid;
 4     int fd, i, nfiles;
 5     struct rlimit rl;
 6 
 7     pid = fork();
 8     if(pid < 0)
 9         ERROR_EXIT("First fork failed!");
10 
11     if(pid > 0)
12         exit(EXIT_SUCCESS);// father exit
13 
14     if(setsid() == -1)
15         ERROR_EXIT("setsid failed!");
16 
17     pid = fork();
18     if(pid < 0)
19         ERROR_EXIT("Second fork failed!");
20 
21     if(pid > 0)// father exit
22         exit(EXIT_SUCCESS);
23    #ifdef RLIMIT_NOFILE
24     /* 关闭从父进程继承来的文件描述符 */
25     if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
26         ERROR_EXIT("getrlimit failed!");
27     nfiles = rl.rlim_cur = rl.rlim_max;
28     setrlimit(RLIMIT_NOFILE, &rl);
29     for(i=3; i<nfiles; i++)
30         close(i);
31     #endif
32     /* 重定向标准的3个文件描述符 */
33     if(!noclose)
34     {
35         if(fd = open("/dev/null", O_RDWR) < 0)
36             ERROR_EXIT("open /dev/null failed!");
37         dup2(fd, STDIN_FILENO);
38         dup2(fd, STDOUT_FILENO);
39         dup2(fd, STDERR_FILENO);    
40         if(fd > 2)
41             close(fd);
42     }
43 
44     /* 改变工作目录和文件掩码常量 */
45     if(!nochdir)
46         chdir("/");
47     umask(0);
48 }

  问题:这样普通用户调用mydaemon(0,0)函数时还是会在console提示open mydaemon.log failed!: Permission denied,但是在37~39行已经将它们重定向到了/dev/null了,很疑惑??当你close(0,1,2)之后普通用户才不会提示错误信息。

  

 

posted @ 2017-09-28 14:17  bw98  阅读(336)  评论(0编辑  收藏  举报