網絡編程十三章:守護進程

守護進程是在後臺運行且不與任何控制終端關聯的進程

守護進程沒有控制終端,所以當有事發生的時候, 它們要有輸出消息的方法可用. syslog函數就是輸出這些消息的標準方法,它把這個消息發給syslogd守護進程

syslogd啓動過程
1 讀取配置文件(日誌消息應該如何處理)
2 創建Unix域套接字,
3 創建UDP套接字,捆綁端口514
4 打開路徑名,/dev/klog,來自內核中的任何出錯消息看着都像是這個設備的輸入.

守護進程訪問方式:
1 創建一個Unix域數據,然後通過路徑名發送消息
2 syslog函數
3 創建一個UDP數據報.

syslog函數

從守護進程中登記消息.

#include<syslog.h>
void syslog(int priority,const char *message,...);
syslog(級別和設施組合,出錯的文本)
級別是:消息的的重要級別
設施是發送進程的類型,

syslog的調用:被應用進程首次調用的時候,創建一個Unix套接字,然後調用connect連接到由syslogd創建的Unix數據包套接字的衆所周知路徑名,這個套接字一直保持打開.

openlog和closelog(syslog的替換)
#include<syslog.h>
void openlog(const char *ident,int options,int facility)//每個消息之前的字符串(通常是程序名字),選項參數
void closelog(void);

詳見289.

daemon_init函數(把一個普通進程轉換爲守護進程):
#include"unp.h"
#include<syslog.h>
#define MAXFD  64
extern int daemon_proc ;//在error.c中定義
int daemon_init(const char *pname,int facility){
	int i;
	pid_t pid;
	if((pid=fork())<0){
		return 	-1;
	}else if(pid){
		exit(0);
	}//關閉父進程.也就是關閉這個終端.
	setsid();//創建一個會話,當前進程變爲會話頭進程
	signal(SIGHUP,SIG_IGN);//忽略這個信號
	if((pid=fork())<0)return -1;
	else if(pid) exit(0);//當沒有控制終端的一個會話頭進程打開一個控制終端的時候,該終端自動成爲該進程的控制終端,再次fork之後,確保這個進程不是一個會話頭進程,也就是確保沒有控制終端,
	daemon_proc = 1;//其值爲0表示,調用syslog替代fprintf到標準錯誤輸出,
	chdir("/");//改變工作目錄到根目錄.
	for(int i=0;i<MAXFD;i++)close(i);	//關閉從父進程繼承來的所有的描述符.
	
	//重定向標準輸入,標準輸出,和標準錯誤輸出.
	open("/dev/null",O_RDONLY);
	open("/dev/null",O_RDWR);
	open("/dev/null",O_RDWR);

	openlog(pname,LOGPID,facility);//隨每個日誌消息登記進程ID.

	return 0;
}
守護進程:時間獲取服務器程序
#include<syslog.h>
#include "unp.h"
int main(){
	int listenfd,clifd;
	socket_len len,clilen;
	socket_addr cliaddr;
	char buff[MAXLINE];
	time_t ticks;
	if(argc<2||argc>3){
		err_quite("不正確(IP地址或者端口號)");
	}
	daemon_init(argv[0],0);//進程名和設施
	if(argc==2) listenfd = tcp_listen(NULL,argv[2],&addrlen);
	else listenfd = tcp_listen(argv[1],argv[2],&addrlen);
	
	cliaddr = malloc(addrlen);
	for(;;){
		len=addrlen;
		connfd = accept(listenfd,cliaddr,len);
		err_msg("connnect from %s\n",sock_ntop(cliaddr,len));
		
		ticks = time(NULL);
		snprintf(buff,sizeof(buff),""%.24s\r\n",ctime(ticks));
		write(connfd,buff,strlen(buff)));
	}
}

inetd 守護進程:

因爲所有守護進程擁有幾乎相同的啓動代碼,並且每個守護進程都在進程表中佔據一個表項(但是大部分時間處於休眠狀態)。
通過inetd處理普通進程的大部分啓動細節,從而不用使用daemon_init。單個inetd進程就能爲多個服務器處理外來客戶請求,取代每個服務一個進程,從而減少機器中進程總數。

inetd守護進程工作流程:
1 讀取配置文件,並且爲文件中每個服務創建一個套接字。
2 爲每個套接字調用bind,指定捆綁相應服務的端口和地址。
3 對於每個TCP套接字,調用listen以接受外來的連接請求
4 創建好所有套接字之後,調用select來等待其中任何一個套接字變爲可讀,
5 可讀之後,調用accept。
6 派生子進程,由子進程處理服務請求。

子進程關閉除了要處理套接字描述符之外的所有其他描述符。

daemon_inetd,用於inetd啓動的服務器
#include "unp.h"
#include <syslog.h>
extern int daemon_proc;
void daemon_inet(const char *pname,int fcility){
	daemon_proc = 1;
	openlog(pname,LOG_PID,facility);
}

由inet啓動的時間獲取服務器程序:

#include "unp.h"
#include<syslog.h>
int main(int argc,char **argv){
	socklen_t len,addrlen;
	struct sockaddr cliaddr;
	char buff[MAXLINE];
	time_t ticks;
	deamon_inet(argv[0],0);
	
	cliaddr = malloc(sizeof(struct sockaddr_storage));//128個字節的網絡,四個重要的數據結構之一。
	len =sizeof(struct sockaddr_storage);//128
	getpeername(0,cliaddr,&len);
	err_msg("connect from %s\n",sock_ntop(cliaddr,len));
	
	ticks = time(NULL);
	snprintf(buff,sizeof(buff),"%.24\r\n",ctime(&ticks));
	write(0,buff,strlen(buff));
	close(0);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章