0xAA55 发表于 2017-1-18 18:46:53

【C】教你如何在CentOS下写一个“呆萌”(守护进程Daemon)

写“呆萌”,也就是后台守护进程(“Daemon”的谐音是“呆萌”),需要注意以下几点:
1、用户如果用ssh控制他的服务器,然后服务器跑了你的呆萌,当用户断开ssh连接的时候,你的呆萌也能继续运行。
2、你的呆萌应该把运行的过程写入到日志里,而不是输出到屏幕上。
3、保证稳定性,别因为无视了一个signal而挂掉。
4、为了便于管理,你应该在临时文件夹写一个lock文件,里面用文本记录你的PID号,以便于用户用脚本控制你的呆萌进程(典型的,使其能兼容chkconfig的配置方式)

一般来说你的守护进程被直接运行的时候,它的父进程应该是bash,然后bash会一直等待你的进程结束,才会继续执行后面的脚本。
为了能让你的程序不堵住bash的脚本执行过程,一般的做法是启动一个新的进程,然后旧进程退出,让bash继续执行,而新进程则作为呆萌保持执行过程。
这里最常用的函数就是fork()了,它在unistd.h里面有定义,这个函数的作用是:
[*]复制自身进程,新的进程拥有完全一样的内存空间(但它一般并不需要完全复制自身的内存数据,虽然此时新的进程的内存数据和旧进程完全相同)。
[*]如果自己是父进程,返回子进程的PID;如果自己是子进程,返回0;如果分身失败,则返回-1.
调用fork()后,旧进程就可以退出了,然后bash继续运行,与此同时你的新进程也在后台运行。但这还不够,因为此时你的旧进程和新进程都不是一个进程组的组长,因此当你的旧进程被杀的时候,新进程也会死。此时你需要调用setsid()来设置新的进程会话ID,也就是让你的新进程成为一个进程组的组长,这样的话它就真正从父进程独立出来了。

此时新进程需要做的事情有以下几个:
[*]把PID写到lock文件里,便于让用户找到你。
[*]关闭没用的文件描述符,比如stdin,stdout和stderr,或者你把stdout和stderr重定向到你的log,但其实自己fopen一个新的fd比做一次重定向方便得多。
[*]设置信号(signal)处理,将无关信号屏蔽,然后对SIGPIPE、SIGHUP、SIGTERM做出响应。


这里给一份例子,展示一个程序如何进入呆萌模式。// 需要的头文件
#include<stdio.h>
#include<signal.h>
#include<stdarg.h>
#include<unistd.h>

// 程序信息
#define EXEC_NAME foo
#define LOCK_FILE "/var/tmp/"EXEC_NAME".lock"
#define LOG_FILE EXEC_NAME".log"

static volatile int g_Quit = 0; // 是否退出
static void _signal_handler(int sig);

void Inst_Log(char *format, ...); // 记录log

//==============================================================================
//Func: _Inst_Daemonize
//Desc: Make program run as a daemon
//------------------------------------------------------------------------------
static int _Inst_Daemonize()
{
        int i,lfp;
        char str;
       
        if(getppid() == 1)
        {
                fprintf(stderr, EXEC_NAME" was still running.\n");
                return 0;
        }
       
        i = fork();
        if(i < 0)
        {
                // fork error
                return 0;
        }
        if(i > 0)
        {
                // Parent exits, child (daemon) continues
                return 0;
        }
       
        setsid(); // obtain a new process group
       
        fclose(stdin);
        fclose(stdout);
        fclose(stderr);
       
        lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0644);
        if(lfp<0)
        {
                // Could not open lock file.
                Inst_Log("Open lock file failed.\n");
                return 0;
        }
       
        if(lockf(lfp, F_TLOCK, 0) < 0)
        {
                // can not lock
                Inst_Log("Lock lock file failed.\n");
                return 0;
        }
       
        // first instance continues
        sprintf(str,"%d\n",getpid());
        write(lfp,str,strlen(str)); // record pid to lockfile
       
        signal(SIGCHLD,SIG_IGN); // ignore child
        signal(SIGTSTP,SIG_IGN); // ignore tty signals
        signal(SIGTTOU,SIG_IGN);
        signal(SIGTTIN,SIG_IGN);
        signal(SIGPIPE,SIG_IGN);
        signal(SIGHUP,_signal_handler); // catch hangup signal
        signal(SIGTERM,_signal_handler); // catch kill signal
       
        return 1;
}

//==============================================================================
//Func: Inst_Log
//Desc: Print log to a specified file.
//------------------------------------------------------------------------------
void Inst_Log(char *format, ...)
{
        FILE *logfile = NULL;
        va_list ap;
       
        logfile=fopen(LOG_FILE,"a");
        if(logfile)
        {
                va_start(ap, format);
                vfprintf(logfile, format, ap);
                va_end(ap);
                fclose(logfile);
        }
}

//==============================================================================
//Func: _signal_handler
//Desc: 处理信号
//------------------------------------------------------------------------------
static void _signal_handler(int sig)
{
        switch(sig)
        {
        case SIGHUP:
                break;
        case SIGTERM:
                Inst_Log("Terminate signal.\n");
                g_Quit = 1;
                break;
        }
}参考资料:
技术上来说,呆萌、服务、进程都有些什么区别?http://askubuntu.com/questions/192058/what-is-technical-difference-between-daemon-service-and-process
为什么呆萌要运行setsid?http://unix.stackexchange.com/questions/240646/why-we-use-setsid-while-daemonizing-a-process


页: [1]
查看完整版本: 【C】教你如何在CentOS下写一个“呆萌”(守护进程Daemon)