日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux网络编程---I/O复用模型之epoll

發布時間:2023/11/30 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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?
    • fd是操作的文件描述符
    • 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 兩種模式結果對比

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

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的Linux网络编程---I/O复用模型之epoll的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 99久久婷婷国产综合精品电影 | 亚洲一级中文字幕 | 欧洲裸体片 | 国产一区免费在线观看 | 国产精品久久国产精麻豆96堂 | 四川黄色一级片 | 97人妻精品一区二区三区软件 | 女女互慰揉小黄文 | 亚洲天堂免费看 | 成人午夜免费电影 | 丰满尤物白嫩啪啪少妇 | 美女交配 | 欧美黑人xxx | 欧美乱大交xxxxx春色视频 | 国产视频一区二区三区四区 | av导航站| 国产精品一二三四五 | 黄色三及| 做暧暧视频在线观看 | 痴女扩张宫交脱垂重口小说 | 公车激情云雨小说 | 午夜一区二区三区 | 中国字幕av| 国产免费黄网站 | 国产手机av在线 | 日本新japanese乱熟 | 国产av电影一区二区三区 | 欧美一级黑人 | 国产欧美精品aaaaaa片 | 免费视频一二三区 | 伊人久久大香 | 躁躁躁日日躁 | 国产女优在线播放 | 色爽黄 | 亚洲视频在线免费播放 | 欧美少妇一区二区三区 | 最新啪啪网站 | 国产素人在线观看 | 久久久久久国产精品无码 | 亚洲一区二区三区免费视频 | 视频三区在线 | 人妻久久一区二区三区 | 极品美妇后花庭翘臀娇吟小说 | 日韩精品一线二线三线 | 日韩极品视频在线观看 | 天天干天天谢 | av综合网站 | 都市激情麻豆 | 他趴在我两腿中间添得好爽在线看 | 亚洲国产电影在线观看 | 黄色国产网站 | 青青草免费在线视频 | 亚洲AV无码精品国产 | 国产精品视频无码 | 亚洲成人免费在线 | 国产盗摄一区二区三区在线 | 国产最新av | 亚洲网站在线观看 | 日日操夜夜爽 | 黄色一级大片在线免费看国产一 | 欧美日韩一级片在线观看 | 久久靠逼视频 | 91黄址| 夜夜爽夜夜爽 | 欧美日韩国产综合网 | 精品人妻一区二区三区久久嗨 | 另类av小说 | 黑人中文字幕一区二区三区 | 亚洲福利av | 国产香蕉视频在线观看 | 日本aa在线观看 | 亚洲性影院 | 天堂在线资源库 | 成人香蕉网 | 色悠悠国产 | 国产成人无码一区二区三区在线 | 白浆导航| 欧美精品日韩少妇 | 色屁屁视频 | 永久影院| 久草免费在线视频观看 | 国产又黄又粗的视频 | 人妻无码一区二区三区久久99 | 女人一级一片30分 | 精品人妻一区二区三区浪潮在线 | 国产又粗又长又黄视频 | 国产精品二区在线观看 | 亚洲国产精品视频在线观看 | 久草中文在线视频 | 日韩欧美二区 | 91搞搞 | 天天爽天天 | xxxxx在线| 伊人一二三 | 国产日韩一区二区在线 | xxxx日韩| 芒果视频污污 | 在线观看色视频 | 草草影院欧美 |