在Linux或者Unix操做系統中在系統引導的時候會開啓不少服務,這些服務就叫作守護進程。linux
守護進程,也就是一般說的Daemon進程,是Linux中的後臺服務進程。sql
它是一個生存期較長的進程,一般獨立於控制終端而且週期性地執行某種任務或等待處理某些發生的事件。shell
守護進程經常在系統引導裝入時啓動,在系統關閉時終止。數據庫
Linux系統有不少守護進程,大多數服務都是經過守護進程實現的,如Internet服務器inetd,Web服務器httpd等;緩存
同時,守護進程還能完成許多系統任務,例如,做業規劃進程crond、打印進程lqd等(這裏的結尾字母d就是Daemon的意思)。服務器
因爲在Linux中,每個系統與用戶進行交流的界面稱爲終端,每個今後終端開始運行的進程都會依附於這個終端,這個終端就稱爲這些進程的控制終端。網絡
當控制終端被關閉時,相應的進程都會自動關閉。可是守護進程卻可以突破這種限制,它從被執行開始運轉,直到整個系統關閉時才退出。session
若是想讓某個進程不由於用戶或終端或其餘地變化而受到影響,那麼就必須把這個進程變成一個守護進程。socket
守護進程也可能從某個終端由用戶在shell提示符下鍵入命令行啓動,這樣的守護進程必須親自脫離與控制終端的關聯,ide
從而避免與做業控制、終端會話管理、終端產生信號等發生任何不指望的交互,也可避免在後臺運行的守護進程非預期地輸出到終端。
Linux守護進程列表:
#include <syslog.h>
void syslog(int priority, const char *message, ...); // 記錄至系統日誌
第一個參數priority是級別(level)和設施(facility)二者的組合;
第二個參數message相似printf的格式串。
日誌消息的level:
日誌消息的facility:
例如rename函數調用意外失敗時,守護進程能夠執行如下調用:
syslog(LOG_INFO | LOG_LOCAL2, "rename(%s, %s): %m", file1, file2);
level和facility的目的在於,容許在/etc/syslog.conf文件中統一配置來自同一個給定設施的全部消息,
或者統一配置具備相同級別的全部消息。舉例來講,該配置文件可能含有如下兩行:
kern.* /dev/console
local7.debug /var/log/cisco.log
這兩行指定全部內核消息登記到控制檯,來自local7設施的全部debug消息添加到文件/var/log/cisco.log的末尾。
當syslog被應用進程首次調用時,它建立一個Unix域數據報套接字,而後調用connect鏈接到由syslogd守護進程建立的
Unix域數據報套接字的衆所周知路徑名(如/var/run/log)。這個套接字一直保持打開,指導進程終止爲止。
做爲替換,進程也能夠調用openlog和closelog。
#include <syslog.h>
void openlog(const char *ident, int options, int facility); // 可在首次調用syslog前調用
void closelog(void); // 可在應用進程再也不須要發送日誌消息時調用
參數ident是一個由syslog冠於每一個日誌消息前的字符串,其值一般爲程序名。
參數options由一個或多個常值的邏輯或構成。
openlog被調用時,一般並不當即建立Unix域套接字;相反,該套接字直到首次調用syslog時纔打開。
LOG_NDELAY選項迫使該套接字在openlog被調用時就建立。
日誌消息也能夠由logger命令產生,可用在shell腳本中以向syslogd發送消息。
Linux建立守護進程的步驟:
1)建立子進程,父進程退出
2)在子進程中建立新會話
3)改變當前目錄爲根目錄
4)重設文件權限掩碼
5)關閉文件描述符
6)忽略SIGCHLD信號
7)用日誌系統記錄出錯信息
8)守護進程退出處理
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<fcntl.h> #include<sys/types.h> #include<unistd.h> #include<sys/wait.h> #define MAXFILE 65535 void sigterm_handler(int arg); volatile sig_atomic_t _running = 1; int main() { pid_t pc, pid; int i, fd, len, flag = 1; char *buf = "this is a Dameon\n"; len = strlen(buf); pc = fork(); //第一步 if(pc < 0){ printf("error fork\n"); exit(1); } else if(pc > 0) exit(0); pid = setsid(); //第二步 if (pid < 0) perror("setsid error"); chdir("/"); //第三步 umask(0); //第四步 for(i = 0; i < MAXFILE; i++) //第五步 close(i); signal(SIGTERM, sigterm_handler); while(_running){ if( flag == 1 && (fd=open("/tmp/daemon.log", O_CREAT | O_WRONLY | O_APPEND, 0600)) < 0){ perror("open"); flag=0; exit(1); } write(fd, buf, len); close(fd); usleep(10 * 1000); //10毫秒 } } void sigterm_handler(int arg) { _running = 0; }
#include "unp.h" #include <syslog.h> #define MAXFD 64 extern int daemon_proc; int daemon_init(const char *pname, int facility) { int i; pid_t pid; if ( (pid = fork()) < 0) return -1; else if (pid) exit(0); if (setsid() < 0) return -1; signal(SIGHUP, SIG_IGN); if ( (pid = fork()) < 0) return -1; else if (pid) exit(0); daemon_proc = 1; // 把全局變量daemon_proc置爲非0值,告知它們改成調用syslog,以取代fprintf到標準錯誤輸出。 chdir("/"); // 把工做目錄改到根目錄,守護進程多是在某個任意的文件系統中啓動,若仍在其中,該文件系統就沒法拆卸,除非採用潛在破壞性的強制措施。 for (i = 0; i < MAXFD; i++) close(i); // 關閉本守護進程從執行它的進程(一般是一個shell)繼承來的全部打開的描述符 open("/dev/null", O_RDONLY); // 將stdin、stdout和stderr重定向到/dev/null open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); openlog(pname, LOG_PID, facility); // 使用syslogd處理錯誤,第一個參數來自調用者,一般爲程序名字(argv[0])。
// 第二個參數指定把進程ID加到每一個日誌消息中。
// 第三個參數由調用者指定。 return 0; }
首次調用fork終止父進程。本進程是從前臺做爲一個shell命令啓動的,當父進程終止時shell認爲該命令已執行完畢。這樣子進程就自動在後臺運行。
另外,子進程繼承了父進程的進程組ID,不過它有本身的進程ID,這就保證子進程不是一個進程組的頭進程,則該接着調用setsid。
setsid用於建立一個新的會話(session)。當前進程變爲新會話的會話頭進程以及新進程組的進程組頭進程,從而再也不有控制終端。
忽略SIGHUP信號並再次調用fork。再次調用fork目的是確保本守護進程未來即便打開一個終端設備,也不會自動得到控制終端。(確保新的子進程再也不是一個會話頭進程)
這裏必須忽略SIGHUP信號,由於當會話頭進程(即首次fork產生的子進程)終止時,其會話中的全部進程(即再次fork產生的子進程)都收到SIGHUP信號。
int main(int argc, char **argv) { int listenfd, connfd; socklen_t addrlen, len; struct sockaddr_in servaddr; char buff[MAXLINE]; pid_t pid; time_t ticks; daemon_init(argv[0], 0); if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ cout<<"Error No:"<<errno<<endl; cout<<"socket error!"<<endl; exit(0); } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(30000); if (bind(listenfd, (SA*)&servaddr, sizeof(servaddr)) < 0){ cout<<"bind error!"<<endl; exit(0); } if (listen(listenfd, LISTENQ) < 0){ cout<<"listen error!"<<endl; exit(0); } for ( ; ; ){ connfd = accept(listenfd, (SA*)NULL, NULL); if ( (pid = fork()) == 0){ close(listenfd); ticks = time(NULL); bzero(buff, sizeof(buff)); sprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); write(connfd, buff, strlen(buff)); close(connfd); //str_echo(connfd); exit(0); } close(connfd); } return 0; }