apue笔记--第13章 守护进程

守护进程(daemon)是长期存在的一种进程-->在系统引导启动时装入,仅在系统关闭时终止

apue笔记--第13章 守护进程_第1张图片

该版本的Linux使用一个名为 kthreadd 的特殊内核进程来创建其他内核进程,所以 kthreadd 表现为其他内核进程的父进程


创建守护进程 code 13_1

/*
    创建守护进程

output:
kali@kali:~/Desktop/Linux Study/Hellos/Chapter13$ ps -ajx |grep 7014
      1    7014    7013    7013 ?             -1 S     1000   0:00 ./Daemon_13_1
   6592    7298    7297    6592 pts/4       7297 S+    1000   0:00 grep 7014

可以看到守护进程为父进程fork()后的子进程,父进程exit后又init进程接管(ppid = 1)


*/
#include "apue.h"
#include 
#include 
#include 

void deamonize(const char*cmd){
    int i,fd0,fd1,fd2;
    pid_t pid;
    struct rlimit rl;
    struct sigaction sa;
    printf("start daemonize..\n");
    /*
        Clear file creation mask
    */
   umask(0);

   /* 
        Get maximum number of file descriptors
   */
    if(getrlimit(RLIMIT_NOFILE,&rl)<0)
        printf("%s: cannot get file limit",cmd);
    
    /*
        Become a session leader to lose controlling TTY
    */
    if((pid = fork())<0)
        printf("%s: can't fork\n",cmd);
    else if(pid!=0)
        exit(0);
    setsid();/* Create a new session with the calling process as its leader. */

    /*
        Ensure future opens won't allocate controlling TTYs.
    */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);/* 解除所有信号屏蔽  */
    sa.sa_flags = 0;

    if(sigaction(SIGHUP,&sa,NULL)<0){
        printf("%s: can't ignore SIGHUP\n",cmd);
    }
    if((pid = fork())<0)
        printf("%s: cannot fork",cmd);
    else if(pid!=0)
        exit(0);


    /*
        Change the current working directory to the root so
        we won't prevent file systems from being unmounted
    */
    if(chdir("/")<0)
        printf("%s: cannot change directory to /",cmd);

    /*
        Close all file descriptor,
        使得守护进程不再持有从其父进程继承来的任何文件描述符
    */
   if(rl.rlim_max == RLIM_INFINITY){
       rl.rlim_max = 1024;
   }
   for (int i = 0; i < rl.rlim_max; i++)
   {
       close(i);
   }
   

    /*
        Attach file descriptors 0,1,2 to /dev/null
        /dev/null 是黑洞文件,所有写入它的内容都会永远丢失,而尝试从它那儿读取内容则什么也读不到
        使得守护进程不与终端设备相关联,所以其输出无处显示,也无处从交互式用户那里接受输入
    */
    fd0 = open("/dev/null",O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    /*
        Initialize the log file.
    */
    openlog(cmd,LOG_CONS,LOG_DAEMON);
    if(fd0!=0 ||fd1!=1 ||fd2!=2){
        syslog(LOG_ERR,"unexpected file descritors %d %d %d\n",fd0,fd1,fd2);
        exit(1);
    }
    printf("finish daemonize..\n");



}

int main(void){
    deamonize("daemon");
    sigset_t waitmask;
    sigemptyset(&waitmask);
    sigaddset(&waitmask,SIGUSR1);
    
    if(sigsuspend(&waitmask)!=-1)
        printf("sigsuspend err\n");
    //sigsuspend(&waitmask);
}

守护进程的出错记录:syslog()

apue笔记--第13章 守护进程_第2张图片

3种产生日志消息的方法:

apue笔记--第13章 守护进程_第3张图片

 syslogd守护进程读取3种格式的日志消息


单例 守护进程--》使用文件和记录锁来保证一个守护进程只有一个副本在运行

/*
    守护进程 -- 出错记录
    守护进程没有控制终端,所以不能只是简单地写道标准错误上

    --》需要有一个集中的守护进程出错记录措施--》syslog

    单例 守护进程--》使用 文件和记录锁 来保证一个守护进程只有一个副本在运行
    通过文件锁避免重复运行多个守护进程 --> lockf()

output: 查看 DeamonSysLog_13_6守护进程

kali@kali:~/Desktop/LinuxStudy/Hellos/Chapter13$ sudo cat /var/log/syslog    
Jun 17 21:43:11 kali Daemon test.: Daemon process is already running[Resource temporarily unavailable].

kali@kali:~/Desktop/LinuxStudy/Hellos/Chapter13$ pstree -pul
systemd(1)─┬─DaemonSysLog_13(9895,kali)
           ├─Daemon_13_1(7014,kali)
           ├─ModemManager(613)─┬─{ModemManager}(623)
           │                   └─{ModemManager}(626)
           ├─NetworkManager(577)─┬─{NetworkManager}(615)
           │                     └─{NetworkManager}(616)
*/
#include "apue.h"
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 



#define LOCKFILE "/home/kali/Desktop/LinuxStudy/Hellos/Chapter13/daemon.pid"
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)

//#define MAXLINE 1024

#define PRINT_PID() printf( "Row[%d]: getpid=[%d].\n", __LINE__, getpid() )

/**
 * Print a message and return to caller.
 * Caller specifies "errnoflag".
 */
static void err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
    char    buf[MAXLINE];

    vsnprintf(buf, MAXLINE, fmt, ap);
    if (errnoflag)
        snprintf(buf+strlen(buf), MAXLINE-strlen(buf), ": %s",
                 strerror(error));
    strcat(buf, "\n");
    fflush(stdout);     /* in case stdout and stderr are the same */
    fputs(buf, stderr);
    fflush(NULL);       /* flushes all stdio output streams */
}
void err_quit(const char*fmt,...){
    va_list ap;
    va_start(ap,fmt);
    err_doit(0,0,fmt,ap);

    va_end(ap);
    exit(1);

}
extern int lockfile(int);

int already_running(void){
    int     fd;
    char    buf[16];

    fd = open(LOCKFILE,O_RDWR|O_CREAT,LOCKMODE);
    if(fd<0){
        syslog(LOG_ERR,"cannot open %s:%s",LOCKFILE,strerror(errno));
        exit(1);
    }



}


void daemonize(const char*cmd){
    int i,fd0,fd1,fd2;
    pid_t pid;
    struct rlimit rl;
    struct sigaction sa;
    printf("start daemonize..\n");
    /*
        Clear file creation mask,第一步:设置文件模式屏蔽字为0
    */
   umask(0);

   /* 
        Get maximum number of file descriptors
   */
    if(getrlimit(RLIMIT_NOFILE,&rl)<0)
        printf("%s: cannot get file limit",cmd);
    
    /*
        Become a session leader to lose controlling TTY
    */
    if((pid = fork())<0)
        printf("%s: can't fork\n",cmd);
    else if(pid!=0)
        exit(0);

    setsid();/* Create a new session with the calling process as its leader. */

    /*
        Ensure future opens won't allocate controlling TTYs.
    */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);/* 解除所有信号屏蔽  */
    sa.sa_flags = 0;

    if(sigaction(SIGHUP,&sa,NULL)<0){
        printf("%s: can't ignore SIGHUP\n",cmd);
    }
    if((pid = fork())<0)
        printf("%s: cannot fork",cmd);
    else if(pid!=0)
        exit(0);

    PRINT_PID();
    /*
        Change the current working directory to the root so
        we won't prevent file systems from being unmounted
    */
    if(chdir("/")<0)
        printf("%s: cannot change directory to /",cmd);

    /*
        Close all file descriptor,
        使得守护进程不再持有从其父进程继承来的任何文件描述符
    */
   if(rl.rlim_max == RLIM_INFINITY){
       rl.rlim_max = 1024;
   }
   for (int i = 0; i < rl.rlim_max; i++)
   {
       close(i);
   }
   

    /*
        Attach file descriptors 0,1,2 to /dev/null
        /dev/null 是黑洞文件,所有写入它的内容都会永远丢失,而尝试从它那儿读取内容则什么也读不到
        使得守护进程不与终端设备相关联,所以其输出无处显示,也无处从交互式用户那里接受输入
    */
    fd0 = open("/dev/null",O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    /*
        Initialize the log file.
    */
    openlog(cmd,LOG_CONS,LOG_DAEMON);//LOG_CONS--log on the console if errors in sending 
    if(fd0!=0 ||fd1!=1 ||fd2!=2){
        syslog(LOG_ERR,"unexpected file descritors %d %d %d\n",fd0,fd1,fd2);
        exit(1);
    }



    /**
     * 第七步:通过文件锁避免重复运行多个守护进程
     */
    int lockfd = open( LOCKFILE, O_RDWR );
    if( lockfd < 0 )
    {
        syslog( LOG_ERR, "Cannot lock file[%s], aborting[%s].\n", LOCKFILE, strerror(errno) );
        //下面这一行无法打印到控制台,项目上应该打印到日志文件中
        printf( "Cannot lock file[%s], aborting[%s].\n", LOCKFILE, strerror(errno) );
        exit(-1);
    }
    if( lockf(lockfd,F_TLOCK,0) < 0 ) // lockf()--> apply, test or remove a POSIX lock on an open file
    {
        syslog( LOG_ERR, "Daemon process is already running[%s].\n", strerror(errno) );
        //下面这一行无法打印到控制台,项目上应该打印到日志文件中
        printf( "Daemon process is already running[%s].\n", strerror(errno) );
        exit(-2);
    }
    printf("finish daemonize..\n");



}

int main( int argc, char **argv )
{
    PRINT_PID();
    daemonize( "Daemon test." );
    //由于父进程退出,所有只有最后一个子进程执行下面的语句(休眠)
    printf( "This line will not be print for the daemon process has no terminate.\n" );
    while(1)
        sleep(120);
    return 0;
}






 

 

 

 

 

 

你可能感兴趣的:(c,Linux)