Linux编程之《守护进程》,linux《守护进程》


Intro

-----

守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统有很多守护进程,大多数服务都是通过守护进程实现的,同时,守护进程还能完成许多系统任务,例如,作业规划进程crond、打印进程lqd等(这里的结尾字母d就是Daemon的意思)。

由于在Linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。但是守护进程却能够突破这种限制,它从被执行开始运转,直到整个系统关闭时才退出。如果想让某个进程不因为用户或终端或其他地变化而受到影响,那么就必须把这个进程变成一个守护进程,下面将完整代码贴上。

  1 /************************************************
  2  * 该例程讲解Linux守护进程的编程方法
  3 ************************************************/
  4 #include <unistd.h>
  5 #include <signal.h>
  6 #include <sys/param.h>  // NOFILE
  7 #include <sys/stat.h>   // umask
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include <time.h>
 11 #include <assert.h>
 12 
 13 bool initDaemon()
 14 {
 15     // 屏蔽一些有关控制终端操作的信号
 16     // 防止守护进程没有正常运转起来时,因控制终端受到干扰退出或挂起
 17     assert(signal(SIGINT, SIG_IGN) != SIG_ERR); // 终端中断
 18     assert(signal(SIGHUP, SIG_IGN) != SIG_ERR); // 连接挂断
 19     assert(signal(SIGQUIT, SIG_IGN) != SIG_ERR);// 终端退出
 20     assert(signal(SIGPIPE, SIG_IGN) != SIG_ERR);// 向无读进程的管道写数据
 21     assert(signal(SIGTTOU, SIG_IGN) != SIG_ERR);// 后台程序尝试写操作
 22     assert(signal(SIGTTIN, SIG_IGN) != SIG_ERR);// 后台程序尝试读操作
 23     assert(signal(SIGTERM, SIG_IGN) != SIG_ERR);// 终止
 24 
 25     // [1] 创建一个子进程,父进程退出
 26     int pid = fork();
 27     if (pid)
 28     {
 29         // 父进程退出
 30         exit(0);
 31     }
 32     else if (pid < 0)
 33     {
 34         return false;
 35     }
 36 
 37     // 子进程继续运行
 38     
 39     // [2] 在子进程中创建新的会话,setsid有三个作用
 40     // a.让进程摆脱原会话的控制
 41     // b.让进程摆脱原进程组的控制
 42     // c.让进程摆脱原控制终端的控制
 43     int ret = setsid();
 44     if (ret < 0)
 45     {
 46         return false;
 47     }
 48 
 49     // [3] 禁止进程重新打开控制终端
 50     // 进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端
 51     // 可以通过使进程不再成为会话组长来禁止进程重新打开控制终端
 52     pid = fork();
 53     if (pid)
 54     {
 55         // 结束第一个子进程
 56         exit(0);
 57     }
 58     else if (pid < 0)
 59     {
 60         return false;
 61     }
 62 
 63     // 第二个子进程继续运行
 64 
 65     // [4] 关闭打开的文件描述符
 66     // 进程从创建它的父进程那里继承了打开的文件描述符,如果不关闭,将会浪费系统资源,
 67     // 造成进程所在的文件系统无法卸下以及引起无法预料的错误
 68     for (int i = 0; i < NOFILE; ++i)
 69     {
 70         close(i);
 71     }
 72 
 73     // [5] 改变当前工作目录
 74     // 进程活动时,其工作目录所在的文件系统不能卸下,一般将工作目录改变到根目录
 75     ret = chdir("/");
 76     if (ret < 0)
 77     {
 78         return false;
 79     }
 80 
 81     // [6] 重新设置文件创建掩码
 82     // 进程从创建它的父进程那里继承了文件创建掩码,它可能修改守护进程所创建的文件的存取位
 83     // 所以将文件创建掩码清除
 84     umask(0);
 85 
 86     return true;
 87 }
 88 
 89 int main()
 90 {
 91     // 初始化守护进程
 92     bool ret = initDaemon();
 93     if (!ret)
 94     {
 95         printf("Init daemon failed\n");
 96         return 1;
 97     }
 98 
 99     FILE* file = NULL;
100     time_t t = 0;
101 
102     // 每隔10秒向test.log报告运行状态
103     while (true)
104     {
105         sleep(10);
106         file = fopen("./var/test.log", "a+");
107         if (file != NULL)
108         {
109             t = time(NULL);
110             fprintf(file, "I am here at %s\n", asctime(localtime(&t)));
111             fclose(file);
112         }
113     }
114 
115     return 0;
116 }

 

该例子的github地址:https://github.com/chxuan/samples/blob/master/Daemon/Daemon.cpp

相关内容