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