摘要:
在之前的《服務器編程入門(5)linux服務器程序規范》中,我們提到過將服務器程序后臺化,這就是本節將要討論的守護進程.
本節主要關注一下問題:
1 什么是守護進程?
2 守護進程的啟動方法有哪些?
3 如何創建一個守護進程?
在后臺運行,且不與任何控制終端關聯的進程。
守護進程不與作業控制、終端會話管理、終端產生信號等發生交互,也可以避免在后臺運行的守護進程非預期地輸出到終端。
兩個特點:
在理解更多關于守護進程的概念之前,我們先了解一下進程、進程組、會話期和控制終端的關系。
守護進程與普通進程的區別如下圖所示:
首先我們使用庫函數daemon創建守護進程,然后研究一下守護進程的創建過程,并實現一個守護進程化函數,達到和庫函數daemon相同的效果。
函數:daemon
聲明:
#include <unistd.h>int daemon(int nochdir, int noclose);
作用:通過在服務器程序中調用它,可以把一個普通進程轉變為守護進程。
參數說明:
If nochdir is zero, daemon() changes the PRocess’s current working directory to the root directory ("/"); otherwise,
If noclose is zero, daemon() redirects standard input, standard output and standard error to /dev/null; otherwise, no changes are made to these file descriptors.
Demo:
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <time.h>#include <fcntl.h>#include <string.h>#include <error.h>int main(int argc, char* argv[]) { time_t t; int fd; // 將當前進程變成守護進程 if (daemon(0, 0) == -1) { perror("daomon error"); exit(EXIT_FAILURE); } while(1) { //這時工作目錄已經被daemon函數切換到了系統根目錄下 fd = open("daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0644); if (fd == -1) { perror("open daemon.log error"); exit(EXIT_FAILURE); } t = time(0); char *buf = asctime(localtime(&t)); write(fd, buf, strlen(buf)); //向daemon.log文件中寫入當前時間 close(fd); sleep(60); // 每隔60s寫入一次 }}
運行截圖:
執行ps命令發現,并沒有名為testDaemon的守護進程,主要原因是daemon函數會將當前工作目錄切換到/目錄下,而普通用戶沒有權限在系統根目錄創建文件。
所以實際上出錯在fd = open("daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0644); 這里,又因為守護進程是不和當前終端交互的,所以沒有看到報錯信息。
現在我們切換到root用戶執行程序,運行截圖:
守護進程創建過程:
代碼實現:
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <time.h>#include <fcntl.h>#include <string.h>#include <error.h>#include <signal.h>#define MAXFD 64extern int daemon_proc;int myDaemon(int ,int );int main(int argc, char* argv[]) { time_t t; int fd; if (myDaemon(0, 0) == -1) { perror("daomon error"); exit(EXIT_FAILURE); } while(1) { fd = open("daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0644); if (fd == -1) { perror("open daemon.log error"); exit(EXIT_FAILURE); } t = time(0); char *buf = asctime(localtime(&t)); write(fd, buf, strlen(buf)); close(fd); sleep(60); } fprintf(stderr, "Hello world!/n");}int myDaemon(int nochdir, int noclose) { int i; pid_t pid; if ( (pid = fork()) < 0 ) return -1; else if (pid) { /* parent terminated */ _exit(0); } /* child 1 continues... */ if (setsid() < 0) /* become session leader */ return -1; signal(SIGHUP, SIG_IGN); /* ignore SIGHUP singal */ if ( (pid = fork()) < 0 ) return -1; else if (pid) { _exit(0); /* child 1 terminated */ } /* child 2 continues... */ daemon_proc = 1; /* use syslog instead of fprintf to stderr */ if (nochdir == 0) chdir("/"); /* change working directory */ if (noclose == 0) { /*close off file descriptors*/ for (i = 0; i < MAXFD; i++) close(i); /* redirect stdin, stdout, and stderr to /dev/null */ open("/dev/null", O_RDONLY); open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); } umask(0);}
運行截圖:
參考資料:
《UNIX網絡編程 卷1:套接字聯網API(第3版)》
linux系統編程之進程(八):守護進程詳解及創建,daemon()使用Linux內核中的進程組及會話新聞熱點
疑難解答