Linux網絡編程---I/O復用模型之epoll

https://blog.csdn.net/men_wen/article/details/53456474

Linux網絡編程—I/O復用模型之epoll

1. epoll模型簡介

epoll是Linux多路服用IO接口select/poll的加強版,e對應的英文單詞就是enhancement,中文翻譯為增強,加強,提高,充實的意思。所以epoll模型會顯著提高程序在大量并發連接中只有少量活躍的情況下的系統CPU利用率。

  • epoll把用戶關心的文件描述符上的時間放在內核的一個事件表中,無需像select和poll那樣每次調用都重復傳入文件描述符集。
  • epoll在獲取事件的時候,無需遍歷整個被監聽的文件描述符集合,而是遍歷那些被內核IO事件異步喚醒而加入ready隊列的描述符集合。

所以,epoll是Linux大規模高并發網絡程序的首選模型。

2.epoll模型的API

epoll使用一組函數來完成任務

2.1 函數epoll_create

創建一個epoll句柄,句柄的英文是handle,相通的意思是把手,把柄。

#include <sys/epoll.h>int epoll_create(int size);
//返回值:若成功,返回一個非負的文件描述符,若出錯,返回-1。
  • 該函數返回一個文件描述符,用來唯一標示內核中這個事件表,sizeof參數提示內核要監聽的文件描述符個數,這與內存大小有關。
  • 返回的文件描述符將是其他所有epoll系統調用的第一個參數,以指定要訪問的內核時間表,所以用該返回的文件描述符相當與其他epoll調用的把手、把柄一樣。

查看進程能夠打開的最大數目的文件描述符

?  ~ cat /proc/sys/fs/file-max
1215126
//該值與內存大小有關

修改最大文件描述符限制

?  ~ sudo vim /etc/security/limits.conf
//重啟生效

2.2 函數epoll_ctl

該函數用來操作epoll的內核事件表

#include <sys/epoll.h>int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
//返回值:若成功,返回0,若出錯返回-1。
  • epfd就是函數epoll_create創建的句柄。
  • op是指定操作類型,有一下三種?
    • EPOLL_CTL_ADD,向epfd注冊fd的上的event
    • EPOLL_CTL_MOD,修改fd已注冊的event
    • EPOLL_CTL_DEL,從epfd上刪除fd的event?
      1. fd是操作的文件描述符
      2. event指定內核要監聽事件,它是struct epoll_event結構類型的指針。epoll_event定義如下:
struct epoll_event {uint32_t     events;      /* Epoll events */epoll_data_t data;        /* User data variable */
};
  • vents成員描述事件類型,將以下宏定義通過位或方式組合

    • EPOLLIN :表示對應的文件描述符可以讀(包括對端SOCKET正常關閉)
    • POLLOUT:表示對應的文件描述符可以寫
    • EPOLLPRI:表示對應的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來)
    • EPOLLERR:表示對應的文件描述符發生錯誤
    • EPOLLHUP:表示對應的文件描述符被掛斷;
    • EPOLLET: 將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對于水平觸發(Level Triggered)來說的
    • EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之后,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列里
  • data用于存儲用戶數據,是epoll_data_t結構類型,該結構定義如下:

typedef union epoll_data {void        *ptr;int          fd;uint32_t     u32;uint64_t     u64;
} epoll_data_t;
  • epoll_data_t是一個聯合體,fd指定事件所從屬的目標文件描述符。ptr可以用來指定fd相關的用戶數據,但兩者不能同時使用。

2.3 函數epoll_wait

函數epoll_wait用來等待所監聽文件描述符上有事件發生

#include <sys/epoll.h>int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
//返回值:若成功,返回就緒的文件描述符個數,若出錯,返回-1,時間超時返回0
  • epfd就是函數epoll_create創建的句柄
  • timeout是超時事件,-1為阻塞,0為立即返回,非阻塞,大于0是指定的微妙
  • events是一個 傳入傳出參數,它是epoll_event結構的指針,用來從內核得到事件的集合
  • maxevents告知內核events的大小,但不能大于epoll_create()時創建的size

3. LT和ET模式

  • LT(Level Triggered,電平觸發):LT模式是epoll默認的工作模式,也是select和poll的工作模式,在LT模式下,epoll相當于一個效率較高的poll。?
    • 采用LT模式的文件描述符,當epoll_wait檢測到其上有事件發生并將此事件通知應用程序后,應用程序可以不立即處理此事件,當下一次調用epoll_wait是,epoll_wait還會將此事件通告應用程序。
  • ET(Edge Triggered,邊沿觸發):當調用epoll_ctl,向參數event注冊EPOLLET事件時,epoll將以ET模式來操作該文件描述符,ET模式是epoll的高效工作模式.?
    • 對于采用ET模式的文件描述符,當epoll_wait檢測到其上有事件發生并將此通知應用程序后,應用程序必須立即處理該事件,因為后續的epoll_wait調用將不在向應用程序通知這一事件。ET模式降低了同意epoll事件被觸發的次數,效率比LT模式高。

4. LT和ET的服務端和客戶端代碼

4.1 服務器端

#include <sys/epoll.h>
#include <fcntl.h>
#include "wrap.h"#define MAX_EVENT_NUM           1024
#define BUFFER_SIZE             10
#define true                    1
#define false                   0int setnonblocking(int fd)
{int old_opt = fcntl(fd, F_GETFD);int new_opt = old_opt | O_NONBLOCK;fcntl(fd, F_SETFD, new_opt);return old_opt;
}//將文件描述符設置為非阻塞的void addfd(int epollfd, int fd, int enable_et)
{struct epoll_event event;event.data.fd = fd;event.events = EPOLLIN;if(enable_et){event.events |= EPOLLET;}epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
//      setnonblocking(fd);
}//將文件描述符fd的EPOLLIN注冊到epollfd指示的epoll內核事件表中,enable_et表示是否對fd啟用ET模式void lt(struct epoll_event *events, int num, int epollfd, int listenfd)
{char buf[BUFFER_SIZE];for(int i = 0; i < num; i++){int sockfd = events[i].data.fd;if(sockfd == listenfd){struct sockaddr_in clientaddr;socklen_t clilen = sizeof(clientaddr);int connfd = Accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);addfd(epollfd, connfd, false);//對connfd使用默認的lt模式}else if(events[i].events & EPOLLIN){//只要socket讀緩存中還有未讀的數據,這段代碼就會觸發printf("event trigger once\n");memset(buf, '\0', BUFFER_SIZE);int ret = recv(sockfd, buf, BUFFER_SIZE-1, 0);if(ret <= 0){Close(sockfd);continue;}printf("get %d bytes of content:%s\n", ret, buf);}else{printf("something else happened\n");}}
}void et(struct epoll_event *event, int num, int epollfd, int listenfd)
{char buf[BUFFER_SIZE];for(int i = 0; i < num; i++){int sockfd = event[i].data.fd;if(sockfd == listenfd){struct sockaddr_in clientaddr;int clilen = sizeof(clientaddr);int connfd = Accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);addfd(epollfd, connfd, true);//多connfd開啟ET模式}else if(event[i].events & EPOLLIN){printf("event trigger once\n");while(1){//這段代碼不會重復觸發,所以要循環讀取數據memset(buf, '\0', BUFFER_SIZE);int ret = recv(sockfd, buf, BUFFER_SIZE-1, 0);if(ret < 0){if((errno == EAGAIN) || (errno == EWOULDBLOCK)){printf("read later\n");break;}Close(sockfd);break;}else if(ret == 0){Close(sockfd);}else{printf("get %d bytes of content:%s\n", ret, buf);}}}else{printf("something else happened \n");}}
}int start_ser(char *ipaddr, char *port)
{int sock = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serveraddr;bzero(&serveraddr, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(port));inet_pton(AF_INET, ipaddr, &serveraddr.sin_addr);Bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr));Listen(sock, 128);return sock;
}int main(int argc, char *argv[])
{int listenfd = start_ser(argv[1], argv[2]);struct epoll_event events[MAX_EVENT_NUM];int epollfd = epoll_create(5);if(epollfd < 0){perr_exit("epoll_create err");}addfd(epollfd, listenfd, true);while(1){int ret = epoll_wait(epollfd, events, MAX_EVENT_NUM, -1);if(ret < 0){printf("epoll failure\n");break;}lt(events, ret, epollfd, listenfd);//lt模式//et(events, ret, epollfd, listenfd);//et模式}Close(listenfd);return 0;
}
//warp.h文件是將socket,bind,listen等函數封裝為第一個字母大寫的頭文件

4.2 客戶端

#include "wrap.h"                                                            int main(int argc, char *argv[])
{int connfd;struct sockaddr_in serveraddr;char buf[1024];connfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&serveraddr, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));inet_pton(AF_INET, argv[1], &serveraddr.sin_addr);Connect(connfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));while(fgets(buf, 1024, stdin) != NULL){Write(connfd, buf, strlen(buf));}Close(connfd);return 0;
}

4.3 兩種模式結果對比

ET模式

LT模式?
當發送超過緩沖區大小的數據量,LT會多次調用epoll_wait函數接受數據,則打印了多次“event level once”,而ET則是循環讀取數據知道讀完,打印了一次“event trigger once”。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383757.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383757.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383757.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

POJ 1741tree-點分治入門

學習了一下點分治&#xff0c;如果理解有誤還請不吝賜教。 為了快速求得樹上任意兩點之間距離滿足某種關系的點對數&#xff0c;我們需要用到這種算法。 點分治是樹上的一種分治算法&#xff0c;依靠樹和子樹之間的關系進行分治從而降低復雜度。 和其他樹上的算法有一些區別…

基于單鏈表的生產者消費者問題

『生產者與消費者問題分析』「原理」生產者生產產品&#xff0c;消費者消費產品。產品如果被消費者消費完了&#xff0c;同時生產者又沒有生產出產品&#xff0c;消費者 就必須等待。同樣的&#xff0c;如果生產者生產了產品&#xff0c;而消費者沒有去消費&#x…

C++智能指針(一)智能指針的簡單介紹

https://blog.csdn.net/nou_camp/article/details/70176949C智能指針 在正式了解智能指針前先看一下下面的一段代碼 #include<iostream> using namespace std; class A { public:A():_ptr(NULL), _a(0){}~A(){} public:int* _ptr;int _a; };void test() {A a;int *p1 ne…

聰聰可可-點分治

聰聰和可可是兄弟倆&#xff0c;他們倆經常為了一些瑣事打起來&#xff0c;例如家中只剩下最后一根冰棍而兩人都想吃、兩個人都想玩兒電腦&#xff08;可是他們家只有一臺電腦&#xff09;……遇到這種問題&#xff0c;一般情況下石頭剪刀布就好了&#xff0c;可是他們已經玩兒…

C++智能指針(二)模擬實現三種智能指針

https://blog.csdn.net/nou_camp/article/details/70186721在上一篇博客中提到了Auto_ptr(C智能指針&#xff08;一&#xff09;)&#xff0c;下面進行模擬實現Auto_ptr 采用類模板實現 #include<iostream> using namespace std; template<class T> class Autoptr …

Prime Distance On Tree-樹分治+FFT

題目描述 Problem description. You are given a tree. If we select 2 distinct nodes uniformly at random, what’s the probability that the distance between these 2 nodes is a prime number? Input The first line contains a number N: the number of nodes in this…

C++智能指針(三)總結

https://blog.csdn.net/nou_camp/article/details/70195795 在上一篇博客中&#xff08;C智能指針&#xff08;二&#xff09;&#xff09;模擬實現了三種智能指針。 其中最好的就是shared_ptr,但是這并不代表它就是最完美的&#xff0c;它也有問題&#xff0c;這個問題就是循環…

POJ2114-Boatherds-樹分治

題目描述 Boatherds Inc. is a sailing company operating in the country of Trabantustan and offering boat trips on Trabantian rivers. All the rivers originate somewhere in the mountains and on their way down to the lowlands they gradually join and finally th…

c++11 你需要知道這些就夠了

https://blog.csdn.net/tangliguantou/article/details/50549751c11新特性舉著火把尋找電燈今天我就權當拋磚引玉&#xff0c;如有不解大家一起探討。有部分內容是引用自互聯網上的內容&#xff0c;如有問題請聯系我。T&& 右值引用 std::move 右值引用出現之前我們只能…

HDU5977-Garden of Eden-樹分治+FWT

題目描述 When God made the first man, he put him on a beautiful garden, the Garden of Eden. Here Adam lived with all animals. God gave Adam eternal life. But Adam was lonely in the garden, so God made Eve. When Adam was asleep one night, God took a rib fro…

C++11新特性學習

https://blog.csdn.net/tennysonsky/article/details/778170481、什么是C11C11標準為C編程語言的第三個官方標準&#xff0c;正式名叫ISO/IEC 14882:2011 - Information technology -- Programming languages -- C。在正式標準發布前&#xff0c;原名C0x。它將取代C標準第二版I…

C++ override 關鍵字用法

override關鍵字作用&#xff1a; 如果派生類在虛函數聲明時使用了override描述符&#xff0c;那么該函數必須重載其基類中的同名函數&#xff0c;否則代碼將無法通過編譯。舉例子說明struct Base {virtual void Turing() 0;virtual void Dijkstra() 0;virtual void VNeumann…

Gym - 101981I-MagicPotion-最大流

題目描述 There are n heroes and m monsters living in an island. The monsters became very vicious these days, so the heroes decided to diminish the monsters in the island. However, the i-th hero can only kill one monster belonging to the set Mi. Joe, the st…

c++仿函數 functor

https://www.cnblogs.com/decade-dnbc66/p/5347088.html內容整理自國外C教材先考慮一個簡單的例子&#xff1a;假設有一個vector<string>&#xff0c;你的任務是統計長度小于5的string的個數&#xff0c;如果使用count_if函數的話&#xff0c;你的代碼可能長成這樣&#…

HDU4812-D Tree-樹分治

題目描述 There is a skyscraping tree standing on the playground of Nanjing University of Science and Technology. On each branch of the tree is an integer (The tree can be treated as a connected graph with N vertices, while each branch can be treated as a v…

成為C++高手之實戰項目

https://blog.csdn.net/niu_gao/article/details/51458721 在內存中模擬出一副牌&#xff0c;然后模擬洗牌&#xff0c;發牌等動作。 流程是這樣的&#xff1a;構建一副牌保存到一個數組中—洗牌—創建玩家—向玩家發牌–輸出每個玩家的牌。 #include <stdio.h> #include…

C++中String類的實現

https://www.cnblogs.com/zhizhan/p/4876093.html原文&#xff1a;http://noalgo.info/382.html String是C中的重要類型&#xff0c;程序員在C面試中經常會遇到關于String的細節問題&#xff0c;甚至要求當場實現這個類。只是由于時間關系&#xff0c;可能只要求實現構造函數、…

Ubuntu軟件更新失敗

剛安裝好Ubuntu以后需要將系統的軟件都更新一下&#xff0c;但是遇到一個問題就是下載倉庫信息失敗&#xff0c;大概是這個樣子的錯誤&#xff1a; 經國遇到這樣的問題可以試一下下面這個命令&#xff1a; sudo rm -rf /var/lib/apt/lists/* sudo apt-get update參考網址&…

getsockname函數與getpeername函數的使用

https://www.tuicool.com/articles/V3Aveygetsockname和getpeername函數 getsockname函數用于獲取與某個套接字關聯的本地協議地址 getpeername函數用于獲取與某個套接字關聯的外地協議地址 定義如下&#xff1a;[cpp] view plaincopy#include<sys/socket.h> int gets…

Ubuntu根目錄空間不足

自己在固態硬盤上安裝的Ubuntu&#xff0c;結果只用了一天就顯示磁盤空間不足。查看空間以后發現Ubuntu自己安裝的時候默認給根目錄分配的是10GB,然而我們下載的軟件以及環境等一般都安裝在根目錄空間下&#xff0c;尤其是/usr目錄所占的空間很大。 不得已我在網上查找了如何給…