守護進程
什么是守護進程
守護進程是生存期長的一種進程.它們常常在系統(tǒng)引導裝入時啟動,僅在系統(tǒng)關閉時才終止.因為它們沒有
控制終端
,所以說它們是在后臺運行的.unix系統(tǒng)有很多守護進程,它們執(zhí)行日常事務活動. --- << APUE >>
linux下的守護進程
root@codelover:/home/codelover# ps -axj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 1 1 1 ? -1 Ss 0 0:01 /sbin/init splash
0 2 0 0 ? -1 S 0 0:00 [kthreadd](用于創(chuàng)建其他內核進程)
2 4 0 0 ? -1 S< 0 0:00 [kworker/0:0H]
2 6 0 0 ? -1 S< 0 0:00 [mm_percpu_wq]
2 7 0 0 ? -1 S 0 0:00 [ksoftirqd/0]
2 8 0 0 ? -1 S 0 0:01 [rcu_sched]
2 9 0 0 ? -1 S 0 0:00 [rcu_bh]
2 10 0 0 ? -1 S 0 0:00 [migration/0]
2 11 0 0 ? -1 S 0 0:00 [watchdog/0]
2 12 0 0 ? -1 S 0 0:00 [cpuhp/0]
2 13 0 0 ? -1 S 0 0:00 [cpuhp/1]
- 父進程id為0的進程通常是內核進程,作為系統(tǒng)引導裝入過程的一部分而啟動
- kthreadd用于創(chuàng)建其他內核進程,所以可以看到很多內核進程的ppid都是2
如何編寫守護進程
調用
umask
將文件模式創(chuàng)建屏蔽字設置為一個已知值(通常0),主要是設置程序創(chuàng)建文件時的默認權限-
調用
fork
,然后退出父進程,這么做的原因是:- 守護進程需要脫離終端控制,那么需要成為一個新會話,成為新會話時才會切斷和終端的聯(lián)系
- 而創(chuàng)建一個新會話的 前提條件是進程不是一個進程組的組長,因此讓fork的子進程來做這個事,因為fork出來的子進程一定不是組長進程
-
調用
setsid
創(chuàng)建一個新會話,此時:- 此進程是新會話的首進程
- 成為一個新進程組的組長進程
- 沒有終端控制
有人建議在此時,再次調用
fork
,讓子進程成為守護進程,這樣可以保證該守護進程不是會話首進程,防止它取得控制終端.切換工作目錄到根目錄下,因為此時有可能進程的工作目錄在一個掛載的文件系統(tǒng),但是,守護進程一般是在操作系統(tǒng)重新啟動一直存在,不應該在一個掛載的目錄下.當然也可以切換到其他目錄下
關閉不需要的文件描述符,因為此時守護進程可能持有其父進程的打開的描述符號,讓守護進程關閉所有打開的文件描述符.
因為守護進程不需要輸入和輸出交互,因此需要打開
/dev/null
這個"黑洞",讓0,1,2
文件描述符都被重定向-
日志記錄,由于守護進程將標準輸入輸出錯誤都重定向了,無法將信息直觀寫到標準錯誤,并且不希望將信息寫到控制臺設備,也不希望寫到文件,因為對于管理人員,寫入文件需要定期檢查文件,很麻煩.處理方法:
-
log
函數,任何一個用戶進程都可以打開/dev/klog
來讀取 -
syslog
函數(大多數守護進程的選擇),消息被發(fā)送到unix域數據報套接子/dev/log
-
udp 端口 514
,syslog
不產生udp數據,需要用戶顯示進行網絡編程.
-
當需要與守護進程進行交互時,一般通過發(fā)送信號,守護進程通過捕捉信號作出相應響應
Talk is cheap. Show me the code
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <syslog.h>
int main(int argc, char const *argv[]) {
//將文件模式創(chuàng)建屏蔽字設置為0
umask(0);
pid_t pid = fork();
if(pid < 0) {
//創(chuàng)建失敗
return (0);
} else if(pid != 0) {
//結束父進程
return (-1);
}
//設置新的會話id
setsid();
//重新fork,讓守護進程不是不是會話首進程
pid = fork();
if(pid != 0) {
return (-1);
}
//切換到根目錄下
chdir("/");
//關閉所有打開的文件描述符
struct rlimit r;
getrlimit(RLIMIT_NOFILE, &r);
for (size_t i = 0; i < r.rlim_max; i++) {
close(i);
}
//重定向0,1,2
int fd = open("/dev/null", O_RDWR);
dup2(fd, 1);
dup2(fd, 2);
openlog("test.log", LOG_CONS, LOG_DAEMON);
syslog(LOG_ERR, "%s","test error");
while (1) {
sleep(10);
}
return 0;
}