基於epoll實現簡單的web伺服器

2021-09-20 09:04:09 字數 3449 閱讀 3242

epoll 是 linux 平台下特有的一種 i/o 復用模型實現,於 2002 年在 linux kernel 2.5.44 中被引入。在 epoll 之前,unix/linux 平台下的 i/o 復用模型包含 select 和 poll 兩個系統呼叫。隨著網際網路的發展,網際網路的使用者量越來越大,c10k 問題出現。基於 select 和 poll 編寫的網路服務已經不能滿足不能滿足使用者的需求了,業界迫切希望更高效的系統呼叫出現。在此背景下,freebsd 的 kqueue 和 linux 的 epoll 被研發了出來。kqueue 和 epoll 的出現,終結了 c10k 問題,c10k 問題就此作古。

在 linux 中,epoll 並不是乙個系統呼叫,而是 epoll_create、epoll_ctl 和 epoll_wait 三個系統呼叫的統稱。關於這三個系統呼叫的細節,這裡就不說明了,大家可以自己去查 man-page。接下來,我們來直接看乙個例子,這個例子基於 epoll 和 tinyhttpd 實現了乙個 i/o 復用版的 http server。在上**前,我們先來演示這個玩具版 http server 的效果。

上面就是玩具版 http server 的執行效果了,看起來還行。在我第一次把它成功跑起來的時候,感覺很奇妙。好了,看完效果,接下來看**吧,如下:

// 根據 cpu 數量建立子程序,為了演示「驚群現象」,這裡多建立一些子程序

for (int i = 0; i < cpu_core_num * 2; i++)

}while (1)

return0;}

void process(int listen_fd)

while(1)

for(int i = 0; i < ready_fd_num; i++)

// 設定 conn_fd 為非阻塞

if (fcntl(conn_fd, f_setfl, fcntl(conn_fd, f_getfd, 0) | o_nonblock) == -1)

ev.data.fd = conn_fd;

ev.events = epollin;

if (epoll_ctl(epoll_fd, epoll_ctl_add, conn_fd, &ev) == -1)

printf("[pid %d] 收到來自 %s:%d 的請求

\n", getpid(), inet_ntoa(client_addr.sin_addr), client_addr.sin_port);

} else

if (events[i].events & epollin) else

if (events[i].events & epollerr) }}

}void handle_subprocess_exit(int signo)

上面的**有點長,不過還好,基本上都是模板**,沒什麼特別複雜的邏輯。希望大家耐心看一下。

上面的**基於epoll + 多程序的方式實現,開始,主程序會通過系統呼叫獲取 cpu 核心數,然後根據核心數建立子程序。為了演示「驚群現象」,這裡多建立了一倍的子程序。關於驚群現象,下一章會講到,大家先別急哈。建立好子程序後,主程序不需再做什麼事了,核心邏輯都會在子執行緒中執行。首先,每個子程序都會呼叫 epoll_create 在核心建立 epoll 例項,然後再通過 epoll_ctl 將 listen_fd 註冊到 epoll 例項中,由核心進行監控。最後,再呼叫 epoll_wait 等待感興趣的事件發生。當 listen_fd 中有新的連線時,epoll_wait 會返回。此時子程序呼叫 accept 接受連線,並把客戶端 socket 註冊到 epoll 例項中,等待 epollin 事件發生。當該事件發生後,即可接受資料,並根據 http 請求資訊返回相應的頁面了。

這裡說明一下,上面**中處理 http 請求的邏輯是寫在 tinyhttpd 專案中的,tinyhttpd 是乙個只有 500 行左右的超輕量型http server,很適合學習使用。為了適應需求,我對其原始碼進行了一定的修改,並新增了一些注釋。本章的測試**已經放到了 github 上,需要的同學自取,傳送門 -> epoll_multiprocess_server.c。

「驚群現象」是指併發環境下,多執行緒或多程序等待同乙個 socket 事件,當這個事件發生時,多執行緒/多程序被同時喚醒,這就是「驚群現象」。對應上面的**,多個子程序通過呼叫 epoll_wait 等待 listen_fd 上某個事件發生。當有新連線進來時,多個程序會被同時喚醒去處理這個事件。但最終只有乙個程序可以去處理事件,其他程序重新進入等待狀態。使用上面的**可以演示驚群現象,如下:

從上圖可以看出,當 listen_fd 上有新連線事件發生時,程序19571和19573被喚醒。但最終程序19573成功處理了新連線事件,程序19571則失敗了。

驚群現象會影響伺服器效能,因為多個程序被喚醒,但最終只有乙個程序可以成功處理事件。而 cpu 需要為乙個事件的發生排程數個程序,因此會浪費 cpu 資源。

對於驚群現象,處理的思路一般有兩種。一種是像 lighttpd 那樣,無視驚群。另一種是像 nginx 那樣,使用全域性鎖避免驚群。簡單起見,本文測試**採用的是 lighttpd 的處理方式,即無視驚群。對於這兩種思路的細節,由於本人未讀過兩個開源軟體的**,這裡就不多說了。如果大家有興趣,可以參考網上的一些博文。

本作品採用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。

基於epoll實現簡單的web伺服器

epoll 是 linux 平台下特有的一種 i o 復用模型實現,於 2002 年在 linux kernel 2.5.44 中被引入。在 epoll 之前,unix linux 平台下的 i o 復用模型包含 select 和 poll 兩個系統呼叫。隨著網際網路的發展,網際網路的使用者量越來越...

基於 epoll 實現 web 伺服器

1.簡介 epoll 是 linux 平台下特有的一種 i o 復用模型實現,於 2002 年在 linux kernel 2.5.44 中被引入。在 epoll 之前,unix linux 平台下的 i o 復用模型包含 select 和 poll 兩個系統呼叫。隨著網際網路的發展,網際網路的使用...

基於epoll的web伺服器

我們在大量併發的時候epoll有高的效能,所以我們選擇用他去做乙個bs模型的web伺服器 web伺服器 網路上是epoll併發,在解析http命令是下面的思路 具體的主要看 的思路 去除前面的 int n1 0 char pname name if strlen pname 1 else 通過對字元...