一:驚群的定義
驚群是多程序多執行緒程式設計中的乙個常見問題,就是當多個程序和執行緒在同時阻塞等待同乙個事件時,如果這個事件發生,會喚醒所有的程序,但最終只可能有乙個程序/執行緒對該事件進行處理,其他程序/執行緒會在失敗後重新休眠,這種效能浪費就是驚群。
二:accept驚群
在使用多程序處理客戶端-伺服器連線時,往往會出現驚群現象,即在主程序listen之後呼叫fork建立多個子程序之後,這些子程序由於等待客戶端的connect鏈結而處於睡眠狀態,而每次只有乙個鏈結進入,核心會所有的子程序來處理,往往只有乙個程序能夠獲得鏈結,而且他的子程序又要重新回到睡眠狀態。
以前舊版本的linux沒有解決accept驚群,目前的linux已經解決了accept驚群。下面展示實驗部分內容。
server.c檔案如下所示:
#include
#include
#include
#include
#include
#include
#define process_num 3
int main()
} } int status;
wait(&status);
return 0;
}為了簡單起見,主程序只建立兩三個子程序,共三個子程序來處理客戶端鏈結。
client.c檔案如下所示:
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv)
if( argc > 2 ) //函式傳參,可以更改伺服器的埠號
int sockfd;
sockfd = socket(af_inet, sock_stream, 0);// 建立通訊端點:套接字
if(sockfd < 0)
// 設定伺服器位址結構體
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr)); // 初始化伺服器位址
server_addr.sin_family = af_inet; // ipv4
server_addr.sin_port = htons(port); // 埠
inet_pton(af_inet, server_ip, &server_addr.sin_addr.s_addr); // ip
// 主動連線伺服器
int err_log = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(err_log != 0)
char send_buf[512] = ;
printf("send data to %s:%d\n",server_ip,port);
while(1)
close(sockfd);
return 0;
}接下來編譯執行server,然後使用ps –ef|grep ./server觀察
jack 4341 4340 0 05:36 pts/6 00:00:00 ./server
jack 4342 4340 0 05:36 pts/6 00:00:00 ./server
jack 4343 4340 0 05:36 pts/6 00:00:00 ./server
接下來觀察 /proc/[pid]/status目錄下的內容結果如下所示:
pidvoluntary_ctxt_switches
nonvoluntary_ctxt_switches
43411 0
43421 0
43431 0
其中voluntary_ctxt_switches 代表主動放棄cpu的次數,nonvoluntary_ctxt_switches: 代表被動放棄cpu的次數,接著執行./client,在./server終端中出現process 4341 accept success!然後重新觀察/proc/[pid]/status目錄,結果如下:
pidvoluntary_ctxt_switches
nonvoluntary_ctxt_switches
43412 0
43421 0
43431 0
結果表明只有4341號程序獲得了一次cpu,並且處理了connect之後又主動放棄了cpu,其餘子程序仍然處於睡眠狀態,並未發生上下文切換。
目前核心解決了accept的驚群問題,並未解決epoll驚群,該部落格**有epoll驚群原始碼,和為何不解決epoll驚群的原因。大家可以按照上述方法去驗證一下epoll驚群。
三 核心如何解決accept驚群
因為涉及到核心,水平有限,不能進行實驗驗證。只能從原理上大概分析一下。
通常情況下,我們首先能想到的就是進行加鎖操作,這樣一來,獲得鎖的程序阻塞與accept呼叫,而未獲得鎖的程序阻塞於鎖的獲取。這樣可以有一定的效能提高,但是當unlock(),釋放鎖之後,同樣會面臨鎖的爭搶,也會出現驚群現象。
核心開發者增加了乙個「互斥等待」選項。乙個互斥等待的行為與睡眠基本類似,主要的不同點在於:
1)當乙個等待佇列入口有 wq_flag_excluseve 標誌置位, 它被新增到等待佇列的尾部. 沒有這個標誌的入口項, 相反, 新增到開始.
2)當 wake_up 被在乙個等待佇列上呼叫時, 它在喚醒第乙個有 wq_flag_exclusive 標誌的程序後停止。
也就是說,對於互斥等待的行為,比如如對乙個listen後的socket描述符,多執行緒阻塞accept時,系統核心只會喚醒所有正在等待此時間的佇列 的第乙個,佇列中的其他人則繼續等待下一次事件的發生,這樣就避免的多個執行緒同時監聽同乙個socket描述符時的驚群問題。
其實驚群的根本原因在於資源競爭,假如核心可以將資源競爭改為資源分配,這樣核心就有了主動權,驚群問題就可以得到緩解。一般的設計可以在父程序中進行accept呼叫,然後將已連線套接字傳遞給某個子程序。
accpet驚群和epoll驚群現象
epoll的驚群現象解決。想一想nginx解決的應該時epoll的驚群問題具體 網上有就不貼出。得到鎖的可以將accpet放進自己的epoll中然後。沒有得到的移出去 在思考這個問題之前,我們應該以前對前面所講幾點有所了解,即先弄清楚問題的背景,並能自己復現出來,而不僅僅只是看書或部落格,然後再來看...
epoll 群驚現象
遇到問題 手頭原來有乙個單程序的linux epoll伺服器程式,近來希望將它改寫成多程序版本,主要原因有 在服務高峰期間 併發的 網路請求非常海量,目前的單程序版本的程式有點吃不消 單程序時只有乙個迴圈先後處理epoll wait 到的事件,使得某些不幸排隊靠後的socket fd的事件處理不及時...
epoll驚群測試
3.1.1 核心版本3.10測試結果 兩個程序均被喚醒 3.1.2 核心版本4.18測試結果 僅有乙個程序被喚醒。如果在epoll wait之後sleep一秒,結果如下 兩個程序均被喚醒,總共收到乙個包。小結 較新版本的核心做了一些優化,不一定會喚醒所有的程序。核心版本為3.10和4.18結果一致,...