博客文章

Linux守护进程

作者: andy.      时间: 2016-08-20 19:31:04

本来每次写的时候都想认认真真写一篇文章的,结果搜索一下,写得仔细得,写得好得比比皆是。就只能写点儿自己的心得了。结果写着写着发现别人也写了。感觉好蠢~Linxu守护进程,具体可以参考一下这个:守护进程

我就总结一下好了。所谓守护进程,比如Web服务器,邮件服务器这种(概念太长,看着心累啊。)。创建守护进程需要的步骤:

1、父进程fork后,执行exit退出。(创立孤儿进程)

2、在子进程中调用setsid。(让进程摆脱原会话的控制、让进程摆脱原进程组的控制、让进程摆脱原控制终端的控制)

3、让更目录“/”成为子进程的工作目录。(防止占用文件,导致卸载U盘这种卸载不掉)

4、把子进程的umask变为0。(权限问题)

5、关闭不需要的文件描述符。(守护进程一般输出都是写日志,不向终端输出的)

6、Linux下的守护进程默认文件名以d结尾。

因为守护进程就只有一个进程,所以通过shell脚本启动,控制其只有一个进程非常的方便。所以写个启动和停止脚本呢。

启动:

WHOAMI=`whoami`
PID=`ps -u $WHOAMI | grep xoxod | awk '{printf $1}'`
if [ "$PID" == "" ]; then
        ./xoxod
fi

停止:

WHOAMI=`whoami`
PID=`ps -u $WHOAMI | grep xoxod | awk '{printf $1}'`
if [ "$PID" != "" ]; then
        kill -s 10 $PID
fi

关于停止有个-s,主要是停止的时候我们并不想那么暴力的停止该进程。所以就发送一个信号给进程,进程自然停止。

接下来直接看代码吧。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>

void Proccess_Signal(int sign){
        char tty[256];
        memset(tty, 0, 256);
        int length;
        switch (sign){
                case SIGUSR1:
                        exit(0);
                        break;
                case SIGUSR2:
                        mkfifo("pipe", 0666);
                        int fd = open("pipe", O_RDONLY);
                        length = read(fd, tty, 256);
                        close(fd);
                        if(length > 0){

                                close(STDOUT_FILENO);

                                if(tty[length - 1] == '\n'){
                                        tty[length - 1] = 0;
                                }

                                fd = open(tty, O_WRONLY);
                        }
                        break;
        }
}

int MySignal(int signal, void (*func)(int)){
        struct sigaction act, oact;

        act.sa_handler = func;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;

        return sigaction(signal, &act,&oact);
}

void SetDaemon(){
        setsid();//2、在子进程中调用setsid。
        chdir("/");//3、让更目录“/”成为子进程的工作目录。
        umask(0);//4、把子进程的umask变为0。

        MySignal(SIGUSR1, Proccess_Signal);
        MySignal(SIGUSR2, Proccess_Signal);
}

int main(){
        pid_t pid = fork();
        if(pid < 0 || pid > 0){
                exit(-1);//1、父进程fork后,执行exit退出。
        }
        if(pid == 0){
                SetDaemon();
                while(1){
                        printf("1\n");
                        sleep(1);
                }
        }
}

大家有没有发现没有第五步,关闭不必要的文件描述符。很简单哈,有时候我们需要调试程序的时候,还是需要再终端输出东西的。所以大家看18~33行:

case SIGUSR2:
        mkfifo("pipe", 0666);
        int fd = open("pipe", O_RDONLY);
        length = read(fd, tty, 256);
        close(fd);
        if(length > 0){

                 close(STDOUT_FILENO);

                 if(tty[length - 1] == '\n'){
                          tty[length - 1] = 0;
                 }

                 fd = open(tty, O_WRONLY);
        }
        break;

收到SIGUSR2信号后,就从管道中读取终端的路径,关闭标准输出,打开该终端。那么按照从小到大的排列,新打开的终端占用标准输出。那么printf输出的东西就在从管道读取的tty中了。我们看看运行结果:

blob.png

正在输出,然后我们关闭终端,再重新打开就肯定没有输出了。如何把输出结果来呢?按照上卖弄的代码执行下面的操作:

blob.png

达到我们预期的结果,这也是一个小技巧吧。最后,通过脚本关闭我们的守护进程:

blob.png

好了,守护进程就讲到这儿。