1. 程式人生 > >Linux學習——守護程序(daemon)

Linux學習——守護程序(daemon)

建立守護程序的步驟:

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;

}