久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Linux高性能服務器程序框架

 心不留意外塵 2016-04-25
http://blog.csdn.net/mfcing/article/details/50241319
2015.12
8  高性能服務器程序框架


服務器解構(gòu)為三個主要模塊:
IO處理單元,。四種IO模型和兩種高效事件處理模式,。
邏輯單元。兩種高效并發(fā)模式,。
存儲單元,。(暫不討論)


IO模型:
阻塞IO
非阻塞IO
IO復用//程序阻塞于IO復用系統(tǒng)調(diào)用,但可同時監(jiān)聽多個IO事件,。
SIGIO信號//信號觸發(fā)讀寫就緒事件,,用戶程序執(zhí)行讀寫操作,程序沒有阻塞階段
異步IO//內(nèi)核執(zhí)行讀寫操作并觸發(fā)讀寫完成事件,。程序沒有阻塞階段


兩種高效的事件處理模式:
服務器通常要處理三類事件:IO事件,、信號事件、定時事件,。
同步IO模型通常用于實現(xiàn)Reactor模式,,異步IO模型用于實現(xiàn)Proactor模式。


Reactor模式:
同步IO模型(以epoll_wait為例)實現(xiàn)的Reactor模式的工作流程:
1,、主線程往epoll內(nèi)核事件表中注冊socket上的讀就緒事件,。
2、主線程調(diào)用epoll_wait等待socket上有數(shù)據(jù)可讀,。
3,、當socket上有數(shù)據(jù)可讀時,epoll_wait通知主線程,。主線程則將socket可讀事件放入請求隊列,。
4、睡眠在請求隊列上的工作線程被喚醒,,它從socket讀取數(shù)據(jù),,并處理客戶請求,然后往epoll內(nèi)核事件表中注冊該socket上的寫就緒事件,。
5,、主線程調(diào)用epoll_wait等待socket可寫。
6,、當socket可寫時,,epoll_wait通知主線程,。主線程將socket可寫事件放入請求隊列。
7,、睡眠在請求隊列上的某個工作線程被喚醒,,它往socket上寫入服務器處理客戶請求的結(jié)果。


工作線程從隊列中取出事件后,,將根據(jù)事件是可讀或可寫執(zhí)行讀寫數(shù)據(jù)和處理請求的操作,。因此,在Reactor模式中,,沒必要區(qū)分所謂的“讀工作線程”和“寫工作線程”,。




Proactor模式:
與Reactor模式不同,Proactor模式將所有IO操作都交給主線程和內(nèi)核來處理,,工作線程僅僅負責業(yè)務邏輯,。
(以aio_read和aio_write為例)工作流程:
1、主線程調(diào)用aio_read函數(shù)向內(nèi)核注冊socket上的讀完成事件,,并告訴內(nèi)核用戶讀緩沖區(qū)的位置,,以及讀操作完成時如何通知應用程序(這里以信號為例,詳情sigevent的man手冊)
2,、主線程繼續(xù)處理其他邏輯,。
3、當socket上的數(shù)據(jù)被讀入用戶緩沖區(qū)后,,內(nèi)核將向應用程序發(fā)送一個信號,,以通知應用程序數(shù)據(jù)已經(jīng)可用。
4,、應用程序預先定義好的信號處理函數(shù)選擇一個工作線程來處理客戶請求,。工作線程處理完客戶請求之后,調(diào)用aio_write函數(shù)向內(nèi)核注冊socket上的寫完成事件,,并告訴內(nèi)核用戶寫緩沖區(qū)位置,以及寫操作完成時如何通知應用程序(仍以信號為例)
5,、主線程繼續(xù)處理其他邏輯,。
6、當用戶緩沖區(qū)的數(shù)據(jù)被寫入socket之后,,內(nèi)核將向應用程序發(fā)送一個信號,,以通知應用程序數(shù)據(jù)已經(jīng)發(fā)送完畢。
7,、應用程序預先定義好的信號處理函數(shù)選擇一個工作線程來做善后處理,,比如決定是否關(guān)閉socket.




使用同步IO模型(仍然以epoll_wait為例)模擬出的Proactor模式的工作流程如下:
1、主線程往epoll內(nèi)核事件表中注冊socket上的讀就緒事件,。
2,、主線程調(diào)用epoll_wait等待socket上有數(shù)據(jù)可讀,。
3、當socket上有數(shù)據(jù)可讀時,,epoll_wait通知主線程,。主線程從socket循環(huán)讀取數(shù)據(jù),知道沒有更多數(shù)據(jù)可讀,,然后將讀取到的數(shù)據(jù)封裝成一個請求對象并插入到請求隊列,。
4、睡眠在請求隊列上的某個工作線程被喚醒,,它獲得請求對象并處理客戶請求,,然后網(wǎng)epoll內(nèi)核事件表中注冊socket上的寫就緒事件。
5,、主線程調(diào)用epoll_wait等待socket可寫,。
6、當socket可寫時,,epoll_wait通知主線程,。主線程網(wǎng)socket上寫入服務器處理客戶端請求的結(jié)果。




兩種高效的并發(fā)模式:
半同步半異步模式:

這里的“同步”和“異步”和前面的IO的“同步”“異步”是完全不同的概念,。在IO模型中,,“同步”和“異步”區(qū)分的是內(nèi)核向應用程序通知的是何種IO事件(是就緒事件還是完成事件),以及該由誰來完成IO讀寫(是應用程序還是內(nèi)核),。在并發(fā)模式中,,“同步”指的是程序完全按照代碼序列的順序執(zhí)行;“異步”指的是程序的執(zhí)行需要由系統(tǒng)事件來驅(qū)動,。常見的系統(tǒng)事件包括中斷,、信號等。
顯然異步線程的執(zhí)行效率高,,實時性強,,是很多嵌入式系統(tǒng)采用的模型。但編寫異步方式執(zhí)行的程序相對復雜,,難于調(diào)試和擴展,,而且不適合于大量的并發(fā)。而同步線程則相反,,它雖然效率相對較低,,實時性較差,但邏輯簡單,。
在半同步半異步模式中,,同步線程用于處理客戶邏輯,異步線程用于處理IO事件,。異步線程監(jiān)聽到客戶請求后,,就將其封裝成請求對象并插入到請求隊列中,。請求隊列將通知某個工作在同步模式的工作線程來讀取并處理該請求對象。




半同步半異步模式存在如下缺點:
1,、主線程和工作線程共享請求隊列,。
2、每一個工作線程在同一時間只能處理一個客戶請求,。
高效模式:每個工作線程都能同時處理多個客戶連接,。
主線程只管理監(jiān)聽socket,連接socket由工作線程來管理,。當有新的連接到來時,,主線程就接受之并將新返回的連接socket派發(fā)給某個工作線程,此后該socket上的任何IO操作都由被選中的工作線程來處理,,直到客戶端關(guān)閉連接,。主線程向工作線程派發(fā)socket的最簡單的方式,是往它和工作線程之間的管道里寫數(shù)據(jù),。工作線程檢測到管道里有數(shù)據(jù)可讀時,,就分析是否是一個新的客戶連接請求到來。如果是,,則把該新socket上的讀寫事件注冊到自己的epoll內(nèi)核事件表中,。
每個線程(主線程和工作線程)都維持自己的事件循環(huán),它們各自獨立的監(jiān)聽不同的事件,。因此在這種模式中,,每個線程都工作在異步模式,所以它并非嚴格意義上的半同步半異步模式,。



領(lǐng)導者追隨者模式:
是多個工作線程輪流獲得事件源集合,,輪流監(jiān)聽、分發(fā)并處理事件的一種模式,。在任意時間點,,程序都僅有一個領(lǐng)導者線程,它負責監(jiān)聽IO事件,。而其他線程都是追隨者,,它們休眠在線程池中等待成為新的領(lǐng)導者。當前的領(lǐng)導者如果檢測到IO事件,,首先要從線程池中推選出新的領(lǐng)導者線程,然后處理IO事件,。此時,,新的領(lǐng)導者等待新的IO事件,而原來的領(lǐng)導者則處理IO事件,,二者實現(xiàn)了并發(fā),。
包含如下幾個組件:句柄集(HandleSet),、線程集(ThreadSet)、事件處理器(EventHandler),、具體的事件處理器(ConcreteEventHandler),。




提高服務器性能的其他建議:


池:


服務器硬件資源相對“充裕”,,那么提高服務器性能的一個很直接的方法就是以空間換時間,,即“浪費”服務器的硬件資源,以換取其運行效率,。這就是池(pool)的概念,。池是一種資源的集合,這組資源在服務器啟動之初就被完全創(chuàng)建并初始化,,這稱為靜態(tài)資源分配,。速度要快得多,因為分配系統(tǒng)資源的系統(tǒng)調(diào)用都是很耗時的,。當服務器處理完一個客戶連接后,,可以把相關(guān)的資源放回池中,無須執(zhí)行系統(tǒng)調(diào)用來釋放資源,。從最終效果來看,,池相當于服務器管理系統(tǒng)資源的應用層設施,它避免了服務器對內(nèi)核的頻繁訪問,。


按照資源類型分類:
內(nèi)存池:通常用于socket的接收緩存和發(fā)送緩存,。
進程池、線程池:并發(fā)編程常用“伎倆”,。
連接池:常用于服務器或服務器集群的內(nèi)部永久連接,。




數(shù)據(jù)復制:


應該避免不必要的數(shù)據(jù)復制,尤其當數(shù)據(jù)復制發(fā)生在用戶代碼和內(nèi)核之間的時候,。如果內(nèi)核可以直接處理從socket或者文件讀入的數(shù)據(jù),,則應用程序就沒有必要將這些數(shù)據(jù)從內(nèi)核緩沖區(qū)復制到應用程序緩沖區(qū)。如ftp服務器,,服務器只需檢測目標文件是否存在,,以及客戶是否有讀取權(quán)限,而不用關(guān)心文件具體內(nèi)容,。就可以使用“零拷貝”sendfile來直接將其發(fā)送給客戶,。
此外,用戶代碼內(nèi)部(不訪問內(nèi)核)的數(shù)據(jù)復制也是應該避免的,。如兩個工作進程之間要傳遞大量的數(shù)據(jù)時,,我們就應該考慮使用共享內(nèi)存來在它們之間直接共享這些數(shù)據(jù),而不是使用管道或者消息隊列來傳遞。




上下文切換和鎖:
并發(fā)程序必須考慮上下文切換(context switch)的問題,,即進程線程切換導致的系統(tǒng)開銷,。即使是IO密集型的服務器,也不應該使用過多的工作線程(或進程,,下同),,否則切換將占用大量CPU時間,服務器真正用于業(yè)務邏輯的CPU時間比重就顯得不足了,。因此為每個客戶連接都建立一個服務器線程的模型不可取,。之前描述的半同步半異步模型是一個比較合理的解決方案,它允許一個線程同時處理多個客戶連接,。此外,,多線程服務器的一個優(yōu)點是不同的線程可以同時運行在不同的cpu上。當線程數(shù)量不大于cpu的數(shù)目時,,上下文切換就不是問題了,。
并發(fā)程序需要考慮的另一個問題是共享資源的枷鎖保護。鎖通常被認為是導致服務器效率低下的一個因素,,因為由它引入的代碼不僅不處理任何業(yè)務邏輯,,而且需要訪問內(nèi)核資源。因此,,服務器如果有更好的解決方案,,就應該避免使用鎖。如果服務器必須使用鎖,,則可以考慮減小鎖的粒度,,比如使用讀寫鎖。當所有工作線程都只讀取一塊共享內(nèi)存的內(nèi)容時,,讀寫鎖并不會增加系統(tǒng)的額外開銷,。只有當其中一個工作線程需要寫這塊內(nèi)存時,系統(tǒng)才必須去鎖住這塊區(qū)域,。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導購買等信息,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多