1. 程式人生 > >Linux網路程式設計——I/O複用函式之epoll

Linux網路程式設計——I/O複用函式之epoll

https://blog.csdn.net/lianghe_work/article/details/46544567

一、epoll概述

epoll 是在 2.6 核心中提出的,是之前的 select() 和 poll() 的增強版本。相對於 select() 和 poll() 來說,epoll 更加靈活,沒有描述符限制。epoll 使用一個檔案描述符管理多個描述符,將使用者關係的檔案描述符的事件存放到核心的一個事件表中,這樣在使用者空間和核心空間的 copy 只需一次。

二、epoll操作過程需要的四個介面函式

四介面函式分別是:

  1. #include <sys/epoll.h>
  2. int epoll_create
    (int size)
    ;
  3. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  4. int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  5. int close(int epfd);

int epoll_create(int size);

功能:

該函式生成一個epoll專用的檔案描述符(其餘的介面函式一般都用使用這個專用的檔案描述符)

引數:

size: 用來告訴核心這個監聽的數目一共有多大,引數 size 並不是限制了 epoll 所能監聽的描述符最大個數,只是對核心初始分配內部資料結構的一個建議。自從 linux 2.6.8 之後,size 引數是被忽略的,也就是說可以填只有大於 0 的任意值

。需要注意的是,當建立好 epoll 控制代碼後,它就是會佔用一個 fd 值,在 linux 下如果檢視 /proc/ 程序 id/fd/,是能夠看到這個 fd 的,所以在使用完 epoll 後,必須呼叫 close() 關閉,否則可能導致 fd 被耗盡

返回值:

成功:epoll 專用的檔案描述符
失敗:-1

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能:

epoll 的事件註冊函式,它不同於 select() 是在監聽事件時告訴核心要監聽什麼型別的事件,而是在這裡先註冊要監聽的事件型別

引數:

epfd: epoll 專用的檔案描述符,epoll_create()的返回值

op: 表示動作,用三個巨集來表示:

EPOLL_CTL_ADD:註冊新的 fd 到 epfd 中;
EPOLL_CTL_MOD:修改已經註冊的fd的監聽事件;
EPOLL_CTL_DEL:從 epfd 中刪除一個 fd;

fd: 需要監聽的檔案描述符

event: 告訴核心要監聽什麼事件,struct epoll_event 結構如下:

  1. // 儲存觸發事件的某個檔案描述符相關的資料(與具體使用方式有關)
  2. typedef union epoll_data {
  3. void *ptr;
  4. int fd;
  5. __uint32_t u32;
  6. __uint64_t u64;
  7. } epoll_data_t;
  8. // 感興趣的事件和被觸發的事件
  9. struct epoll_event {
  10. __uint32_t events; /* Epoll events */
  11. epoll_data_t data; /* User data variable */
  12. };
events 可以是以下幾個巨集的集合:

EPOLLIN :表示對應的檔案描述符可以讀(包括對端 SOCKET 正常關閉);

EPOLLOUT:表示對應的檔案描述符可以寫;

EPOLLPRI:表示對應的檔案描述符有緊急的資料可讀(這裡應該表示有帶外資料到來);

EPOLLERR:表示對應的檔案描述符發生錯誤;

EPOLLHUP:表示對應的檔案描述符被結束通話;

EPOLLET :將 EPOLL 設為邊緣觸發(Edge Triggered)模式,這是相對於水平觸發(Level Triggered)來說的。

EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個 socket 的話,需要再次把這個 socket 加入到 EPOLL 佇列裡

返回值:

成功:0
失敗:-1

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

功能:
等待事件的產生,收集在 epoll 監控的事件中已經發送的事件,類似於 select() 呼叫。
引數:
epfd: epoll 專用的檔案描述符,epoll_create()的返回值
events: 分配好的 epoll_event 結構體陣列,epoll 將會把發生的事件賦值到events 陣列中(events 不可以是空指標,核心只負責把資料複製到這個 events 陣列中,不會去幫助我們在使用者態中分配記憶體)。
maxevents: maxevents 告之核心這個 events 有多大 。
timeout: 超時時間,單位為毫秒,為 -1 時,函式為阻塞
返回值:
成功:返回需要處理的事件數目,如返回 0 表示已超時。
失敗:-1


epoll 對檔案描述符的操作有兩種模式:LT(level trigger)和 ET(edge trigger)。LT 模式是預設模式,LT 模式與 ET 模式的區別如下:

LT 模式:當 epoll_wait 檢測到描述符事件發生並將此事件通知應用程式,應用程式可以不立即處理該事件。下次呼叫 epoll_wait 時,會再次響應應用程式並通知此事件。
ET 模式:當 epoll_wait 檢測到描述符事件發生並將此事件通知應用程式,應用程式必須立即處理該事件。如果不處理,下次呼叫 epoll_wait 時,不會再次響應應用程式並通知此事件。

ET 模式在很大程度上減少了 epoll 事件被重複觸發的次數,因此效率要比 LT 模式高。epoll 工作在 ET 模式的時候,必須使用非阻塞套介面,以避免由於一個檔案控制代碼的阻塞讀/阻塞寫操作把處理多個檔案描述符的任務餓死

int close(int epfd);

在用完之後,記得用close()來關閉這個創建出來的epoll控制代碼

三、epoll示例:

接下來我們epoll實現udp同時收發資料

  1. #include <string.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/select.h>
  6. #include <sys/time.h>
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <sys/epoll.h>
  11. int main(int argc,char *argv[])
  12. {
  13. int udpfd = 0;
  14. int ret = 0;
  15. struct sockaddr_in saddr;
  16. struct sockaddr_in caddr;
  17. bzero(&saddr,sizeof(saddr));
  18. saddr.sin_family = AF_INET;
  19. saddr.sin_port = htons(8000);
  20. saddr.sin_addr.s_addr = htonl(INADDR_ANY);
  21. bzero(&caddr,sizeof(caddr));
  22. caddr.sin_family = AF_INET;
  23. caddr.sin_port = htons(8000);
  24. //建立套接字
  25. if( (udpfd = socket(AF_INET,SOCK_DGRAM, 0)) < 0)
  26. {
  27. perror("socket error");
  28. exit(-1);
  29. }
  30. //套接字埠綁字
  31. if(bind(udpfd, (struct sockaddr*)&saddr, sizeof(saddr)) != 0)
  32. {
  33. perror("bind error");
  34. close(udpfd);
  35. exit(-1);
  36. }
  37. printf("input: \"sayto 192.168.220.X\" to sendmsg to somebody\033[32m\n");
  38. struct epoll_event event; // 告訴核心要監聽什麼事件
  39. struct epoll_event wait_event;
  40. int epfd = epoll_create(10); // 建立一個 epoll 的控制代碼,引數要大於 0, 沒有太大意義
  41. if( -1 == epfd ){
  42. perror ("epoll_create");
  43. return -1;
  44. }
  45. event.data.fd = 0; // 標準輸入
  46. event.events = EPOLLIN; // 表示對應的檔案描述符可以讀
  47. // 事件註冊函式,將標準輸入描述符 0 加入監聽事件
  48. ret = epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event);
  49. if(-1 == ret){
  50. perror("epoll_ctl");
  51. return -1;
  52. }
  53. event.data.fd = udpfd; // 有名管道
  54. event.events = EPOLLIN; // 表示對應的檔案描述符可以讀
  55. // 事件註冊函式,將有udp描述符udpfd 加入監聽事件
  56. ret = epoll_ctl(epfd, EPOLL_CTL_ADD, udpfd, &event);
  57. if(-1 == ret){
  58. perror("epoll_ctl");
  59. return -1;
  60. }
  61. while(1)
  62. {
  63. // 監視並等待多個檔案(標準輸入,udp套接字)描述符的屬性變化(是否可讀)
  64. // 沒有屬性變化,這個函式會阻塞,直到有變化才往下執行,這裡沒有設定超時
  65. ret = epoll_wait(epfd, &wait_event, 2, -1);
  66. write(1,"UdpQQ:",6);
  67. if(ret == -1){ // 出錯
  68. close(epfd);
  69. perror("epoll");
  70. }
  71. else if(ret > 0){ // 準備就緒的檔案描述符
  72. char buf[100] = {0};
  73. if( ( 0 == wait_event.data.fd )
  74. && ( EPOLLIN == wait_event.events & EPOLLIN ) ){ // 標準輸入
  75. fgets(buf, sizeof(buf), stdin);
  76. buf[strlen(buf) - 1] = '\0';
  77. if(strncmp(buf, "sayto", 5) == 0)
  78. {
  79. char ipbuf[16] = "";
  80. inet_pton(AF_INET, buf+6, &caddr.sin_addr);//給addr套接字地址再賦值.
  81. printf("\rsay to %s\n",inet_ntop(AF_INET,&caddr.sin_addr,ipbuf,sizeof(ipbuf)));
  82. continue;
  83. }
  84. else if(strcmp(buf, "exit")==0)
  85. {
  86. close(udpfd);
  87. exit(0);
  88. }
  89. sendto(udpfd, buf, strlen(buf),0,(struct sockaddr*)&caddr, sizeof(caddr));
  90. }
  91. else if( ( udpfd == wait_event.data.fd )
  92. && ( EPOLLIN == wait_event.events & EPOLLIN )){ //udp套接字
  93. struct sockaddr_in addr;
  94. char ipbuf[INET_ADDRSTRLEN] = "";
  95. socklen_t addrlen = sizeof(addr);
  96. bzero(&addr,sizeof(addr));
  97. recvfrom(udpfd, buf, 100, 0, (struct sockaddr*)&addr, &addrlen);
  98. printf("\r\033[31m[%s]:\033[32m%s\n",inet_ntop(AF_INET,&addr.sin_addr,ipbuf,sizeof(ipbuf)),buf);
  99. }
  100. }
  101. else if(0 == ret){ // 超時
  102. printf("time out\n");
  103. }
  104. }
  105. return 0;
  106. }


執行結果:


原始碼下載:


相關推薦

Linux網路程式設計——I/O函式epoll

https://blog.csdn.net/lianghe_work/article/details/46544567一、epoll概述epoll 是在 2.6 核心中提出的,是之前的 select() 和 poll() 的增強版本。相對於 select() 和 poll()

Linux網路程式設計---I/O模型epoll

Linux網路程式設計—I/O複用模型之epoll 1. epoll模型簡介 epoll是Linux多路服用IO介面select/poll的加強版,e對應的英文單詞就是enhancement,中文翻譯為增強,加強,提高,充實的意思。所以epoll模型會顯

Linux網路程式設計——I/Opoll函式

#include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/select.h> #include <sys/time.

網路程式設計-I/O

## I/O模型 Unix下可用的I/O模型有五種: + 阻塞式I/O + 非阻塞式I/O + I/O複用(select和poll、epoll) + 訊號驅動式I/O(SIGIO) + 非同步I/O(POSIX的aio_系列函式) > 詳見Unix網路程式設計卷一第六章 sel

Linux網路程式設計---I/O多路select

1.I/O多路複用(IO multiplexing) 我們之前講了I/O多路複用和其他I/O的區別,在這裡,我們再具體討論下I/O多路複用是怎麼工作? I/O 多路複用技術就是為了解決程序或執行緒阻塞到某個 I/O 系統呼叫而出現的技術,使程序不阻塞於某個特定的 I/O 系統呼叫。

Linux網路程式設計---I/O多路epoll

/* TCP伺服器 用法:./server port */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string

Linux 高效能伺服器程式設計—— I/O epoll

一 核心事件表   epoll不同於select和poll,它是使用一組函式來完成任務;同時,epoll把使用者關心的檔案描述符上的事件放在核心裡的一個事件表中,從而不像select 和 poll每次呼叫都需要重複傳入檔案描述符集或事件集。但是epoll需要一個額外的檔案描述符來表示核

Linux 高效能伺服器程式設計——I/O poll

一:poll系統呼叫   同select相似,也是在指定時間內輪詢一定數量的檔案描述符,以測試其中是否有就緒者。 二:poll函式   1.函式原型: #include<poll.h> int poll(struct pollfd* fds,nfds

Linux高效能伺服器程式設計——I/O select

提出背景     不管是多執行緒,或者多程序,以及執行緒池,程序池。他們都存在一定的效率問題。 1.每個程序或執行緒只能為一個客戶端進行服務,知道該客戶端結束。(如果客戶端在同一時間的訪問數量特別大呢?) 2.當客戶端傳送來資料後,分配執行緒或程序為其服務完後,就要等

Linux----網路程式設計(IOepoll系統呼叫函式

伺服器端epoll.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <assert.h> #include <unistd.h&

18.Linux下的I/Oepoll詳解

為什麼引出epoll? 1.select的缺點 1.select所用到的FD_SET是有限的 /linux/posix_types.h: #define __FD_SETSIZE 102

Linux----網路程式設計(IO中select,poll,epoll之間的區別)

前面學習了select、poll和epoll三組IO複用系統呼叫,現在從向核心傳遞檔案描述符數、核心實現、檢索就緒描述符方式、工作模式和時間複雜度等五個方面比較其中的區別,以明確在實際應用中應該選擇使用哪個。 由於select與poll的特性相似,所以把它們聯絡在一起與ep

I/O模型select函式用法——伺服器開發

現在我們介紹另外一種常用併發伺服器開發的技術——select函式I/O複用模型。 先來介紹select及相關的函式: select函式的作用是監聽指定的多個I/O的檔案描述符,在設定的時間內阻塞,當有一個或者多個I/O埠滿足某個“讀”或者“寫”的條件,則在fd_set型別

高階I/O技術:Epoll的使用及一個完整的C例項

        高效能的網路伺服器需要同時併發處理大量的客戶

Linux----網路程式設計I/Oselect系統呼叫)

io_select_ser.c 1. #include <string.h> 2. #include <assert.h> 3. #include <unistd.h> 4. #include <stdio.h> 5. #in

Linux網路程式設計——tcp併發伺服器(I/Oselect)

與多執行緒、多程序相比,I/O複用最大的優勢是系統開銷小,系統不需要建立新的程序或者執行緒,也不必維護這些執行緒和程序。 程式碼示例: #include <stdio.h> #include <unistd.h> #include <

Linux網路程式設計I/O迴圈伺服器

原文:http://blog.csdn.net/chenjin_zhong/article/details/7270166 1.介紹 在前幾節,我們介紹了迴圈伺服器,併發伺服器. 簡單的迴圈伺服器每次只能處理一個請求,即處理的請求是序列的。而併發伺服器可以通過建立多

Linux I/Oselect函式詳解

置頂 2017年02月12日 20:50:08 難免有錯_ 閱讀數:7438更多 select函式的功能和呼叫順序 使用select函式時統一監視多個檔案描述符的: 1、 是否存在套接字接收資料? 2、 無需阻塞傳輸資料的套接字有哪些? 3、 哪些套接字發生了

網路程式設計I/Oselect的用法

網路程式設計select的用法 select使用流程圖 在網路程式設計中需要新增的程式碼行以及意義 例程 參考文獻及部落格 注:本文對select函式、相關引數及結構體不做解釋 select使用流程

網路程式設計 筆記(八) I/O

select函式呼叫示例 #include <stdio.h> #include <unistd.h> #include <sys/time.h> #include <sys/select.h> #defi