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