伺服器端為了能流暢處理多個客戶端鏈結,一般在某個執行緒a裡面accept新的客戶端連線並生成新連線的socket fd,然後將這些新連線的socketfd給另外開的數個工作執行緒b1、b2、b3、b4,這些工作執行緒處理這些新連線上的網路io事件(即收發資料),同時,還處理系統中的另外一些事務。這裡我們將執行緒a稱為主線程,b1、b2、b3、b4等稱為工作執行緒。工作執行緒的**框架一般如下:
while
(!m_bquit)
在epoll_or_select_func()中通過select()或者poll/epoll()去檢測socket fd上的io事件,若存在這些事件則下一步handle_io_events()來處理這些事件(收發資料),做完之後可能還要做一些系統其他的任務,即呼叫handle_other_things()。
這樣做有三個好處:
執行緒a接收的新連線,可以根據一定的負載均衡原則將新的socket fd分配給工作執行緒。常用的演算法,比如round robin,即輪詢機制,即,假設不考慮中途有連線斷開的情況,乙個新連線來了分配給b1,又來乙個分配給b2,再來乙個分配給b3,再來乙個分配給b4。如此反覆,也就是說執行緒a記錄了各個工作執行緒上的socket fd數量,這樣可以最大化地來平衡資源,避免一些工作執行緒「忙死」,另外一些工作執行緒「閒死」的現象。
即使工作執行緒不滿載的情況下,也可以讓工作執行緒做其他的事情。比如現在有四個工作執行緒,但只有三個連線。那麼執行緒b4就可以在handle_other_thing()做一些其他事情。
下面討論乙個很重要的效率問題:
在上述while迴圈裡面,epoll_or_selec_func()中的epoll_wait/poll/select等函式一般設定了乙個超時時間。如果設定超時時間為0,那麼在沒有任何網路io時間和其他任務處理的情況下,這些工作執行緒實際上會空轉,白白地浪費cpu時間片。如果設定的超時時間大於0,在沒有網路io時間的情況,epoll_wait/poll/select仍然要掛起指定時間才能返回,導致handle_other_thing()不能及時執行,影響其他任務不能及時處理,也就是說其他任務一旦產生,其處理起來具有一定的延時性。這樣也不好。那如何解決該問題呢?
其實我們想達到的效果是,如果沒有網路io時間和其他任務要處理,那麼這些工作執行緒最好直接掛起而不是空轉;如果有其他任務要處理,這些工作執行緒要立刻能處理這些任務而不是在epoll_wait/poll/selec掛起指定時間後才開始處理這些任務。
我們採取如下方法來解決該問題,以linux為例,不管epoll_fd上有沒有檔案描述符fd,我們都給它繫結乙個預設的fd,這個fd被稱為喚醒fd。當我們需要處理其他任務的時候,向這個喚醒fd上隨便寫入1個位元組的,這樣這個fd立即就變成可讀的了,epoll_wait()/poll()/select()函式立即被喚醒,並返回,接下來馬上就能執行handle_other_thing(),其他任務得到處理。反之,沒有其他任務也沒有網路io事件時,epoll_or_select_func()就掛在那裡什麼也不做。
這個喚醒fd,在linux平台上可以通過以下幾種方法實現:
管道pipe,建立乙個管道,將管道繫結到epoll_fd上。需要時,向管道一端寫入乙個位元組,工作執行緒立即被喚醒。
linux 2.6新增的eventfd:
int
eventfd
(unsigned
int initval,
int flags)
;
步驟也是一樣,將生成的eventfd繫結到epoll_fd上。需要時,向這個eventfd上寫入乙個位元組,工作執行緒立即被喚醒。
3. 第三種方法最方便。即linux特有的socketpair,socketpair是一對相互連線的socket,相當於伺服器端和客戶端的兩個端點,每一端都可以讀寫資料。
int
socketpair
(int domain,
int type,
int protocol,
int sv[2]
);
呼叫這個函式返回的兩個socket控制代碼就是sv[0],和sv[1],在乙個其中任何乙個寫入位元組,在另外乙個收取位元組。
將收取的位元組的socket繫結到epoll_fd上。需要時,向另外乙個寫入的socket上寫入乙個位元組,工作執行緒立即被喚醒。
如果是使用socketpair,那麼domain引數一定要設定成afx_unix。
由於在windows,select函式只支援檢測socket這一種fd,所以windows上一般只能用方法3的原理。而且需要手動建立兩個socket,然後乙個連線另外乙個,將讀取的那一段繫結到select的fd上去。這在寫跨兩個平台**時,需要注意的地方。
C 伺服器端編碼心得
很久以前寫的,最近來了兩個做c 的兄弟,感覺有必要共享一下 1.用斷言巨集來檢測程式的邏輯錯誤。分析一下程式core掉的原因,絕大部分是因為空指標或者無效指標引發的,而絕大部空指什和無效指標是因為程式的邏輯錯誤導至的。因此,在除錯版中用斷言巨集來檢測邏輯錯誤,是乙個很有效的方式。以下是我程式裡面的一...
關於伺服器端程式設計的程式除錯心得
對某些需要在編譯以後放到伺服器端或者集群執行的程式,在沒有除錯工具的情況下,解決問題的方法主要是通過輸出語句 將info輸出到日誌中 分為以下步奏 1 對問題的定位 將程式分為幾大邏輯,根據順序逐個大邏輯進行測試 不要放過任何一部分,不要認為某個部分一定不會出錯,往往出錯的就是這部分 然後逐步縮小問...
多執行緒伺服器端的實現
1.單cpu系統中如何同時執行多個程序?請解釋該過程中發生的上下文切換。答 只有1個cpu cpu的運算裝置core 的系統中不是也可以同時執行多個程序嗎?只是因為系統將cpu時間分成了多個微小的塊後分配給了多個程序。為了分時使用cpu,需要 上下文切換 的過程。2.為何執行緒上下文切換更快?執行緒...