Chinaunix首页 | 论坛 | 博客
  • 博客访问: 147264
  • 博文数量: 14
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 145
  • 用 户 组: 普通用户
  • 注册时间: 2014-02-12 15:27
个人简介

文章分类

全部博文(14)

文章存档

2014年(14)

分类: C/C++

2014-02-26 15:34:45

一、守护进程简介
   守护进程是linux后台服务进程,它独立于终端(shell)并且周期性的执行某个特定任务。守护进程必须从当前的运行环境中脱离出来,当前环境通常是从父进程继承下来的,包括文件描述符、控制终端(shell)、工作目录、文件权限掩码、进程组、会话。守护进程的启动方式有三种:1、/etc/rc.d随系统启动   2、crond定时任务启动  3、终端(shell)启动。
   实际上守护进程与其他进程一样,只是在编程时按照特定的步骤进行而已。

二、守护进程编写步骤
   1、后台运行
       首先创建子进程,父进程退出。此时程序给shell造成退出的假象,从而使子进程形式上脱离终端,用户可以在终端执行其他命令。程序的其他功能则在子进程中完成。
       BTW:Linux中当父进程先于子进程退出时,子进程会变成孤儿进程,被系统的init(进程号为1)进程收养,此时子进程的父进程是init。
       可以用如下代码实现:

点击(此处)折叠或打开

  1. if((pid = fork()) < 0) //1. fork , father process exit
  2. {
  3.     perror("error fork");
  4.     exit(1);
  5. }
  6. else if(pid > 0) exit(0);
  7. else {}
    2、创建新会话,脱离控制终端、进程组、会话
       首先要了解两个概念:进程组和会话期
        进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等                      于进程组ID。且该进程组ID不会因组长进程的退出而受到影响。
        会话周期:会话期是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
       此过程非常简单,调用 setsid()函数就可以完成。此处的setsid()函数作用有以下几点:
        1、创建新会话并担任组长
        2、使进程脱离原会话的控制
        3、使进程脱离原进程组控制
        4、使进程脱离原控制终端控制
       由于在fork时,子进程继承了父进程的进程组、会话、控制终端,虽然父进程退出,但是这些信息没有改变,所以setsid使子进程完全独立出来,摆脱其他进程的控制。
   3、完全脱离控制终端
       此时进程成为会话组长,虽然是脱离了终端控制;但是还可以重新申请控制终端,所以需要进行限制,通过再创建子进程2使其不是会话组长,并且退出子进程1.

点击(此处)折叠或打开

  1. if((pid = fork()) < 0) //3
  2.     {
  3.         perror("error fork");
  4.         exit(1);
  5.     }
  6.     else if(pid > 0) exit(0);
  7.     else {}
    4、改变当前工作目录
      当前工作目录,如果有进程在活动的话,是无法卸载的。所以要改变工作目录,通常设置为根目录(/)。其他需要也可以改成其他目录。如日志(/tmp)
      chdir("/");
    5、修改文件权限掩码
      文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。
    6、关闭文件描述符
      子进程从父进程那里继承了文件描述符,但是子进程从不使用,这样就会造成资源浪费,而且文件系统无法卸载。所以应该关闭所有继承的文件描述符。

点击(此处)折叠或打开

  1. for(i = 0; i < getdtablesize(); i++) // 6
  2.         close(i);
    7、信号处理
      1、一般结束守护进程通过kill命令,所以程序需要响应kill发出的SIGTERM信号,来正常退出。
      2、对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进            程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。
          signal(SIGCHLD,SIG_IGN);
          这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。

三、实例

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <sys/wait.h>
  7. #include <signal.h>

  8. void sigterm_handler(int arg);
  9. volatile int _running = 1;

  10. void init_daemon()
  11. {
  12.     pid_t pid;
  13.     int i;
  14.     
  15.     if((pid = fork()) < 0) //1. fork , father process exit
  16.     {
  17.         perror("error fork");
  18.         exit(1);
  19.     }
  20.     else if(pid > 0) exit(0);
  21.     else {}

  22.     setsid(); // 2
  23.     if((pid = fork()) < 0) //3
  24.     {
  25.         perror("error fork");
  26.         exit(1);
  27.     }
  28.     else if(pid > 0) exit(0);
  29.     else {}
  30.     
  31.     chdir("/"); // 4
  32.     umask(0); // 5
  33.     
  34.     for(i = 0; i < getdtablesize(); i++) // 6
  35.         close(i);
  36. }

  37. int main(void)
  38. {
  39.     int i, fd, len;
  40.     char *buf = "this is a Dameon\n";
  41.     len = strlen(buf);
  42.     
  43.     init_daemon();
  44.     signal(SIGTERM, sigterm_handler);
  45.     signal(SIGCHLD, SIG_IGN);
  46.     
  47.     while( _running )
  48.     {
  49.         if((fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600)) < 0)
  50.         {
  51.             perror("open");
  52.             exit(1);
  53.         }
  54.         write(fd, buf, len);
  55.         close(fd);
  56.         sleep(1); // 1s
  57.     }
  58. }

  59. void sigterm_handler(int arg)
  60. {
  61.     _running = 0;
  62. }

阅读(3195) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~