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
{
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;
}