Linux学习——守护进程(daemon)

时间:2022-07-23 15:34:33

创建守护进程的步骤:

1)父进程中执行fork后,执行exit退出;

2)在子进程中调用setsid;(脱离控制台)

3)让根目录“/”成为子进程的工作目录;

4)把子进程的umask变为0;

5)关闭任何不需要的文件描述符。

其中,setsid函数创建一个新会话和一个新进程组,然后守护进程成为新会话的会话领导,以及新进程组的进程组领导。

chdir函数,根据参数pathname设置当前工作。

umask函数,umask调用把守护进程的umask设置为0,这样取消了来着父进程的umask,它们能潜在的干扰创建文件和目录。(指定权限)

 

一旦系统调用setsid,它不再有控制终端。

可以通过syslog提供服务,记录守护进程的各种输出信息。

openlog函数打开日志,syslog写入日志,closelog关闭日志。

 

例子如下:

int main(int arg,char *args[])

{

    pid_t pid = fork();

    if(pid == -1)

    {

       return;

    }

    if(pid > 0)

    {

       exit(0);

    }

    if(pid == 0)

    {

       setsid();

    /*  chdir("/");//防止程序干扰其它比如拔插U盘的工作

       umask(0);

       close(STDIN_FILENO);

        close(STDOUT_FILENO);

       close(STDERR_FILENO); */

    }

    syslog(LOG_INFO,"mydaemon is OK");

    while(1)

    {

       sleep(1);

    }

    return EXIT_SUCCESS;

}

上面注释掉的程序不是必须要的。

 

一般守护进程的makefile文件中:可执行文件的名字最后是字母d。

 

和一个守护进程通信,要向它发送信号。

int status = 0;

 

void catch_Signal(int Sign)

{

    switch(Sign)

    {

    case SIGINT:

       printf("SIGINTSignal\n");

       exit(0);//SIGINT来了后,进程退出

    case SIGALRM:

       printf("SIGALRMSignal\n");

       alarm(5);

       break;

    case SIGUSR1:

       status = 1;

       break;

    }

}

 

int mysignal(int signo,void (*func)(int))

{

    struct sigaction act, oact;

    act.sa_handler = func;

    sigemptyset(&act.sa_mask);

    act.sa_flags = 0;

    return sigaction(signo, &act, &oact);

}

 

void setdaemon()

{

    pid_t pid = fork();

    if(pid == -1)

    {

       return;

    }

    if(pid > 0)

    {

       exit(0);

    }

    if(pid == 0)

    {

       setsid();

    /*  chdir("/");//防止程序干扰其它比如拔插U盘的工作

       umask(0);

       close(STDIN_FILENO);

        close(STDOUT_FILENO);

       close(STDERR_FILENO); */

    }

    return;

}

 

int main(int arg,char *args[])

{

    setdaemon();

    mysignal(SIGINT, catch_Signal); //SIGINT:控制守护进程退出

    mysignal(SIGALRM, catch_Signal);

    mysignal(SIGUSR1, catch_Signal);

 

    int i = 0;

    while(1)

    {

       if(status == 1)

           printf("hello:%d\n", i++);

       sleep(1);

    }

    return EXIT_SUCCESS;

}

 

通过shell脚本启动守护进程:(执行多次,只启动一个守护进程)

#!/bin/sh

 

WHOAMI=`whoami`

 

PID=`ps -u $WHOAMI | grep signd |awk '{print $1}'`

 

if (test "$PID" ="") then

        signd

fi

 

通过shell脚本结束守护进程:(执行一次,将所有已存在的守护进程杀死)

#!/bin/sh

 

WHOAMI=`whoami`

 

PID=`ps -u $WHOAMI | grep signd |awk '{print $1}'`

if(test"$PID"!="") then

        kill -s 2 $PID

fi

 

使用FIFO与守护进程通信的例子如下:

读FIFO

void readfifo()
{
int len = 0;
char buf[1024];
memset(buf,0,sizeof(buf));
int fd = open("/home/zangyongcan/1/fifo1", O_RDONLY);
if(fd == -1)
{
printf("open file error %s\n",strerror(errno));
return;
}
while((len = read(fd,buf,sizeof(buf)) > 0)) //循环读取管道文件内容,直到管道文件被关闭,循环break
{
printf("%s\n", buf);
memset(buf, 0, sizeof(buf));
}
close(fd);
return;
}


void setdaemon() //把程序设置为daemon状态
{
pid_t pid = fork();
if(pid == -1)
{
printf("fork error!\n");
exit(0);
}
if(pid == 0)
{
setsid();
chdir("/");
umask(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
if(pid > 0)
{
exit(0);
}
}


int main(void)
{
setdaemon();
readfifo();
puts("!!!Hello World!!!"); /* prints !!!Hello World!!! */
return EXIT_SUCCESS;
}


写FIFO

void writefifo() //写fifo1管道文件内容
{
char buf[1024];
memset(buf,0,sizeof(buf));
int fd = open("/home/zangyongcan/1/fifo1", O_WRONLY);
if(fd == -1)
{
printf("write fifo1 error %s\n",strerror(errno));
return;
}
while(1)
{
memset(buf,0,sizeof(buf));
scanf("%s", buf);
write(fd, buf, sizeof(buf));
}
close(fd);
return;
}

int main(void)
{
writefifo();
return EXIT_SUCCESS;
}


如何让后台运行的daemon程序向屏幕打印信息。

daemon.c

1、执行mkfifo fifo1命令,创建一个名为fifo1的管道文件;

2、编译daemon.c,执行程序,关闭当前控制台终端端口,让程序在后台运行;

3、打开一个新的终端窗口,通过ps aux找到进程PID

4、执行

     kill –s 2 PID

     tty > fifo1

 

附:daemon.c内容如下

int mysignal(int signo,void (*func)(int))

{

    struct sigaction act, oact;

    act.sa_handler = func;

    sigemptyset(&act.sa_mask);

    act.sa_flags = 0;

    return sigaction(signo, &act, &oact);

}

 

void setdaemon()//把程序设置为daemon状态

{

    pid_t pid, sid;

    pid = fork();

    if(pid < 0)

    {

       printf("fork error%s\n",strerror(errno));

       exit(EXIT_FAILURE);

    }

    if(pid > 0)

    {

       exit(EXIT_SUCCESS);

    }

    if((sid = setsid()) < 0)

    {

       printf("setsidfailed %s\n",strerror(errno));

       exit(EXIT_FAILURE);

    }

}

 

void listenfifo()

{

    const char *sfifoname = "fifo1";

    int len = 0;

    char buf[128];

    memset(buf, 0,sizeof(buf));

    int fd = open(sfifoname,O_RDONLY);//打开fifo1管道文件

    if (fd == -1)

    {

       printf("open %sfailed, %s\n", sfifoname,strerror(errno));

    }

    len = read(fd, buf, sizeof(buf));

    if(len > 0)

    {

       if(buf[strlen(buf)-1] =='\n')//如果读到的字符串最后一位是\n,\n变为0

       {

           buf[strlen(buf)-1] = 0;//进程阻塞,直到有数据来了才返回

       }

       close(STDOUT_FILENO);//关闭标准输出

       open(buf, O_WRONLY);

    }

    close(fd);

}

 

void catch_Signal(int Sign)

{

    switch(Sign)

    {

    case SIGINT:

       listenfifo();

       break;

    case SIGPIPE:

       break;

    }

}

 

int main(void)

{

    setdaemon();//把进程设置为daemon状态

    mysignal(SIGINT, catch_Signal);//捕捉SIGINT信号

    mysignal(SIGPIPE, catch_Signal);//捕捉SIGPIPE信号

    while(1)

    {

       puts("!!!HelloWorld!!!");/* prints!!!Hello World!!! */

       sleep(1);

    }

    return EXIT_SUCCESS;

}