史云輝 于 峰 張云成
[摘 要]Unix系統(tǒng)的fork函數(shù)為并發(fā)服務提供可能性,通過輔助控制完善fork函數(shù)的使用,可以提高應用系統(tǒng)的穩(wěn)定性。
[關鍵詞]Unix;并發(fā);fork
一、引言
Unix并發(fā)服務器的實現(xiàn)方式有多種:有動態(tài)創(chuàng)建服務器進程的方式(TongLink/Easy);有啟動固定數(shù)量服務進程的方式;還有兩者結合,預先啟動固定數(shù)量的服務進程,然后根據(jù)應用過程中的請求情況,動態(tài)增加或減少服務進程的方式(Tuxedo)來滿足客戶需求。以上方式各有自己的特點,以適應不同類型的客戶服務請求。但無論采取何種并發(fā)服務器形式,Unix內核創(chuàng)建新進程的方法只有一個,即調用fork函數(shù)。本文針對fork機制從派生數(shù)量及運行時間上進行控制,使之完善,以提高應用系統(tǒng)服務程序的穩(wěn)定性,更好地為客戶服務。
二、fork函數(shù)應用簡述
對于客戶端的請求,一般服務器都使用循環(huán)處理方式,并且阻塞在接收客戶端請求操作上。當請求到來時,父進程調用fork函數(shù)產生一子進程,然后父進程繼續(xù)接收客戶端請求;子進程完成對客戶端請求的處理,處理完成后結束子進程。
fork函數(shù)在并發(fā)服務系統(tǒng)應用的典型流程如下:
while(1){
接收客戶端請求:
if ((pid=fork())<0){
錯誤處理;
else if (pid==0){
子進程處理:
Exit(0);
}
}
該方式雖然實現(xiàn)簡單,但是存在以下問題:
1.并發(fā)數(shù)量問題:并發(fā)數(shù)量決定了應用系統(tǒng)并發(fā)服務的處理能力。Unix系統(tǒng)為每個用戶分配的資源是有限制的,當大量的并發(fā)請求到達時,可能達到用戶進程數(shù)量上限,導致產生“系統(tǒng)資源不能臨時獲得”的錯誤發(fā)生,影響系統(tǒng)的穩(wěn)定性。綜合考慮服務器的其他須處理作業(yè)情況,應當為該服務器定制可派生進程的上限,在確保系統(tǒng)穩(wěn)定運行的前提下,為客戶端提供及時、正確的響應。運行時間問題:服務進程可能因某些異常情況產生超時,并且自身不能恢復,應提供一種機制能夠監(jiān)測到處于超時狀態(tài)的進程,然后將其安全退出,釋放資源。
三、fork函數(shù)的完善
完善fork機制的設計思想及處理流程:
1.首先在服務程序啟動時,應該告訴它最大的進程并發(fā)數(shù)量和服務最長的超時時間。通過讀取配置文件實現(xiàn),由于父子進程都要使用這兩個參數(shù),所以應將其放到共享內存中并輔以信號量進行互斥訪問控制。
2.在父進程調用fork函數(shù)之前,應檢查共享內存記錄的處于工作狀態(tài)的子進程數(shù)量是否已經到達預定義的上限:如已達到,則返回相應的錯誤信
息給客戶程序;如未達到,則計數(shù)器加一后調用fork函數(shù)。實際上,在遍歷處于工作狀態(tài)的子進程時,也要檢查其工作時間是否已經超過超時時限;如果超時,則kill該子進程。
3.進入子進程,第一件事情將該進程ID和開始運行時間登記在共享內存中,供父進程檢查超時以及殺掉該進程使用。該步操作也可以放在父進程中,調用fork之后進行。放在子進程處理的好處是節(jié)省父進程的時間,使之盡快返回,準備處理下一客戶請求。
4.子進程退出前的最后一件事情:釋放資源,即將共享內存中處于工作狀態(tài)進程數(shù)的計數(shù)器減一。
5.應提供查看服務進程狀態(tài)的功能,通過檢索共享內存數(shù)據(jù)實現(xiàn)。
四、具體技術實現(xiàn)
1.數(shù)據(jù)結構定義
①進程控制結構定義:置于共享內存的進程狀態(tài)信息
structs_forlctrl{
pid_tpid;
longnum;
time_tstime;
};
structs_fork_ctrl shm[MaxProcNum+1];
2.結構數(shù)組說明:
結構數(shù)組的元素個數(shù):允許的最大進程數(shù)加一,其中第—個元素父進程使用,其他元素記錄子進程信息。
第一個元素shin[0]字段說明:
shm[0].pid:記錄父進程ID,供查看使用
shm[0】.num:并發(fā)進程上限(配置文件讀入)
shin[0].stime:超時時間(配置文件讀入)
其他元素字段說明:
shrn[1—MaxProcNum].pid:子進程ID,超時后,可以根據(jù)該值Kill子進程
shm[1—MaxProcNum].num:子進程執(zhí)行次數(shù)累計,用于統(tǒng)計分析
shm[1—MaxProcNum].stime:子進程派生時間,紀錄該進程運行開始時間,與系統(tǒng)當前時間比較,可知該進程是否處于超時狀態(tài)
2.函數(shù)原型定義
按照設計思想,定義一組實現(xiàn)函數(shù),用于在派生進程前后使用,以提高應用程序的穩(wěn)定性:
1.in fort_init(char*program,int MaxProcNum,intReleaseTime):初始化函數(shù)。按最大進程數(shù)加一分配共享內存空間,并設定初試值。第—個參數(shù)程序名用來獲取IPC資源ID。
2.intforkprealloc():進程預分配函數(shù)。遍歷共享內存進程結構,如果找到空閑項,則預分配之,進程數(shù)加—;如果發(fā)現(xiàn)超時進程,則殺掉該進程,并使用該進程位置預分配。該函數(shù)屬于原子操作,必須使用互斥防問機制(如信號量)控制。
3. int fork_alloc():進程信息分配函數(shù)。登記新派生的子進程ID及其啟動時間。
4. void fork_free():進程信息釋放函數(shù)。刪除進程登記信息,供父進程繼續(xù)分配使用。
5.int for_info():進程信息顯示函數(shù)。顯示共享內存登記的進程狀狀況。
五、完善后的fork調用流程
fork_init(argv[0],MaxProcNum,ReleaseTime):
while(1){
接收客戶端請求;
if (fork_prealloc()<0){
并發(fā)服務已達處理上限,暫不接受請求;
continue;
}
if ((pid==fork())<0){
錯誤處理:
else if(pid==0){
fork—alloc();
子進程處理:
fork_free();
exit(0);
}
}(編輯/劉佳)