守護進程最重要的特性是後台運行。在這一點上DOS下的常駐記憶體程式TSR與之相似。其次,守護進程必須與其運行前的環境隔離開來。這些環境包括未關閉的檔案描述符,控制終端,會話和進程組,工作目錄以及檔案建立掩模等。這些環境通常是守護進程從執行它的父進程(特別是shell)中繼承下來的。最後,守護進程的啟動方式有其特殊之處。它可以在Linux系統啟動時從啟動指令碼/etc/rc.d中啟動,可以由作業規划進程crond啟動,還可以由使用者終端(通常是shell)執行。
總之,除開這些特殊性以外,守護進程與普通進程基本上沒有什麼區別。因此,編寫守護進程實際上是把一個普通進程按照上述的守護進程的特性改造成為守護進程.
分四步:
1) 建立子進程,脫離父進程,
為避免掛起控制終端將Daemon放入後台執行。方法是在進程中調用fork使父進程終止,讓Daemon在子進程中後台執行。
2) 脫離控制終端,登入工作階段和進程組 :
進程屬於一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登入工作階段可以包含多個進程組。這些進程組共用一個控制終端。這個控制終端通常是建立進程的登入終端。
控制終端,登入工作階段和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎上,調用setsid()使進程成為交談群組長:
3) 禁止進程重新開啟控制終端 :
現在,進程已經成為無終端的交談群組長。但它可以重新申請開啟一個控制終端。可以通過使進程不再成為交談群組長來禁止進程重新開啟控制終端:
4)關閉開啟的檔案描述符:
進程從建立它的父進程那裡繼承了開啟的檔案描述符。如不關閉,將會浪費系統資源,造成進程所在的檔案系統無法卸下以及引起無法預料的錯誤
static void deamonize()
{
int fd;
int pid;
if(pid = fork()) {
exit(0); /* parent process, exit it */
} else if(pid < 0) {
spd_log(LOG_ERROR, "error when fork\n");
exit(0); /* error */
} else {
if(setsid() < 0) {
spd_log(LOG_ERROR, "error when change session id");
exit(0);
}
}
/* child proc, for now , we are session leader and process group leader */
if(pid = fork()) {
exit(0); /* kill child */
} else if(pid < 0) {
spd_log(LOG_ERROR, "error when fork\n");
exit(0);
}
/* child child proc, we are no longer the session leader and process group leader */
/* ignore fd leak */
fd = open("/dev/null", O_RDONLY);
if(fd != 0) {
dup2(fd, 0);
close(fd);
}
fd = open("/dev/null", O_WRONLY);
if(fd != 1) {
dup2(fd, 1);
close(fd);
}
fd = open("/dev/null", O_WRONLY);
if(fd != 2) {
dup2(fd, 2);
close(fd);
}
}