1. 程式人生 > >通過完整示例來理解如何使用 epoll

通過完整示例來理解如何使用 epoll

網路伺服器通常使用一個獨立的程序或執行緒來實現每個連線。由於高效能應用程式需要同時處理大量的客戶端,這種方法就不太好用了,因為資源佔用和上下文切換時間等因素影響了同時處理大量客戶端的能力。另一種方法是在一個執行緒中使用非阻塞 I/O,以及一些就緒通知方法,即當你可以在一個套接字上讀寫更多資料的時候告訴你。

本文介紹了 Linux 的 epoll(7) 機制,它是 Linux 最好的就緒通知機制。我們用 C 語言編寫了示例程式碼,實現了一個完整的 TCP 伺服器。 我假設您有一定 C 語言程式設計經驗,知道如何在 Linux 上編譯和執行程式,並且可以閱讀手冊檢視各種需要的 C 函式。

epoll 是在 Linux 2.6 中引入的,在其他類 UNIX 作業系統上不可用。它提供了一個類似於 select

(2) 和 poll(2) 函式的功能:

  • select(2) 一次可以監測 FD_SETSIZE數量大小的描述符,FD_SETSIZE 通常是一個在 libc 編譯時指定的小數字。
  • poll(2) 一次可以監測的描述符數量並沒有限制,但撇開其它因素,我們每次都不得不檢查就緒通知,線性掃描所有通過描述符,這樣時間複雜度為 O(n)而且很慢。

epoll 沒有這些固定限制,也不執行任何線性掃描。因此它可以更高效地執行和處理大量事件。

一個 epoll 例項可由 epoll_create(2) 或 epoll_create1(2) (它們採用不同的引數)建立,它們的返回值是一個 epoll 例項。epoll_ctl

(2) 用來新增或刪除監聽 epoll 例項的描述符。epoll_wait(2) 用來等待被監聽的描述符事件,一直阻塞到事件可用。更多資訊請參見相關手冊。

當描述符被新增到 epoll 例項時,有兩種模式:電平觸發和邊緣觸發(譯者注:借鑑電路里面的概念)。當你使用電平觸發模式,並且資料可以被讀取,epoll_wait(2) 函式總是會返回就緒事件。如果你還沒有讀完資料,並且再次在 epoll 例項上呼叫 epoll_wait(2) 函式監聽這個描述符,由於還有資料可讀,那麼它會再次返回這個事件。在邊緣觸發模式下,你只會得到一次就緒通知。如果你沒有將資料全部讀走,並且再次在 epoll 例項上呼叫 epoll_wait(2) 函式監聽這個描述符,它就會阻塞,因為就緒事件已經發送過了。

傳遞到 epoll_ctl(2) 的 epoll 事件結構體如下。對每一個被監聽的描述符,你可以關聯到一個整數或者一個使用者資料的指標。

12345678910111213 typedefunionepoll_data{void*ptr;intfd;__uint32_t   u32;__uint64_t   u64;}epoll_data_t;structepoll_event{__uint32_t   events;/* Epoll events */epoll_data_t data;/* User data variable */};  

現在我們開始寫程式碼。我們將實現一個小的 TCP 伺服器,將傳送到這個套接字的所有資料列印到標準輸出上。首先編寫一個 create_and_bind() 函式,用來建立和繫結 TCP 套接字:

123456789101112131415161718192021222324252627282930313233343536373839404142434445 staticintcreate_and_bind(char*port){structaddrinfo hints;structaddrinfo*result,*rp;ints,sfd;memset(&hints,0,sizeof(structaddrinfo));hints.ai_family=AF_UNSPEC;/* Return IPv4 and IPv6 choices */hints.ai_socktype=SOCK_STREAM;/* We want a TCP socket */hints.ai_flags=AI_PASSIVE;/* All interfaces */s=getaddrinfo(NULL,port,&hints,&result);if(s!=0){fprintf(stderr,"getaddrinfo: %sn",gai_strerror(s));return-1;}for(rp=result;rp!=NULL;rp=rp->ai_next){sfd=socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol);if(sfd==-1)continue;s=bind(sfd,rp->ai_addr,rp->ai_addrlen);if(s==0){/* We managed to bind successfully! */break;}close(sfd);}if(rp==NULL){fprintf(stderr,"Could not bindn");return-1;}freeaddrinfo(result);returnsfd;}

create_and_bind() 包含一個標準程式碼塊,用一種可移植的方式來獲得 IPv4 和 IPv6 套接字。它接受一個 port 字串引數,可由 argv[1] 傳遞。getaddrinfo(3) 函式返回一堆 addrinfo 結構體到 result變數中,它們與傳入的 hints引數是相容的。 addrinfo結構體像這樣:

1234567891011 structaddrinfo{intai_flags;intai_family;intai_socktype;intai_protocol;size_t           ai_addrlen;structsockaddr*ai_addr;char*ai_canonname;structaddrinfo*ai_next;};

我們依次遍歷這些結構體並用它們建立套接字,直到可以建立並繫結一個套接字。如果成功了,create_and_bind( ) 返回這個套接字描述符。如果失敗則返回 -1。

下面我們編寫一個函式,用於將套接字設定為非阻塞狀態。make_socket_non_blocking() 為傳入的 sfd 引數設定 O_NONBLOCK 標誌:

12345678910111213141516171819202122 staticintmake_socket_non_blocking(intsfd){intflags,s;flags=fcntl(sfd,F_GETFL,0);if(flags==-1){perror("fcntl");return-1;}flags|=O_NONBLOCK;s=fcntl(sfd,F_SETFL,flags);if(s==-1){perror("fcntl");return-1;}return0;}

現在說說 main() 函式吧,它裡面包含了這個程式的事件迴圈。這是主要程式碼:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182

相關推薦

通過完整示例理解如何使用 epoll

網路伺服器通常使用一個獨立的程序或執行緒來實現每個連線。由於高效能應用程式需要同時處理大量的客戶端,這種方法就不太好用了,因為資源佔用和上下文切換時間等因素影響了同時處理大量客戶端的能力。另一種方法是在一個執行緒中使用非阻塞 I/O,以及一些就緒通知方法,即當你可以在一個套接字上讀寫更多資料的時候告訴你

通過簡單例子理解先驗分佈、後驗分佈、似然估計&&貝葉斯公式

這幾個概念可以用“原因的可能性”和“結果的可能性”的“先後順序”及“條件關係”來理解。下面舉例: 隔壁老王要去10公里外的一個地方辦事,他可以選擇走路,騎自行車或者開車,並花費了一定時間到達目的地。在這個事件中,可以

通過經典題目理解多種閉包實現方式

在之前的文章中也有提及此題,這裡更詳細的說一下。 需求:生成十個按鈕,內容分別是1~10,點選每個按鈕輸出當前的序號,即點選1輸出1。 for (var i = 1; i <= 1

CAS 5.1.x 的搭建和使用(三)—— 通過官方示例熟悉客戶端搭建

原文地址:http://www.cnblogs.com/flying607/p/7601177.html 這裡主要有兩件事要做: 第一個就是給客戶端的jre新增和服務端對應的證書 之前我們已經生成了一個keystore,我們就直接使用這個keystore生成一個證書,命

通過反編譯字節碼理解 Java 枚舉

enum枚舉的聲明很簡單, 像 enum Gender { Male, Female }, 其余事情就是 Java 編譯器幫我們幹的了,所以 enum 也就是一塊語法糖。有了枚舉確實是很方便,避免了傳統常量的無範圍性。那麽編譯器到底在後面做了什麽呢?以及理解了這個之後我們可以怎麽去使用 Java 的枚舉, 下

Service 服務發現的兩種方式-通過案例理解

系統 balance 輕松 分配 添加 ber mes 另一個 amp 1.環境變量 在創建一個Pod時,kubelet在該Pod的所有容器中為當前所有Service添加一系列環境變量。 例如,已存在名稱為“redis-master”的Service,它對外暴露6379的T

C# 通過 Observer觀察者 設計模式 理解 抽象類 和 介面 應用在什麼地方

什麼時候用抽象類?什麼時候用介面?怎麼理解抽象類?怎麼理解介面? 一、百度解釋抽象類和介面的區別 總而言之就是一句話:抽象類可以包含具體實現,介面只能包含定義。 實現介面時必須實現介面定義的方法等,抽象類中如果給方法加上了“abstract”,那麼這個方法也需要在繼承後實現這個

yield 的本質-通過send理解

1.yield 的本質:將運算以yield中分界線一分為二來劃分區段1 可以理解為: yield後面的程式碼挪到yield之前才是一個完整的執行過程 yield後面程式碼放在函式的開始 就類似於return了 2.send的本質: n

以太坊dapp智慧合約示例——通過對映使用者玩彩票

在前面的一些文章中,我們學習了以太坊智慧合約程式設計的基礎知識。現在我們來建立一個實戰以太坊dapp案例:彩票。 我們彩票案例的目的是多個玩家能夠通過傳送錢參與彩票。玩家傳送的錢越多,他贏得所有資金的機會就越大。當彩票的運營發行者決定關閉彩票後,就會選擇一個優

Dagger2使用攻略---通過生成的程式碼理解相關概念

原文網址:http://blog.csdn.net/qq_17766199/article/details/50606011 Dagger2使用攻略 Dagger 2 是 Square 的 Dagger 分支,是一種依賴注入框架。目前由 Googl

通過示例學習ES2016, 2017, 2018的新特性

譯者按: 本文系統地總結了所有的新特性,並用淺顯的例子解釋。 為了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原作者所有,翻譯僅用於學習。 小編推薦:Fundebug專注於JavaScript、微信小程式、微信小遊戲,Node.js和Java線上b

通過leetcode中的兩數之和耗時最少的程式理解查詢其實可以不用先賦值

昨天剛刷了個leetcode簡單演算法兩數之和,結果發現自己第一遍耗時188ms(純屬暴力破解),如下為我的程式碼:class Solution { public: vector<int> twoSum(vector<int>& num

曹工說Redis原始碼(4)-- 通過redis server原始碼理解 listen 函式中的 backlog 引數

文章導航 Redis原始碼系列的初衷,是幫助我們更好地理解Redis,更懂Redis,而怎麼才能懂,光看是不夠的,建議跟著下面的這一篇,把環境搭建起來,後續可以自己閱讀原始碼,或者跟著我這邊一起閱讀。由於我用c也是好幾年以前了,些許錯誤在所難免,希望讀者能不吝指出。 曹工說Redis原始碼(1)-- redi

Android與H5互調(通過實例了解Hybrid App)

ext 傳感器 顯示 相同 blank show ima 一次 點擊 前些日子,Android原生開發將被取締的吵得火熱,JavaScript是能做一個完全的APP,但只使用JavaScript做出來的APP也不會牛逼到哪裏去。最好的是混合(Hybrid)開發,在需要的

C# 用實例理解IComparable和IComparer

大於 sum summary spa 必須 理解 實例 eap bject 通過Array的Sort方法來理解的 Sort方法要 通過對象去繼承IComparable接口來實現排序(當然也有其它辦法),我想入門這可能就是對這句話有點不理解,在下面會有註釋 using

磁盤I/O高居不下,通過什麽查看占用I/O的進程?

監控命令 iotop命令是一個用來監視磁盤I/O使用狀況的top類工具。iotop具有與top相似的UI,其中包括PID、用戶、I/O、進程等相關信息。Linux下的IO統計工具如iostat,nmon等大多數是只能統計到per設備的讀寫情況,如果你想知道每個進程是如何使用IO的就比較麻煩,使用iotop命令

通過traceur.js編寫ES6

char 編寫 scrip charset rac href mpat option script 通過traceur.js來編寫ES6 <!DOCTYPE html><html><head> <meta charset="u

黃錦宣:用戶思維,通過用戶獲取用戶

互聯網 社會用戶思維,應該歸類於互聯網思維,簡單直接的命名,基本可以理解為用戶的思維。 其實其闡述的是要以用戶的思維為核心,來運營商業項目,通過站在用戶的立場去思考運營,通過了解用戶的切身的需求,量身訂做出能解決用戶需求的產品。 那麽這裏的了解更多體現的應該是數據型了解,需要大量的用戶數據去了解,這個過程的

動畫效果 詳情請看 jQuery api 動畫收放 slideUp slideDown 通過id class 設置 jQuery實現效果

color ada htm border order style borde query list <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8">

C語言編程之--scanf()函數與getchar()函數搭配理解C程序的輸入緩存(buffer)

使用 ges border urn alt color 數值 amp tps 博主最近在學習C語言編程,在書中的代碼示例中出現了下面的代碼段: #include<stdio.h> int main() { char a[5]; int i; printf("