1什么叫做守護進程
守護進程一般在系統引導裝入的時候啟動,在系統關閉的時候關閉,因為他們沒有控制終端,所以說他們是后台運行的,用來執行一些日長的任務
利用ps -axj可以查看,其中a是查看其他的用戶所擁有的進程,x顯示沒有控制終端的進程狀態,-j顯示與作業有關的信息,主要是第一行的信息


這個ps命令要正確 的打印,系統必須支持會話,否則不能正確的顯示,父進程是0的通常是內核進程,內核進程的生命周期存在於系統的整個生命周期中,他們以超級用戶權限運行,沒有控制終端,不能使用命令行
如何判斷是否是守護進程,當TPGID=-1的時候就是守護進程,comman命令加了[]就是系統級的守護進程,沒有加[]就是用戶級的守護進程,要注意,init並不是一個內核級的守護進程,他是用戶級的守護進程的父進程
我們如何判定是否使用內核守護進程:我們使用一個內核組件,卻不被用戶進程的上下文所調用,並且是常駐內存,我們就應該使用內核守護進程
使用守護進程的大體原則:
如果一個進程永遠都是以后台方式啟動,並且不能受到Shell退出影響而退出,一個正統的做法是將其創建為守護進程(daemon)。守護進程值得是系統長期運行的后台進程,類似Windows服務。守護進程信息通過ps –a無法查看到,需要用到–x參數,當使
用這條命令的時候,往往還附上-j參數以查看作業控制信息,其中TPGID一欄為-1就是守護進程。
2對於守護進程的編程規則
#include "apue.h"
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h>
void daemonize(const char *cmd)
{
int i,fd0,fd1,fd2;
pid_t pid;
struct rlimit r1;
struct sigaction sa;
umask(0);
if(getrlimit(RLIMIT_NOFILE,&r1) < 0)
err_quit("%s:can't get file limit",cmd);
if((pid = fork())<0)
err_quit("%s:can't fork",cmd);
else if(pid != 0)
exit(0);
setsid();
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP,&sa,NULL) < 0)
err_quit("%s:can't ignore SIGHUP",cmd);
if((pid = fork()) < 0)
err_quit("%s:can't fork",cmd);
else if(pid != 0)
{
exit(0);
}
if(chdir("/") < 0)
err_quit("%s:can't change directory to /",cmd);
if(r1.rlim_max == RLIM_INFINITY)
r1.rlim_max = 1024;
for(i = 0; i < r1.rlim_max;i++)
close(i);
fd0 = open("/dev/null",O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
openlog(cmd,LOG_CONS,LOG_DAEMON);
if(fd0 != 0|| fd1 != 1||fd2 != 2)
{
syslog(LOG_ERR,"unexpected file descriptors %d %d %d",fd0,fd1,fd2);
exit(0);
}
}
int main()
{
daemonize("my ");
sleep(10);
return 0;
}
將一個進程變成一個守護進程的方法
(1)首先應該改變的是文件模式屏蔽字,因為在原本的進程上下文中,文件屏蔽字是不一定符合規范的,而且守護進程一般需要創建一個具有特定要求的屏蔽字,所以應該自己設置一個合理的屏蔽字,如果有更加嚴格的要求一般將文件模式設置為007
(2)調用fork,進行創建一個新的子進程,為什么要這么做第一,如果在shell中這樣啟動一個程序,當父進程exit的時候,會讓shell認為此程序已經執行完畢第二,這樣保證了這個進程不是一個子進程,這是使用setsid的先決條件
(3)調用setsid來進行設置一個新的會話,在創建會話的時候為了保證不分配一個控制終端,通常會在fork一次,這樣就一定這個進程不是控制終端進程(其實在第一次fork的時候,並且使用了setsid的時候就確定了一定不是控制終端)這樣做其實是為了更加確定(可以這樣理解)
(4)更改當前的工作目錄成為根目錄,因為如果不更改為根目錄,本來工作的目錄其實就要一直被掛載,這在某些系統中,顯然是這樣做到,因為有可能這個操作系統會被刪除
(5)進行所有的文件描述符的關閉
(6)某些守護進程打開/dev/null等,使其映射到0,1,2
關於程序的運行的結果顯示
3守護進程出錯記錄
首先我們進行syslog的介紹
syslog是一種工業標准的協議,可用來記錄設備的日志。在UNIX系統,路由器、交換機等網絡設備中,系統日志(System Log)記錄系統中任何時間發生的大小事件。管理者可以通過查看系統記錄,隨時掌握系統狀況。UNIX的系統日志是通過syslogd這個進程記錄系統有關事件記錄,也可以記錄應用程序運作事件。通過適當的配置,我們還可以實現運行syslog協議的機器間通信,通過分析這些網絡行為日志,藉以追蹤掌握與設備和網絡有關的狀況。
因為守護進程是沒有控制終端的,所以一般不能要求守護進程將出錯記錄寫到控制台上,當然也不能寫到一個文件中,如果每個守護進程都寫到一個文件中,則無疑再用的時候是很麻煩的

這是日志系統的主要設施,這個設施被稱為syslog設施,與用戶進程調用syslog不是一個東西,三種可以產生日志的方法
(1)用戶守護進程調用syslog函數產生日志消息,這些消息被送往UNIX域數據報套接字(這是一個設備,目前先了解為就是一個設施)然后接下來syslog是不產生UDP數據報的,要注意
(2)通過tcp/ip網絡在或者不在此系統上的進程發送消息到UDP端口54
(3)內核通過log發送到klog
其中syslogd這個守護進程讀取這三種格式的日志消息,並且根據/etc/syslog.conf發送,該文件決定了不同種類的消息應該送向何處,列如緊急消息可送往系統管理員,而警告信息可以送往一個文件
該syslog整個設備的接口(這里的接口指的是操作整個syslog的接口,可以往/dev/log或者UDP或者klog里面寫,總之就是整個syslog的寫入接口,但是可以根據選項選擇怎樣寫,寫什么樣的小設備)
主要有四個接口
#include<syslog.h>
void openlog(const char* ident,int option,int facility);
ident:一般是程序的名字,自動追加到每則日志消息中
option:

打開日志系統,其中這個是可選的,如果沒有調用openlog,則在第一次調用syslog的時候,自動調用openlog。
facility:

facility可以讓配置文件說明,來自不同設施的消息將以不同的方式進行處理
注意:如果不調用openlog,或者以facility為0來調用,那么在調用syslog,可將facility作為priority參數的一個不分進行說明
void syslog(int priority,const char *format,....);
priority:是facility和level的組合,其中level可以是

format:以及其他的所有參數傳至vsprintf函數進行格式話,在format中,每個出現的%m字符都被替換成和errno對應的字符串
void closelog(void );
用來關閉被用於與syslogd函數通信的描述符
int setlogmask(int makdpri);
設置屏蔽字,如果被設置,則不進行屏蔽,優先級屏蔽字
列如,我們提供一個范列

4單實例守護進程
什么叫做單實例守護進程
因為出於需要,系統在某個時間只需要一個守護進程運行,列如有的守護進程需要排他性的訪問一個設備;比如說,cron進程在具體 的時間內運行某一個進程,此進程是守護進程,如果運行多個這樣子的守護進程,則會在相同的時間內運行多個一樣的程序,造成混輪,所以這個cron進程需要字任意一個時間內運行一個守護進程!!
守護進程采用文件-和記錄鎖實現了單實例守護進程,此方法假設,守護進程都需要創建一個文件,並且在第一個守護進程創建文件之后就對整個文件進行加鎖,使其他的相同的守護進程不能自進行此文件的加鎖,這樣就實現了守護進程的單實例運行
#include "unistd.h"
#include "stdlib.h"
#include "fcntl.h"
#include "syslog.h"
#include "string.h"
#include "errno.h"
#include "stdio.h"
#include <sys/stat.h>
#define LOCKFILE "var/run/daemon.pid"
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
extern int lockfile(int);
int already_running(void)
{
int fd;
char buf[16];
fd = open(LOCKFILE,O_RDWR|O_CREAT,LOCKMODE);
if(fd < 0)
{
syslog(LOG_ERR,"can't open :%s %s",LOCKFILE,strerror(errno));
exit(1);
}
if(lockfile(fd) < 0)
{
if(errno == EACCES || errno ==EAGAIN)
{
close(fd);
return(1);
}
syslog(LOG_ERR,"can't lock %s:%s",LOCKFILE,strerror(errno));
exit(1);
}
ftruncate(fd,0);
sprintf(buf,"%ld",(long)getpid());
write(fd,buf,strlen(buf)+1);
return 0;
}
int main()
{
already_running();
return 0;
}
如以上的程序,先創建一個文件夾,並且在運用此函數時候鎖住該文件,如果再有守護進程調用的時候,則進行鎖住,並且將其寫入文件,strerror是將錯誤標志轉換成字符串
5守護進程的慣例
在unix系統中,通常的守護進程設計通常是如此設計
(1)如果守護進程使用鎖文件,則該鎖文件通常位於/var/run目錄中。然而需要主義的是,在該目錄創建文件通常需要超級用戶的權限,鎖文件的名字通常是name.pid,例如cron守護進程鎖文件的名字是/var/run/crond.pid
(2)如果守護進程支持配置選項,那么該配置文件一般是放在/etc上的,文件名字一般是name.cond,name是該守護進程或服務的名字,
(3)守護進程可以使用命令啟動,但是該啟動命令一班是在系統初始化 的腳本中進行的,如果守護進程終止,應當自動進行重啟,並且如果一個守護進程更新了配置文件,則應該重新啟動
(4)為了避免當更改配置文件,需要停止在啟動進程的時候,某些進程將捕捉SIGHUP信號,當接受到此信號的時候,重新讀取配置文件。因為守護進程不與終端結合,他們或者是沒有控制終端的會話首進程,或者是孤兒進程組的成員,所以守護進程沒有里有期望接受SIGHUP,因此,守護進程可以重復使用SIGHUP