調用成功後,程序成為新的會話組長和新的程序組長,并與原來的登入會話和程序組脫離。由于會話過程對控制終端的獨占性,程序同時與控制終端脫離。
4.禁止程序重新打開控制終端。現在,程序已經成為無終端的會話組長,并且該會話沒有控制終端,但會話組長可以重新申請打開一個控制終端。是以要讓程序不是會話組長,來禁止程序重新打開控制終端。代碼:
if( (pid = fork()) > 0)
exit(0); //之前的子程序結束,子子程序繼續運作
此時的程序組id、會話id是之前子程序的pid,現在程式運作的是子子程序。雖然剛才的會話組長結束了,但是它pid還在,因為程序組、會話的生命期結束消失是在這個組、會話中所有程序都結束時才發生的。
5.關閉打開的檔案描述符。程序從建立它的父程序那裡繼承了打開的檔案描述符。如不關閉,将會浪費系統資源,造成程序所在的檔案系統無法卸下以及引起無法預料的錯誤。代碼:
#define MAX_FILENO 1024
for(i = 0; i < MAX_FILENO; ++i)
close(i);
程式能打開的檔案最大個數,在頭檔案<linux/limits.h>中 NR_OPEN 指定,檢視并使用此值。
6.改變目前工作目錄。程序活動時,其工作目錄所在的檔案系統不能卸下。一般需要将工作目錄改變到根目錄。寫運作日志的程序将工作目錄改變到特定目錄如/tmp。代碼:
chdir("/");
7.重設檔案建立掩碼。程序從建立它的父程序那裡繼承了檔案建立掩模。它可能修改守護程序所建立的檔案的存取位。為防止這一點,将檔案建立掩模清除。代碼:
umask(0);
8.處理SIGCHLD信号。對于某些程序,特别是伺服器程序往往在請求到來時生成子程序處理請求。如果父程序不等待子程序結束,子程序将成為僵屍程序(zombie)進而占用系統資源。如果父程序等待子程序結束,将增加父程序的負擔,影響伺服器程序的并發性能。在Linux下可以簡單地将 SIGCHLD信号的操作設為SIG_IGN來解決問題。代碼:
signal(SIGCHLD, SIG_IGN);
上面說了這麼多都是守護程序的編寫要點,值得一提的是系統已經給我們提供了API讓程序直接成為守護程序:
#include <unistd.h>
int daemon(int nochdir, int noclose); //使程序成為守護程序
//0,則改變工作目錄到“/”; 0,則重定向stdin、stdout、stderr到“/dev/null”
此外,守護程序可以與日志守護程序建立聯系,将日志資訊寫入日志檔案。用到的函數:
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
//打開目前程式與日志守護程序之間的聯系。
ident,是一個标記,ident所表示的字元串将固定地加在每行日志的前面以辨別這個日志,通常就寫成目前程式的名稱以作标記。
option,一般是下列選項值取“與”運算(使用“|”表示,如“LOG_CONS | LOG_PID”)的結果:
LOG_CONS:如果送到system logger時發生問題,直接寫入系統終端;
LOG_NDELAY:立即開啟連接配接,通常連接配接是在第一次寫入消息時才打開的;
LOG_NOWAIT: Don't wait for child processes that may have been created while logging the message.
(The GNU C library does not create a child process, so this option has no effect on Linux.)
LOG_ODELAY:The converse of LOG_NDELAY; opening of the connection is delayed until syslog() is called.
(This is the default,and need not be specified.)
LOG_PERROR:将消息也同時送到stderr裝置;
LOG_PID:将程序PID含入所有消息中。
facility,指明記錄日志的程式的類型,它主要具有如下幾類日志類型:
LOG_AUTH :安全/授權消息
LOG_AUTHPRIV:安全/授權消息(私有)
LOG_CRON:時間守護程序(cron和at)專用
LOG_DAEMON:其它系統守護程序
LOG_KERN:核心消息
LOG_LOCAL0到LOG_LOCAL7:系統保留
LOG_LPR:printer子系統
LOG_MAIL:mail子系統
LOG_NEWS:USENET新聞子系統
LOG_SYSLOG:syslogd程序内部所産生的消息
LOG_USER(預設):一般使用者預設使用消息
LOG_UUCP:UUCP子系統
LOG_FTP:FTP子系統使用
void syslog(int priority, const char *format, ...);
//産生一條日志資訊,然後由日志守護程式将其發送到各日志檔案。
level priority,緊急級别:
LOG_EMERG:緊急狀況
LOG_ALERT:高優先級問題,比如說資料庫崩潰等,必須要立即采取反應行動
LOG_CRIT:重要狀況發生,比如硬體故障
LOG_ERR:錯誤發生
LOG_WARNING:警告發生
LOG_NOTICE:一般狀況,需要引起注意
LOG_INFO:資訊狀況
LOG_DEBUG:調試消息
第二個參數是消息及其格式,之後是格式對應的參數,如同C語言裡面printf輸出函數一樣使用。
在實際使用中,如果我們的程式要使用系統日志功能,隻需要在程式啟動時使用openlog函數來連接配接syslogd程式,後面随時用syslog函數寫日志就行了。
void closelog(void);
//關閉日志。雖然這個函數簡單,但必不可少,因為日志也是資源,隻打開不管可能會造成記憶體不足。
守護程式完整示例:
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_FILENO 1024
int init_daemon()
{
int pid;
int i;
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP ,SIG_IGN);
if( (pid = fork()) > 0)
{
exit(EXIT_SUCCESS);
}
else if(pid< 0)
{
perror("fork");
exit(EXIT_FAILURE);
}
setsid();
if( (pid = fork()) > 0)
{
exit(EXIT_SUCCESS);
}
else if(pid< 0)
{
perror("fork");
exit(EXIT_FAILURE);
}
for(i = 0; i < MAX_FILENO; ++i)
{
close(i);
}
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
chdir("/tmp");
umask(0);
signal(SIGCHLD,SIG_IGN);
return 0;
}
int main(int argc,char *argv[])
{
init_daemon();
openlog("MyLog", LOG_PID, LOG_KERN);
while(1)
{
sleep(1);
syslog(LOG_INFO,"This is my log : %s\n",argv[0]);
}
}
此時,ps -axj,顯示:
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 4613 4612 4612 ? -1 S 1000 0:00 ./main
打開/var/log/messages 檔案: