zoukankan      html  css  js  c++  java
  • 多进程编程之守护进程Daemonize

    1、守护进程

      守护进程(daemon)是一类在后台运行的特殊进程,用于执行特定的系统任务。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。另一些只在需要的时候才启动,完成任务后就自动结束。所有的守护进程都没有控制终端,其终端名设置为问号。

    2、编程规则

      1)首先调用umask函数将文件模式创建屏蔽字设置为一个已知值,通常是0;

      umask函数为进程设置文件模式创建屏蔽字,并返回以前的值。umask也是shell命令,功能和umask函数一样。

    1 #include <sys/stat.h>
    2 
    3 mode_t umask(mode_t mask);

      在进程创建一个新的文件或目录时,如调用open函数创建一个新文件,新文件的实际存取权限是mode与umask按照  mode&~umask运算以后的结果。umask函数用来修改进程的umask。

      首先看一下umask命令的作用:

        首先查看一下当前的umask为022,用vi创建一个umask_3.c,查看该文件的权限为644,修改umask为0,vi创建umask_4.c,查看该文件的权限为666。

         

      umask函数的使用:

        实现函数,首先修改当前进程umask为0.创建fan_test1文件,然后修改umask为006,创建文件fan_test2,输出结果如下图

     1 #include <stdio.h>
     2 #include <sys/stat.h>
     3 
     4 int main()
     5 {
     6         umask(0);
     7         if (creat("fan_test1",S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) < 0)
     8                 printf("error creat
    ");
     9         umask(S_IROTH|S_IWOTH);
    10         if (creat("fan_test2",S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) < 0)
    11                 printf("error creat
    ");
    12         return 0;
    13 }

        

      2)调用fork,然后是父进程exit。创建守护进程最关键的一步是调用setsid函数创建一个新的会话(会话是一个或多个进程组的集合),使守护进程成为新会话的首进程,并成为新进程组的组长,失去当前的控制终端,成为一个没有控制终端的进程。而调用函数setsid()之前,要保证当前进程不是进程组的组长,否则该函数返回-1;要保证当前进程不是进程组的组长,就要调用fork,父进程退出;fork创建的子进程和父进程在同一个进程组中,进程组的组长必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,fork之后调用setsid就没有问题了;

      3)调用setsid()函数创建一个新的会话:

           (1) 该进程变成新会话首进程,会话首进程通常是创建该会话的进程,该进程是新会话中的唯一的进程。
           (2) 该进程成为一个新进程组的组长进程,新进程组ID就是调用进程的ID。
           (3) 该进程没有控制终端,如果在调用setsid之前该进程有一个控制终端,那么这种联系将被中断。

      4)将当前的工作目录更改为根目录。如果守护进程所在的目录为一个挂载的文件系统,那么该文件系统就不能被卸载;另外如果守护进程所在的目录不是根目录,启动守护进程之后,当前工作目录所在的文件夹将不能删除;调用函数chdir("/");

       5)关闭打开的文件描述符;子进程有可能从父进程继承了一些打开的文件,这些文件可能守护进程将不再使用,但这些文件描述符依然消耗系统资源,也有可能导致相关的文件系统无法被卸载。

      关闭文件描述符的方法:

     1 方法1:
     2 #include <sys/resource.h>
     3     
     4 struct rlimit    rl;
     5     
     6 if(getrlimit(RLIMIT_NOFILE, &rl) < 0)
     7 {
     8   perror("getrlimit(RLIMIT_NOFILE, &rl)");
     9   return -1;
    10 }
    11 if(rl.rlim_max == RLIM_INFINITY)
    12 {
    13   rl.rlim_max = 1024;
    14 }
    15 for(i = 0; i < rlim_max; i++)
    16 {
    17   close(i);
    18 }
    19 方法2:
    20 max_fd = sysconf(_SC_OPEN_MAX);
    21 for(i = 0; i < max_fd; i++)
    22 {
    23     close(i);
    24 }

      6)守护进程打开/dev/null使其具有文件描述符0、1和2,也就是将0、1、2的文件描述符都指向/dev/null; 

    1 /*attach file descriptions 0,1 and  2 to /dev/null*/
    2 fd0 = open("/dev/null", O_RDWR);
    3 fd1 = dup(0);
    4 fd2 = dup(0);

      为什么会有这一步操作,守护进程已经脱离终端了,为什么还要将文件描述符0、1、2重定向到/dev/null呢,并且前面已经有了关闭所有文件描述符的操作,文件描述符已经关闭了,在此处为什么还要再打开,两者不是冲突了么?

      7)再次调用fork,使父进程退出;第一次fork()后的子进程已经成为会话组的组长,有权利再调出一个终端,如果出现此情况,则未达到完全脱离终端的目的,此时再调用fork并退出父进程,使得此时的子进程成为完全的后台进程,独立于任何的终端,在第二次fork之前通常会忽略SIGHUP信号,这是因为会话首进程退出时会给该会话中的前台进程组(当打开控制终端后,就有一个前台进程组)的所有进程发送SIGHUP信号,而信号的默认处理函数通常是进程终止,因此需要对信号进程屏蔽处理

    实现一个守护进程:

     1 #include <stdio.h>
     2 #include <sys/stat.h>
     3 #include <signal.h>
     4 #include <sys/resource.h>
     5 #include <unistd.h>
     6 #include <stdlib.h>
     7 void daemonize(void)
     8 {
     9     struct rlimit rl;
    10     pid_t pid;
    11     struct sigaction sa;
    12     int i;
    13     umask(0);
    14     if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
    15         printf("error getrlimit
    ");
    16         exit(0);
    17     }
    18     if ((pid = fork()) < 0) {
    19         printf("error fork
    ");
    20         exit(0);
    21     } else if (pid != 0) {
    22         exit(0);
    23     }
    24     setsid();
    25     sa.sa_handler = SIG_IGN;
    26     sigemptyset(&sa.sa_mask);
    27     sa.sa_flags = 0;
    28     if (sigaction(SIGHUP, &sa, NULL) < 0) {
    29         printf("error sigaction
    ");
    30         exit(0);
    31     }
    32     if (chdir("/") < 0) {
    33         printf("error chdir
    ");
    34         exit(0);
    35     }
    36     if (rl.rlim_max == RLIM_INFINITY)
    37         rl.rlim_max = 1024;
    38     for (i = 0; i < rl.rlim_max; i++)
    39         close(i);
    40 
    41     if ((pid = fork()) < 0) {
    42          printf("error fork
    ");
    43          exit(0);
    44      } else if (pid != 0) {
    45           exit(0);
    46      }
    47     
    48 }
    49 
    50 int main()
    51 {
    52     daemonize();
    53     while(1) {
    54         printf("111111111111111
    ");
    55         sleep(2);
    56     }
    57     
    58 }

      

  • 相关阅读:
    PHP:__get()、__set()、__isset()、__unset()、__call()、__callStatic()六个魔术方法
    概念:RPG游戏中两个兵种互相攻击的逻辑
    php怎么获取checkbox复选框的内容?
    20150724之问题
    Uploadify 之使用
    oneThink后台添加插件步骤详解
    针对各种浏览器,前端解决方案(持续更新...)
    解决IE8中select下拉列表文字上下不居中的问题
    针对IE6 7 8当独写样式
    document对象详解
  • 原文地址:https://www.cnblogs.com/funblogs/p/7542818.html
Copyright © 2011-2022 走看看