Linux高性能服務器編程:高性能服務器程序框架


服務器有三個主要模塊:

(1)I/O處理單元

(2)邏輯單元

(3)存儲單元

 

1.服務器模型

C/S模型

邏輯:服務器啟動后,首先創建一個或多個監聽socket,並調用bind函數將其綁定到服務器感興趣的端口上,然后調用listen函數等待客戶連接。

服務器運行穩定后,客戶端就可以調用connect函數向服務器發起連接了。

P2P模型

P2P模型使得每台機器在消耗服務的同時也給別人提供服務,這樣資源能夠充分、自由的共享。

 

2.服務器編程框架

包含:

I/O處理單元  請求隊列   邏輯單元   請求隊列  網絡存儲單元

I/O處理單元:處理客戶連接,讀寫網絡數據

邏輯單元:業務進程或線程

網絡存儲單元:本地數據庫、文件或緩存

請求隊列:各單元之間的通信方式

 

3. I/O模型

阻塞I/O執行的系統調用可能因為無法立即完成而被操作系統掛起,知道等待的事件發生為止。可能阻塞的系統調用包括:accept,send,recv和connect。

非阻塞I/O執行的系統調用則總是立即返回,而不管時間是否發生。如果事件沒有立即發生,這些系統調用就返回-1,和出錯的情況一樣。必須根據errno來區分這兩種情況。

對accept,send和recv而言,事件未發生時errno通常被設置成EAGAIN或者EWOULDBLOCK:對connect而言,errno則被設置成EINPROGRESS。

 

顯然,只有在事件已經發生的情況下操作非阻塞I/O,才能提高程序的效率。I/O通知機制有:I/O復用和SIGIO信號。

I/O復用:應用程序通過I/O復用函數向內核注冊一組事件,內核通過I/O復用函數把其中就緒的事件通知給應用程序。Linux上常用I/O復用函數是selcet、poll和epoll_wait。I/O復用本身是阻塞的,能提高效率的原因是

它們具有同時監聽多個I/O事件的能力。

SIGIO信號:可以為一個目標文件描述符指定宿主進程,那么被指定的宿主進程將捕獲到SIGIO信號。這樣,當目標文件描述符上有事件發生時,SIGIO信號的信號處理函數將被觸發,可以在該信號處理函數中對目標文件描述符執行非阻塞I/O操作了。

 

理論上說,阻塞I/O,I/O復用和信號驅動I/O都是I/O同步模型。在這三種模型中,I/O的讀寫操作,都是I/O事件發生之后,由應用程序來完成的。

對異步I/O而言,用戶可以直接對I/O執行讀寫操作,這些操作告訴內核用戶讀寫緩沖區的位置,以及I/O操作完成之后內核通知應用程序的方式,異步I/O的讀寫操作總是立即返回,而不論I/O是否是阻塞的,因為真正的讀寫操作已經由內核接管。

也就是說,同步I/O模型要求用戶代碼自行執行I/O操作(將數據從內核緩沖區讀入用戶緩沖區,或將數據從用戶緩沖區寫入內核緩沖區),而異步I/O機制則由內核來執行I/O操作(數據在內核緩沖區和用戶緩沖區之間的移動是由內核在“后台”完成的)。

同步I/O向應用程序通知的是I/O就緒事件,而異步I/O向應用程序通知的是I/O完成事件。

 

4.兩種高效的事件處理模式

使用同步I/O模型實現Reactor模式:

(1)主線程往epoll內核事件表中 注冊socket上的讀就緒事件。

(2)主線程調用epoll_wait等待socket上有數據可讀。

(3)當socket上有數據可讀時,epoll_wait通知主線程,主線程將socket事件放入請求隊列中。

(4)睡眠在請求隊列上的某個工作線程被喚醒,從socket讀取數據,並處理客戶請求,然后往epoll內核事件表中注冊該事件的寫就緒事件。

(5)主線程調用epoll_wait等待socket可寫。

(6)當socket可寫時,epoll_wait通知主線程。主線程將socket可寫事件放入請求隊列。

(7)睡眠在請求隊列上的某個工作線程被喚醒,它往socket上寫入服務器處理客戶請求的結果。

 

Proactor模式

Proactor模式將所有I/O操作都交給主線程和內核來處理,工作線程僅僅負責業務邏輯。

 

半同步/半異步:

同步線程用於處理客戶邏輯,異步線程用於處理I/O事件。

異步線程由主線程充當。負責監聽所有socket上的事件。若監聽socket上有可讀事件發生,即有新的連接到來,主線程就接受之以得到新的連接socket,然后往epoll內核事件表中注冊該socket上的讀寫事件。

如果連接socket上有讀寫事件發生,即有新的客戶請求到來或有數據要發送到客戶端,主線程就將該連接socket插入請求隊列中。所有工作線程都睡眠在請求隊列上,當有任務帶來時,將通過競爭獲得任務的接管權。

缺點:

主線程和工作線程共享請求隊列。主線程往請求隊列中添加任務,或者工作線程從請求隊列中取出任務,都需要對請求隊列加鎖保護,從而拜拜浪費CPU時間。

每個工作線程在同一時間只能處理一個客戶請求。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM