基本概念
- Linux Daemon(守護(hù)進(jìn)程)是運行在后臺的一種特殊進(jìn)程。它獨立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件崖瞭。它不需要用戶輸入就能運行而且提供某種服務(wù),不是對整個系統(tǒng)就是對某個用戶程序提供服務(wù)胚吁。Linux系統(tǒng)的大多數(shù)服務(wù)器就是通過守護(hù)進(jìn)程實現(xiàn)的牙躺。常見的守護(hù)進(jìn)程包括系統(tǒng)日志進(jìn)程syslogd、 web服務(wù)器httpd腕扶、郵件服務(wù)器sendmail和數(shù)據(jù)庫服務(wù)器mysqld等孽拷。
- 守護(hù)進(jìn)程一般在系統(tǒng)啟動時開始運行,除非強行終止半抱,否則直到系統(tǒng)關(guān)機都保持運行脓恕。守護(hù)進(jìn)程經(jīng)常以超級用戶(root)權(quán)限運行,因為它們要使用特殊的端口(1-1024)或訪問某些特殊的資源窿侈。
- 一個守護(hù)進(jìn)程的父進(jìn)程是init進(jìn)程炼幔,因為它真正的父進(jìn)程在fork出子進(jìn)程后就先于子進(jìn)程exit退出了,所以它是一個由init繼承的孤兒進(jìn)程史简。守護(hù)進(jìn)程是非交互式程序乃秀,沒有控制終端,所以任何輸出圆兵,無論是向標(biāo)準(zhǔn)輸出設(shè)備stdout還是標(biāo)準(zhǔn)出錯設(shè)備stderr的輸出都需要特殊處理环形。
- 守護(hù)進(jìn)程的名稱通常以d結(jié)尾,比如sshd衙傀、xinetd抬吟、crond等
編程規(guī)則
- 在父進(jìn)程中執(zhí)行fork并exit推出-->確保不是進(jìn)程組組長進(jìn)程,進(jìn)程組組長统抬,就無法調(diào)用setsid火本;
- 在子進(jìn)程中調(diào)用setsid函數(shù)創(chuàng)建新的會話-->沒有控制終端;
- 在子進(jìn)程中調(diào)用chdir函數(shù)聪建,讓根目錄 ”/” 成為子進(jìn)程的工作目錄-->若掛載在文件系統(tǒng)中钙畔,該文件系統(tǒng)不能卸載;
- 在子進(jìn)程中調(diào)用umask函數(shù)金麸,設(shè)置進(jìn)程的umask為0擎析;
- 在子進(jìn)程中關(guān)閉任何不需要的文件描述符-->不需要父進(jìn)程的相關(guān)描述符。
為什么需要設(shè)置umask?
每個文件建立時有一個權(quán)限挥下,如755揍魂,umask命令用來設(shè)置限制新建文件權(quán)限的掩碼,表示在原有權(quán)限上刪除相應(yīng)的權(quán)限棚瘟。如文件原來的初始化權(quán)限是777现斋,那么執(zhí)行命令umask 022以后,該文件的權(quán)限將變?yōu)?55:如果該文件原來的初始化權(quán)限是666偎蘸,那么該文件的權(quán)限將變?yōu)?44庄蹋。
因為守護(hù)進(jìn)程一開始是從父進(jìn)程fork來瞬内,繼承來的umask可能會設(shè)置為拒絕一些權(quán)限,當(dāng)守護(hù)進(jìn)程要創(chuàng)建可讀可寫文件時限书,可能會因為權(quán)限不夠而無法發(fā)揮作用虫蝶。
會話
會話是一個或多個進(jìn)程組的集合,進(jìn)程調(diào)用setsid()創(chuàng)建一個新會話倦西,如果該進(jìn)程不是一個進(jìn)程組的組長秉扑,將會發(fā)生以下:
會話的作用主要由以下三個,主要用于守護(hù)進(jìn)程中:
- 使當(dāng)前進(jìn)程脫離原會話的控制
- 使當(dāng)前進(jìn)程脫離原進(jìn)程組的控制
- 使當(dāng)前進(jìn)程脫離原控制終端的控制
總結(jié)說就是非進(jìn)程組組長進(jìn)程創(chuàng)建會話后调限,該進(jìn)程稱為會話首進(jìn)程,并且成為新建進(jìn)程組的組長進(jìn)程误澳,新進(jìn)程組ID為該進(jìn)程ID耻矮,重要一點就是沒有控制終端。
創(chuàng)建會話一個條件就是非組長進(jìn)程忆谓,為了保證裆装,通常先調(diào)用fork,然后使父進(jìn)程終止倡缠,而子進(jìn)程繼續(xù)哨免,子進(jìn)程繼承了進(jìn)程組ID,而進(jìn)程ID又是新分配昙沦,保證了不是進(jìn)程組組長琢唾。
一個會話通常開始于用戶登錄,終止于用戶退出盾饮,期間所有的進(jìn)程都屬于這個會話采桃。一個會話一般包含一個會話首進(jìn)程、一個前臺進(jìn)程組和一個后臺進(jìn)程組丘损,控制終端可有可無普办;此外,前臺進(jìn)程組只有一個徘钥,后臺進(jìn)程組可以有多個衔蹲,這些進(jìn)程組共享一個控制終端
對守護(hù)進(jìn)程進(jìn)行總結(jié),守護(hù)進(jìn)程是脫離終端并在后臺運行的進(jìn)程呈础,需要脫離原控制終端舆驶,原進(jìn)程組,原會話而钞,因此要建立一個新的會話贞远,由于進(jìn)程組組長不能創(chuàng)建會話,需要先fork笨忌,確保進(jìn)程ID不是原會話ID和進(jìn)程組組長ID蓝仲;由于fork保留了父進(jìn)程的相關(guān)信息,如目錄和權(quán)限,由于守護(hù)進(jìn)行要更大的靈活性袱结,需要改變目錄和修改權(quán)限亮隙。同時,需要關(guān)閉原來不需要的文件描述符垢夹。過程:fork-->setsid-->chdir-->umask-->close
作業(yè)控制
Shell分前后臺來控制的不是進(jìn)程而是作業(yè)(Job)或者進(jìn)程組(Process Group)溢吻。一個前臺作業(yè)可以由多個進(jìn)程組成,一個后臺作業(yè)也可以由多個進(jìn)程組成果元,Shell可以同時運行一個前臺作業(yè)和任意多個后臺作業(yè)促王,這稱為作業(yè)控制(Job Control)
$ proc1 | proc2 &
$ proc3 | proc4 | proc5
其中proc1和proc2屬于同一個后臺進(jìn)程組,proc3而晒、proc4蝇狼、proc5屬于同一個前臺進(jìn)程組,Shell進(jìn)程本身屬于一個單獨的進(jìn)程組倡怎。這些進(jìn)程組的控制終端相同迅耘,它們屬于同一個Session,一個Session與一個控制終端相關(guān)监署。當(dāng)用戶在控制終端輸入特殊的控制鍵(例如Ctrl-C)時颤专,內(nèi)核會發(fā)送相應(yīng)的信號(例如SIGINT)給前臺進(jìn)程組的所有進(jìn)程。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// 守護(hù)進(jìn)程初始化函數(shù)
void init_daemon()
{
pid_t pid;
int i = 0;
if ((pid = fork()) == -1) {
printf("Fork error !\n");
exit(1);
}
if (pid != 0) {
exit(0); // 父進(jìn)程退出
}
setsid(); // 子進(jìn)程開啟新會話钠乏,并成為會話首進(jìn)程和組長進(jìn)程
if ((pid = fork()) == -1) {
printf("Fork error !\n");
exit(-1);
}
if (pid != 0) {
exit(0); // 結(jié)束第一子進(jìn)程栖秕,第二子進(jìn)程不再是會話首進(jìn)程
}
chdir("/tmp"); // 改變工作目錄
umask(0); // 重設(shè)文件掩碼
for (; i < getdtablesize(); ++i) {
close(i); // 關(guān)閉打開的文件描述符
}
return;
}
int main(int argc, char *argv[])
{
int fp;
time_t t;
char buf[] = {"This is a daemon: "};
char *datetime;
int len = 0;
// 初始化 Daemon 進(jìn)程
init_daemon();
// 每隔一分鐘記錄運行狀態(tài)
while (1) {
if (-1 == (fp = open("/tmp/daemon.log", O_CREAT|O_WRONLY|O_APPEND, 0600))) {
printf("Open file error !\n");
exit(1);
}
len = strlen(buf);
write(fp, buf, len);
t = time(0);
datetime = asctime(localtime(&t));
len = strlen(datetime);
write(fp, datetime, len);
close(fp);
sleep(60);
}
return 0;
}
出錯記錄
出錯記錄提出來主要是解決守護(hù)進(jìn)程的錯誤消息記錄問題,因為守護(hù)進(jìn)程沒有自己的終端顯示晓避,但若是每個守護(hù)進(jìn)程都維護(hù)一個自己記錄文件就很麻煩累魔,需要有一個集中的守護(hù)進(jìn)程出錯記錄設(shè)施。
在linux中使用syslog進(jìn)行記錄够滑。
后臺進(jìn)程job與守護(hù)進(jìn)程區(qū)別
后臺進(jìn)程一般不需要交互垦写,啟動時加一個 & 就可以,會輸出相應(yīng)的job ID.
對切換到后臺的進(jìn)程彰触,ps可以查看到梯投,也可以通過 jobs命令只查看所有后臺進(jìn)程。
而守護(hù)進(jìn)程永遠(yuǎn)都以后臺方式啟動况毅,ps -a無法查看到分蓖,ps xj輸出中TPGID為-1就是守護(hù)進(jìn)程。