併發伺服器 多程序實現

2021-08-15 22:59:02 字數 3510 閱讀 9403

通過簡單的socket可以實現一對一的c/s通訊,當多個客戶端同時進行伺服器訪問,那麼伺服器只能按序的一一進行處理,除了第乙個客戶端,其餘客戶端都會陷入等待。並且這樣的程式只能實現半雙工通訊(資料能雙向傳輸,但同一時刻只能單向傳遞,通過切換傳輸方向實現雙工),而且實現方式繁瑣,功能拘束,實用價值很低。那麼要想實現乙個伺服器能同時接受多個客戶端訪問並且能夠雙工通訊的併發伺服器,其中一種實現方式----多程序。

函式原型:

#include pid_t fork(void);   //失敗時返回-1
fork函式將會複製正在執行的、呼叫fork函式的程序(實際linux實現了寫時賦值,建立新程序並不會直接建立全部記憶體副本,程序可隨意讀取記憶體資料,只有當有程序需要會記憶體進行修改時,作業系統才會為新程序建立新的記憶體副本)兩個程序都將執行fork函式呼叫之後的**。為加以區分父程序和子程序,fork函式在不同程序返回值不同。

int mian(void)

if( 0 == pid )

else

return 0;

}

當子程序執行完畢後,並不會釋放所有資源,子程序會保留一部分資源(子程序的結束狀態等資訊)等待父程序**,當子程序退出,父程序未**這段時間,子程序就成為殭屍程序。

#include pid_t wait(int *status);  //成功時返回終止的子程序id,失敗時返回-1
呼叫此函式時如果已有子程序終止,那麼子程序終止時傳遞的返回值(exit函式的引數值、main函式的return返回值)將儲存到該函式的引數所指記憶體空間。但函式引數指向的單元中還包含其他資訊,需要通過下列巨集進行分離。

示例:

if(wifexited(status)) //是否正常終止

呼叫wait函式時,如果沒有已終止的子程序,那麼程式將阻塞直到有子程序終止。

wait函式會引起程式阻塞,呼叫waitpid函式既可以銷毀殭屍程序,又能防止阻塞。

#include pid_t waitpid(pid_t pid,int *status,int options);   //成功時返回終止的子程序id(或0),失敗時返回-1。

waitpid(-1,&status,wnohang);  //銷毀任意殭屍程序,若沒有終止的子程序則返回0
子程序不知何時結束時,waitpid函式要麼迴圈呼叫,要麼在程序中阻塞等待,無論哪一種都不是優秀的解決方案。

子程序終止的識別主體是作業系統,因此,若作業系統能把如下資訊告訴正忙於工作的父程序,就能搞笑的解決問題。

所以這裡引入了訊號處理(signal handling)機制。當特定的事件發生時由作業系統向程序發出訊息,程序執行相關操作響應該訊息。

函式原型:

#include void (*signal(int signo,void (*func)(int)))(int);
引數signo代表監聽的訊號,void (*func)(int)代表在訊號發生時呼叫的函式位址值(指標)。

在signal函式中註冊的部分特殊情況(訊號)和對應的常數。

void child(int)

signal(sigchld,child); //子程序終止時,呼叫child函式(child函式返回型別為void,引數列表為int)

發生sigalrm訊號,需要介紹alarm函式

#include unsigned int alarm(unsigned int seconds);  //返回0或以秒為單位的距sigalrm訊號發生所剩時間
示例:

void timeout(int sig)

signal(sigalrm,timeout);

alarm(2); //兩秒後發出sigalrm訊號

利用sigaction函式進行訊號處理

由於signal函式在unix系列的不同作業系統中可能存在區別,但sigaction函式完全相同,相比signal函式更穩定常用。

函式原型:

#include int sigaction(int signo,const struct sigaction * act,struct sigaction * oldact);  //成功時返回0,失敗時返回-1
sigaction結構體:

struct sigaction

結構體的sa_handler成員儲存訊號處理函式的指標值。sa_mask和sa_flags初始化為0即可,這兩個成員用於指定訊號相關的選項和特性。

//銷毀殭屍程序

void dest_childpro(int sig)

}int main(void)

else

return 0;

}

利用以上知識加上socket程式設計即可完成多程序的併發伺服器,值得一提的是套接字作為作業系統的資源,並不會在fork函式後被複製,但會複製其檔案描述符,所以最好子程序中close監聽套接字檔案描述符,乙個套接字同時存在多個檔案描述符時,只有所有檔案描述符都銷毀時,才能銷毀套接字。

i/o分割

pid_t pid = fork();

if( 0 == pid)

else

...

分割i/o後,子程序負責寫,父程序負責讀,就能實現雙工服務端了。

程序通訊(ipc)意味著兩個不同程序間可以交換資料,為了完成這一點,有多種方式實現,在這介紹一種較為簡單的單工通訊--管道。

管道並非程序資源,和套接字一樣,屬於作業系統,也就是不會被fork函式複製。

函式原型:

#include int pipe(int filedes[2]);   //成功時返回0,失敗時返回-1
以2個元素的int陣列位址作為引數呼叫上述函式時,陣列中存在兩個檔案描述符,它們將被用作管道的出口和入口。父程序呼叫該函式建立管道,同時獲取對應於出入口的檔案描述符,通過fork函式,檔案描述符也會被複製,子程序也能通過檔案描述符進行管道通訊。

char str1 = "helloworld";

char str2[11];

int fds[2];

int *read_fd = &fds[0];

int *write_fd = &fds[1];

pid_t pid = fork();

if(0==pid)

else

以上為管道的單向通訊,其實當父子程序都擁有管道的檔案描述符後,不難實現可以利用管道雙向通訊(父子程序都利用兩個檔案描述符),但是資料進入管道後就成為無主資料,先進行讀操作的程序會把資料取走。這樣一來,就增加了程式設計的複雜度和降低了安全性。既然乙個管道用作單向通訊,那麼我們可以再建立乙個管道,兩個管道分別作為父子程序的讀寫。(以此思路可以實現程序間雙工通訊)。

linux多程序併發伺服器

多程序併發伺服器,多執行緒版 include include include include include include include include include include include 程序 函式 void recyle int num int main int argc,cha...

Web伺服器 併發伺服器 多程序(3 4 1)

目錄 在單程序的時候,相當於 是來乙個客戶,派乙個人去服務一下 效率低,現在使用多程序來服務 假設場景 100個人同時訪問頁面 單程序 一次處理,後面的會等待時間長 多程序 併發處理,一起處理 當然程序不能越多越好,硬體不支援 執行緒的開銷比程序少,但最好使用協程 注意 用process來建立子程序...

TCP併發伺服器多程序程式設計

環境 linux c 功能 併發伺服器實現cs通訊 server.c include include include include include include 檔案io read write close fork waitpid include socket struct sockaddr i...