zoukankan      html  css  js  c++  java
  • daemon

    守护进程

    特点:后台运行,没有控制终端。

    常见的系统守护进程:

    keventd:为在内核中运行 计划执行的函数 提供进程上下文。

    kapmd:对计算机系统中具有的高级电源管理提供支持。

    kswapd(pageout daemon):页面调出守护进程,它通过将脏页面以低速写到磁盘上,从而使这些页面在需要时仍可回收使用。即页面回收。

    bdflush:将脏缓冲区从缓冲池(cache buffer)中冲洗到磁盘上。即cache回收。

    Kupdated:每隔一定时间间隔,将脏页面冲洗到磁盘上,以便系统在失效时减少丢失的数据。

    portmap:端口映射,将RPC(Remote procedure call,远程过程调用)程序号映射为网络端口号。

    syslogd:记录系统日志

    xinetd:侦听系统网络接口,以便取得来自网络的对各种网络服务进程的请求。

    nfsd,lockd,rpciod提供对网络文件系统的支持。

    cron:在指定的日期和时间执行指定的命令。

    大多数守护进程都以超级用户(用户id为0)特权运行。没有一个守护进程具有控制终端,其终端名设置为问号(?),终端前台进程组ID设置为-1.

    父进程ID为0的各进程通常为内核进程,进程1通常是init。

    编写守护进程的步骤:

    1. 设置文件模式创建屏蔽字;

      umask(0);清除文件模式创建屏蔽字

    2. fork;

      调用fork(),接着让父进程退出

    3. setsid;

      setsid()设置session leader and process group leader

    4. chdir;

      chdir("/"),更改当前工作目录为根目录

    5. close fd;

      关闭从父进程进程的文件描述符

    6. 打开/dev/null使其具有fd 0 1 2;

      禁掉交互

    禁掉fd 0,1,2后,进程出错怎样跟踪呢,可以通过syslog记录守护进程的信息。

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <sys/types.h>
     4 #include <sys/stat.h>
     5 #include <sys/resource.h>
     6 #include <unistd.h>
     7 #include <fcntl.h>
     8 #include <string.h>
     9 
    10 #define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
    11 
    12 int main(int argc, char** argv)
    13 {
    14     int i, fd0, fd1, fd2, fd;
    15     pid_t pid;
    16     pid_t sid;
    17     struct rlimit rl;
    18     char acCwd[128]={0};
    19 
    20     //1. clear file creation mask
    21     umask(0);
    22     //2. fork()
    23     pid = fork();    
    24     if (pid < 0)
    25     {
    26         printf("fork error
    ");
    27         exit(1);
    28     }
    29     else if (pid != 0)
    30         exit(0);
    31     
    32     printf("child process ID:%d
    ", getpid());
    33     //3. setsid()
    34     sid = setsid();
    35     printf("child process session ID:%d
    ", sid);
    36     //4. chdir()
    37     printf("cwd real len:%d", getcwd(acCwd, 128));
    38     printf("
    path:%s
    ", acCwd);
    39     if (chdir("/") < 0)
    40     {
    41         printf("can't change current work dir
    ");
    42         exit(1);
    43     }
    44     memset(acCwd, '', 128);
    45     getcwd(acCwd, 128);
    46     printf("after chdir, current work dir:%s
    ", acCwd);
    47     
    48     //5. close all open file descriptor
    49     if (0 != getrlimit(RLIMIT_NOFILE, &rl))    
    50     {
    51         printf("get max number of fd failed
    ");
    52         exit(1);
    53     }
    54 
    55     if (rl.rlim_max == RLIM_INFINITY)
    56         rl.rlim_max = 1024;
    57     for (i = 0; i < rl.rlim_max; ++i)
    58         close(i);
    59     
    60     //6. attach file descriptors 0 1 2 to /dev/null
    61     //way 1:
    62     fd0 = open("/dev/null", O_RDWR);
    63     fd1 = dup(0);
    64     fd2 = dup(0);
    65     
    66     //way 2:
    67     //fd = open("/dev/null", O_RDWR);
    68     //dup2(fd, 1);
    69     //dup2(fd, 2);
    70 
    71         //7.initialize log file
    72         openlog("frank-daemon", LOG_CONS, LOG_DAEMON);
    73         if (fd0 != 0 | fd1 != 1 || fd2 != 2)
    74        {
    75             syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0 , fd1, fd2);
    76             exit(1);
    77        }
    78 
    79 
    80     while(1)
    81         sleep(999);
    82 
    83     return 0;
    84 }        //这一段代码存在的一个隐含问题是,子进程先执行而父进程并没有结束。所以需要有方式保证让父进程先终止

    第二次fork()是可选的,其作用是确保不会打开控制终端。

    因为打开一个控制终端的条件是该进程必须是会话组长(session leader),而通过二次fork后,确保第二次fork出来的子进程不是session leader.

     下面个函数是调用两次fork函数的daemon初始化函数:

     1 void daemonize2(const char* cmd)
     2 {
     3     int i, fd0, fd1, fd2, fd;
     4     pid_t pid;
     5     pid_t sid;
     6     struct rlimit rl;
     7 
     8     pr_ids("xxxxxxxx");
     9     //1. clear file creation mask
    10     umask(0);
    11     //2. fork()
    12     pid = fork();    
    13     if (pid < 0)
    14     {
    15         printf("fork error
    ");
    16         exit(1);
    17     }
    18     else if (pid > 0)//parent
    19     {
    20         pr_ids("parent");
    21         exit(0);
    22     }
    23 
    24     sleep(1);//sleep的作用是让父进程先终止
    25     printf("child process ID:%d
    ", getpid());
    26     //3. setsid()
    27     sid = setsid();
    28     printf("child process session ID:%d
    ", sid);
    29     pr_ids("child");
    30 
    31     //ensure future opens won't allocate controlling TTYs
    32     pid = fork();
    33     if (pid < 0)
    34     {
    35         printf("fork error
    ");
    36         exit(1);
    37     }
    38     else if (pid > 0)//parent
    39     {
    40         exit(0);
    41     }
    42     sleep(1);//sleep的作用是让父进程先终止
    43 
    44     pr_ids("grandchild");
    45 
    46     //4. chdir()
    47     if (chdir("/") < 0)
    48     {
    49         printf("can't change current work dir
    ");
    50         exit(1);
    51     }
    52 
    53     //5. close all open file descriptor
    54     if (0 != getrlimit(RLIMIT_NOFILE, &rl))    
    55     {
    56         printf("get max number of fd failed
    ");
    57         exit(1);
    58     }
    59 
    60     if (rl.rlim_max == RLIM_INFINITY)
    61         rl.rlim_max = 1024;
    62     for (i = 0; i < rl.rlim_max; ++i)
    63         close(i);
    64 
    65     //6. attach file descriptors 0 1 2 to /dev/null
    66     //way 1:
    67     fd0 = open("/dev/null", O_RDWR);
    68     fd1 = dup(0);
    69     fd2 = dup(0);
    70     
    71     //way 2:
    72     //fd = open("/dev/null", O_RDWR);
    73     //dup2(fd, 1);
    74     //dup2(fd, 2);
    75     
    76     //7. initialize the log file
    77     openlog(cmd, LOG_CONS|LOG_PID, LOG_DAEMON);
    78     if (fd0 != 0 | fd1 != 1 || fd2 != 2)
    79     {
    80         syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0 , fd1, fd2);
    81         exit(1);
    82     }
    83     
    84     pr_ids("grandchild");
    85 }

    保证父进程先终止的运行结果:

    没有保证父进程先终止的运行结果:

  • 相关阅读:
    Spring Boot配置过滤器的两种方式
    Redis工具类封装RedisUtils
    Android_开机动画
    Android_OTA升级
    QT_学习笔记
    松翰单片机_SN8F570310——ADC
    全志_基于dts设备树驱动开发
    松翰单片机_SN8F570310
    松翰单片机_SN8F570310——GPIO
    松翰单片机_SN8F570310——COM & OPA
  • 原文地址:https://www.cnblogs.com/black-mamba/p/6701573.html
Copyright © 2011-2022 走看看