Start A Daemon

概述

守护进程(daemon)听着像一个ghost,无处不在而又默默存在。其实还真是这样一个存在。 它默默运行在系统的后台并具有以下特征:

  • 父进程是init(1号进程)
  • 没有和任何控制终端关联,所以也不会收到诸如SIGINT( 信号名称,Ctrl-C会产生这个信号 )、SIGTSTP( TSTP的意思是tty stop,即在control terminal上输入了ctl-z代表的suspend键,会产生这个信号,继而发送给进程)等信号

The Implementation of A Daemon

在Linux中,每个从终端开始运行的进程都会依附于这个终端,这个终端是这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。 但是守护进程为了实现不受某些参数的影响,不和终端关联,必须要从这个限制突破出去,成为一个自由的ghost。从被执行开始运转,直到整个系统关闭时才退出。 如果想让某个进程不因为用户或终端或其他地变化而受到影响,那么就必须把这个进程变成一个守护进程,脱离于终端并且在后台运行。

接下来就可以用某种方法实现这个过程:

  • 创建子进程,父进程退出 由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成一程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离。 在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程,就会自动由1号进程(init)收养它,这样原先的子进程就会变成init进程的子进程。
  • 在子进程中创建新会话 这个步骤是创建守护进程中最重要的一步,在这里使用的是系统函数setsid,在具体介绍setsid之前,首先要了解两个概念:进程组和会话期【2】。 进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程组ID不会因组长进程的退出而受到影响。 会话周期:会话期是一个或多个进程组的集合。通常,一个会话开始与用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。

接下来就可以具体介绍setsid的相关内容: setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid有下面的3个作用:

  • 让进程摆脱原会话的控制
  • 让进程摆脱原进程组的控制
  • 让进程摆脱原控制终端的控制

那么,在创建守护进程时为什么要调用setsid函数呢?由于创建守护进程的第一步调用了fork函数来创建子进程,再将父进程退出。由于在调用了 fork函数时,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,还还不是真正意义上的独立开来,而setsid函数能够使进程完全独立出来,从而摆脱其他进程的控制。

setsid命令的一般格式:setsid();

    <!-- lang: cpp -->
    pid_t pid = fork(); //fork a process
    if (pid < 0) exit(0); //fork error
    if (pid > 0) exit(0); //father process exit
    setsid();//creat a new session for a process

之前parent和child运行在同一个session里,parent是会话(session)的领头进程,parent进程作为会话的领头进程,如果exit结束执行的话,那么子进程会成为孤儿进程,并被init收养。执行setsid()之后,child将重新获得一个新的会话(session)id。这时守护进程能够完全独立出来,从而摆脱其他进程的控制。

现在用守护进程实现一个每隔10s在/tmp/dameon.log中写入一句话的功能,实现代码(damon.c)如下:

    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<fcntl.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #define MAXFILE 65535
    void main(void)
    {
      pid_t pid;
      int i, fd, len;
      char *Buf="I am Dameon\n";
      len = strlen(buf);
      
      pid = fork(); //第一步
      if(pid<0){
          printf("error fork\n");
          exit(1);
      }
      else if(pid>0)
      exit(0);
      setsid(); //第二步
      chdir("/"); //第三步
      umask(0); //第四步
      for(i=0;i<MAXFILE;i++) //第五步
          close(i);
          if((fd=open("/tmp/dameon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0){
              perror("open");
              exit(1);
      }  
      while(1){
         write(fd,buf,len+1);
          sleep(10);
        }
     }

Daemon开机自启动实现

首先,将daemon.c编译成名为 simple-daemon 然后,写一个shell脚本daemon-script,编辑需要自启动的 simple-daemon程序的名字和路径

<!-- lang: shell -->
###########################
#start writing the script#
###########################
#source function library
PATH=/bin:/usr/bin:/sbin:/usr/sbin
./lib/lsb/init-functions
PROG="simple-daemon"
PROG_PATH="home/huangyi/MyProgram"
start(){
          $PROG_PATH/$PROG
          echo "$PROG started"
          }

stop(){
          echo "begin stop"
      killall $PROG
      echo "$PROG stopped"
          } 再将脚本daemon-script 复制到/etc/init.d/目录下。init进程逐一加载开机启动程序,其实就是运行目录/etc/init.d/里的启动脚本。 最后执行 chkconfig  - -add daemon-script  这样就可以和Apache服务器一样开机自启动,并用服务进行管理。

<!-- lang: shell -->
service daemon-script start   //启动,系统默认是开启的  
service daemon-script stop   //停止
service daemon-script restart  //重启

Reference

[1].http://blog.csdn.net/jiaxiongxu/article/details/6692541

[2].http://www.informit.com/articles/article.aspx?p=366888&seqNum=8

[3].http://my.oschina.net/u/556678/blog/183780



Previous     Next
/
Published under (CC) BY-NC-SA in categories linux  tagged with linux