epoll事件模型

事件模型

EPOLL事件有兩種模型:

Edge Triggered (ET) 邊緣觸發只有數據到來才觸發,不管緩存區中是否還有數據。

Level Triggered (LT) 水平觸發只要有數據都會觸發。

思考如下步驟:

  1. 假定我們已經把一個用來從管道中讀取數據的文件描述符(RFD)添加到epoll描述符。
  2. 管道的另一端寫入了2KB的數據
  3. 調用epoll_wait,并且它會返回RFD,說明它已經準備好讀取操作
  4. 讀取1KB的數據
  5. 調用epoll_wait……

在這個過程中,有兩種工作模式:

ET模式

ET模式即Edge Triggered工作模式。

如果我們在第1步將RFD添加到epoll描述符的時候使用了EPOLLET標志,那么在第5步調用epoll_wait之后將有可能會掛起,因為剩余的數據還存在于文件的輸入緩沖區內,而且數據發出端還在等待一個針對已經發出數據的反饋信息。只有在監視的文件句柄上發生了某個事件的時候 ET 工作模式才會匯報事件。因此在第5步的時候,調用者可能會放棄等待仍在存在于文件輸入緩沖區內的剩余數據。epoll工作在ET模式的時候,必須使用非阻塞套接口,以避免由于一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。最好以下面的方式調用ET模式的epoll接口,在后面會介紹避免可能的缺陷。

1)????? 基于非阻塞文件句柄

2)????? 只有當read或者write返回EAGAIN(非阻塞讀,暫時無數據)時才需要掛起、等待。但這并不是說每次read時都需要循環讀,直到讀到產生一個EAGAIN才認為此次事件處理完成,當read返回的讀到的數據長度小于請求的數據長度時,就可以確定此時緩沖中已沒有數據了,也就可以認為此事讀事件已處理完成。

LT模式

LT模式即Level Triggered工作模式。

與ET模式不同的是,以LT方式調用epoll接口的時候,它就相當于一個速度比較快的poll,無論后面的數據是否被使用。

LT(level triggered):LT是缺省的工作方式,并且同時支持block和no-block socket。在這種做法中,內核告訴你一個文件描述符是否就緒了,然后你可以對這個就緒的fd進行IO操作。如果你不作任何操作,內核還是會繼續通知你的,所以,這種模式編程出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表。

ET(edge-triggered):ET是高速工作方式,只支持no-block socket。在這種模式下,當描述符從未就緒變為就緒時,內核通過epoll告訴你。然后它會假設你知道文件描述符已經就緒,并且不會再為那個文件描述符發送更多的就緒通知。請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only once).

實例一:

基于管道epoll ET觸發模式

?

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>#define MAXLINE 10int main(int argc, char *argv[])
{int efd, i;int pfd[2];pid_t pid;char buf[MAXLINE], ch = 'a';pipe(pfd);pid = fork();if (pid == 0) {close(pfd[0]);while (1) {for (i = 0; i < MAXLINE/2; i++)buf[i] = ch;buf[i-1] = '\n';ch++;for (; i < MAXLINE; i++)buf[i] = ch;buf[i-1] = '\n';ch++;write(pfd[1], buf, sizeof(buf));sleep(2);}close(pfd[1]);} else if (pid > 0) {struct epoll_event event;struct epoll_event resevent[10];int res, len;close(pfd[1]);efd = epoll_create(10);/* event.events = EPOLLIN; */event.events = EPOLLIN | EPOLLET;        /* ET 邊沿觸發 ,默認是水平觸發 */event.data.fd = pfd[0];epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &event);while (1) {res = epoll_wait(efd, resevent, 10, -1);printf("res %d\n", res);if (resevent[0].data.fd == pfd[0]) {len = read(pfd[0], buf, MAXLINE/2);write(STDOUT_FILENO, buf, len);}}close(pfd[0]);close(efd);} else {perror("fork");exit(-1);}return 0;
}

水平觸發:

運行結果:

ubuntu1604@ubuntu:~/wangqinghe/linux/20190827$ ./et

res 1

aaaa

res 1

bbbb

res 1

cccc

res 1

dddd

res 1

eeee

res 1

ffff

^C

實例二:

基于網絡C/S模型的epoll ET觸發模式

server

?

/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h>#define MAXLINE 10
#define SERV_PORT 8080int main(void)
{struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int listenfd, connfd;char buf[MAXLINE];char str[INET_ADDRSTRLEN];int i, efd;listenfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));listen(listenfd, 20);struct epoll_event event;struct epoll_event resevent[10];int res, len;efd = epoll_create(10);event.events = EPOLLIN | EPOLLET;        /* ET 邊沿觸發 ,默認是水平觸發 */printf("Accepting connections ...\n");cliaddr_len = sizeof(cliaddr);connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));event.data.fd = connfd;epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);while (1) {res = epoll_wait(efd, resevent, 10, -1);printf("res %d\n", res);if (resevent[0].data.fd == connfd) {len = read(connfd, buf, MAXLINE/2);write(STDOUT_FILENO, buf, len);}}return 0;
}

client

/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>#define MAXLINE 10
#define SERV_PORT 8080int main(int argc, char *argv[])
{struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd, i;char ch = 'a';sockfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(SERV_PORT);connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));while (1) {for (i = 0; i < MAXLINE/2; i++)buf[i] = ch;buf[i-1] = '\n';ch++;for (; i < MAXLINE; i++)buf[i] = ch;buf[i-1] = '\n';ch++;write(sockfd, buf, sizeof(buf));sleep(10);}Close(sockfd);return 0;
}

?

邊沿觸發:

運行結果:

ubuntu1604@ubuntu:~/wangqinghe/linux/20190827$ ./et

res 1

aaaa

res 1

bbbb

res 1

cccc

^C

?

實例三:

基于網絡C/S非阻塞模型的epoll ET觸發模式

server

/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>#define MAXLINE 10
#define SERV_PORT 8080int main(void)
{struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int listenfd, connfd;char buf[MAXLINE];char str[INET_ADDRSTRLEN];int i, efd, flag;listenfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));listen(listenfd, 20);struct epoll_event event;struct epoll_event resevent[10];int res, len;efd = epoll_create(10);/* event.events = EPOLLIN; */event.events = EPOLLIN | EPOLLET;        /* ET 邊沿觸發 ,默認是水平觸發 */printf("Accepting connections ...\n");cliaddr_len = sizeof(cliaddr);connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));flag = fcntl(connfd, F_GETFL);flag |= O_NONBLOCK;fcntl(connfd, F_SETFL, flag);event.data.fd = connfd;epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);while (1) {printf("epoll_wait begin\n");res = epoll_wait(efd, resevent, 10, -1);printf("epoll_wait end res %d\n", res);if (resevent[0].data.fd == connfd) {while ((len = read(connfd, buf, MAXLINE/2)) > 0)write(STDOUT_FILENO, buf, len);}}return 0;
}

client

/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>#define MAXLINE 10
#define SERV_PORT 8080int main(int argc, char *argv[])
{struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd, i;char ch = 'a';sockfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(SERV_PORT);connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));while (1) {for (i = 0; i < MAXLINE/2; i++)buf[i] = ch;buf[i-1] = '\n';ch++;for (; i < MAXLINE; i++)buf[i] = ch;buf[i-1] = '\n';ch++;write(sockfd, buf, sizeof(buf));sleep(10);}Close(sockfd);return 0;
}

?

轉載于:https://www.cnblogs.com/wanghao-boke/p/11425936.html

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

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

相關文章

epoll反應堆模型代碼

libevent函數庫核心思想 /*** epoll_loop.c ***/ #include<stdio.h> #include<sys/epoll.h> #include<sys/socket.h> #include<arpa/inet.h> #include<fcntl.h> #include<unistd.h> #include<errno.h> #include<string.h> #in…

UDP廣播

廣播是在局域網之間的一對多的通信方式&#xff0c;使用的udp協議 /*** client.c ***/ #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h>#define SERVER_PORT 8000 #define MAXLINE…

UDP組播

多播(組播) 組播組可以是永久的也可以是臨時的。組播組地址中&#xff0c;有一部分由官方分配的&#xff0c;稱為永久組播組。永久組播組保持不變的是它的ip地址&#xff0c;組中的成員構成可以發生變化。永久組播組中成員的數量都可以是任意的&#xff0c;甚至可以為零。那些沒…

Python3數據結構

列表&#xff1a; Python列表是可變的&#xff0c;這是它區別于字符串數組和元組的最重要的特點。列表可以修改&#xff0c;而字符串和元組不能。 以下是Python中列表的描述方法&#xff1a; 方法 描述 list.append(x) 將元素添加到列表結尾 list.extend(L) 通過添加指定列…

sed、awk工具

ed sed意為流編輯器&#xff08;Stream Editor&#xff09;&#xff0c;在Shell腳本和Makefile中作為過濾器使用非常普遍&#xff0c;也就是把前一個程序的輸出引入sed的輸入&#xff0c;經過一系列編輯命令轉換為另一種格式輸出。sed和vi都源于早期UNIX的ed工具&#xff0c;所…

C語言正則表達式

POSIX規定了正則表達式的C語言庫函數&#xff0c;詳見regex(3)。我們已經學習了很多C語言庫函數的用法&#xff0c;讀者應該具備自己看懂man手冊的能力了。本章介紹了正則表達式在grep、sed、awk中的用法&#xff0c;學習要能夠舉一反三&#xff0c;請讀者根據regex(3)自己總結…

makefile通用版本

實際當中程序文件比較大&#xff0c;這時候對文件進行分類&#xff0c;分為頭文件、源文件、目標文件、可執行文件。也就是說通常將文件按照文件類型放在不同的目錄當中&#xff0c;這個時候的Makefile需要統一管理這些文件&#xff0c;將生產的目標文件放在目標目錄下&#xf…

Python3OS文件/方法

Python3OS文件/方法 os模塊提供了非常豐富的方法用來處理文件和目錄。 方法 描述 os.access(path,mode) 檢驗權限模式 os.chdir(path) 改變當前工作目錄 os.chflags(path,flags) 設置路徑的標記為數字標記 os.chmod(path,mode) 更改權限 os.chown(path,uid,gid) 更改…

Python3文件

open()方法 Python open()方法永于打開一個文件&#xff0c;并返回文件對象&#xff0c;并對文件進行處理過程中都需要用到這個方法&#xff0c;如果該文件無法被打開&#xff0c;則拋出OSError 注意&#xff1a;使用open()方法一定要保證關閉文件對象&#xff0c;即調用close(…

Python3輸入輸出

Python兩種輸出值的方式&#xff0c;表達式語句和print()函數。 第三種方式是使用文件對象的write()方法&#xff0c;標準輸出文件可以用sys.stdout的引用。 如果你希望輸出的形式更加多樣&#xff0c;可以使用str.fomat()函數來格式化輸出值。 如果你希望將輸出的值轉化成字符…

動態庫加載順序

1.編譯目標代碼時指定的動態庫搜索路徑&#xff1b; 2.環境變量LD_LIBRARY_PATH指定的動態庫搜索路徑&#xff1b; 3.配置文件/etc/ld.so.conf中指定的動態庫搜索路徑&#xff1b; 4.默認的動態庫搜索路徑/lib&#xff1b; 5.默認的動態庫搜索路徑/usr/lib。 轉載于:https://ww…

Python3正則表達式

正則表達式是一個特殊的字符序列&#xff0c;他能幫助你方便的檢查一個字符串是否與某種模式匹配。re.match函數 re.match嘗試從字符串的起始位置匹配一個模式&#xff0c;如果不是起始位置匹配成功的話&#xff0c;match()就返回一個none。 函數語法&#xff1a; re.match(pat…

C/C++輸入

fgets(str,n,stdin) 從鍵盤輸入一行&#xff0c;替代gets()。讀取到n-1字節時或換行符時終止&#xff0c;如果是文件的話&#xff0c;讀到文件結尾也會停止 getline(cin,str) str的類型必須是string類&#xff0c;它是C特定的字符串類&#xff0c;區別于C的char *數據類型。 ci…

strlen和sizeof的區別

C語言中沒有字符串&#xff0c;用的是字符數組來模擬字符串。 C風格的字符串時字符數組然后在末尾加0表示結尾。 在C語言中有strlen和sizeof兩個函數求字符數組的長度函數&#xff0c;他們倆的區別就是是否把最后的結束標志也加上去。 strlen是不加的&#xff0c;他表示字符串的…

shell編程練習題

求2個數之和計算1-100的和將一目錄下所有的文件的擴展名改為bak編譯當前目錄下的所有.c文件&#xff1a;打印root可以使用可執行文件數&#xff0c;處理結果: roots bins: 2306打印當前sshd的端口和進程id&#xff0c;處理結果: sshd Port&&pid: 22 5412輸出本機創建20…

shell編程題(一)

求2個數之和 #!/bin/bashfunction add {if(( $# < 2 )); thenecho "The arg int correct"elsesum$(($1$2))echo $sumfi }add 1 add 1 2 運行結果&#xff1a; exbotubuntu:~/shareWin/linux/shell$ ./sum.sh 1 2The arg int correct3 $#&#xff1a;相當于C語言…

vimset

vim ~./vimrc set nocompatible "去掉有關vi一致性模式&#xff0c;避免以前版本的bug和局限 set nu! "顯示行號 set guifontLuxi/ Mono/ 9 " 設置字體&#xff0c;字體名稱和字號 filetype on …

shell編程題(二)

計算1-100之和 #!/bin/bashsum0 for i in seq 1 100;do #符號不是單引號 是 1左邊的符號sum$[$i $sum ] done echo $sum #!/bin/bashi0 n1              #定義循環變量 while [ $n -lt 101 ];do  #循環變量小于101 i$(( $i $n))        #累加 n$(( …

vim命令大全

1. vim模式 正常模式&#xff08;按Esc或Ctrl[進入&#xff09; 左下角顯示文件名或為空 插入模式&#xff08;按i進入&#xff09; 左下角顯示--INSERT-- 可視模式&#xff08;按v進入&#xff09; 左下角顯示--VISUAL-- 替換模式&#xff08;按r或R開始&#xff09; 左下角顯…

signal()函數

函數原型 void (*signal(int sig,void(*func)(int)))(int); 指定使用sig指定的信號編號處理信號的方法。參數func指定程序可以處理信號的三種方式之一&#xff1a; l 默認處理(SIG_DFL)&#xff1a; 信號由該特定信號的默認動作處理l 忽略信號&#xff08;SIG_IGN&a…