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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

epoll监听文件_epoll使用详解

發布時間:2023/12/13 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 epoll监听文件_epoll使用详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

epoll介紹

epoll的行為與poll(2)相似,監視多個有IO事件的文件描述符。epoll除了提供select/poll那種IO事件的水平觸發(Level Triggered)外,還提供了邊緣觸發(Edge Triggered),這就使得用戶空間程序有可能緩存IO狀態,減少epoll_wait/epoll_pwait的調用,提高應用程序效率。

epoll_create(2) 創建一個新的epoll實例,并返回一個引用該實例的文件描述符

epoll_ctl(2) 創建epoll實例后,注冊對感興趣的文件描述符。當前注冊在epoll實例上的文件描述符集被稱為epoll集合。

epoll_wait(2) 等待I/O事件,如果當前沒有事件可用,則阻塞調用線程。

水平觸發和邊沿觸發

epoll事件分布接口既可以表現為邊緣觸發(ET),也可以表現為水平觸發(LT)。這兩種機制的區別

可以這樣描述。假設有這種情況發生:

表示管道(rfd)的讀側的文件描述符在epoll實例上注冊。

管道寫入器在管道的寫入端寫入2 kB的數據。

調用epoll_wait(2)將返回rfd作為就緒文件描述符。

管道讀取器從rfd讀取1kb的數據。

epoll_wait(2)調用完成。

如果使用邊緣觸發標志將rfd文件描述符注冊到epoll接口,那么第五步的epoll_wait(2)的調用可能會掛起,盡管文件輸入緩沖區仍然有1kb數據可讀;同時,遠程對等端可能正在期望基于它已發送的數據的應答。這樣做的原因是,只有在被監視文件描述符上發生更改時,邊緣觸發模式才交付事件。因此,在步驟5中,調用者可能會以等待那些仍在輸入緩沖區中的數據的狀態下結束。

在上面的例子中,將生成rfd上的一個事件,因為在2中完成了寫入,而在3中使用了該事件。由于在4中完成的讀操作不會消耗整個緩沖區數據,所以在步驟5中完成的對epoll_wait(2)的調用可能會無限期阻塞。

使用EPOLLET標志的應用程序應該使用非阻塞文件描述符,以避免在處理多個文件描述符時出現有阻塞的讀寫饑餓任務。建議使用epoll作為邊沿觸發(EPOLLET)接口的方式如下:

i、 具有非阻塞文件描述符

ii、只有在read(2)或write(2)返回EAGAIN后才等待事件。

相反,當EPOLLET作為水平觸發接口使用時(默認情況下,沒有指定EPOLLET), epoll只是一個更快的poll(2),并且可以在使用后者的任何地方使用,因為它具有相同的語義。

Epoll的優點:

1、支持一個進程打開大數目的socket描述符(FD)

select能打開的文件描述符有一定的限制,FD_SETSIZE設置,默認值是2048,有兩種解決方法,1、修改它的值,然后重新編譯內核。2、使用多進程加入要并發20w個客戶,那么就要開100進程;epoll則沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大于2048,舉個例子,在1GB內存的機器上大約是2萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統內存關系很大。

2、IO效率不隨FD數目增加而線性下降

select/poll采用輪詢的方式掃描文件描述符,文件描述符數量越多,性能越差;內核 / 用戶空間內存拷貝問題,select/poll需要復制大量的句柄數據結構,產生巨大的開銷;select/poll返回的是含有整個句柄的數組,應用程序需要遍歷整個數組才能發現哪些句柄發生了事件,導致效率呈現線性下降。但是epoll不存在這個問題,它只會對"活躍"的socket進行操作---這是因為在內核實現中epoll是根據每個fd上面的callback函數實現的。

3、支持邊緣觸發模式

select/poll的觸發方式是水平觸發,應用程序如果沒有完成對一個已經就緒的文件描述符進行IO操作,那么之后每次select/poll調用還是會將這些文件描述符通知進程。

4、使用mmap加速內核與用戶空間的消息傳遞。

select/poll和epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝很重要,在這點上,select/poll需要復制整個FD數組,產生巨大的開銷;而epoll是通過內核于用戶空間mmap同一塊內存實現的。

epoll的系統調用

epoll_create

int epoll_create(int size);

int epoll_create1(int flags);

創建一個epoll的句柄。自從linux2.6.8之后,size參數是被忽略的,更推薦使用epoll_crete1(0)來替代,flags可以設置EPOLL_CLOEXEC標志

epoll_ctl

#include

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

該系統調用對文件描述符epfd引用的epoll(7)實例執行控制操作。它請求對目標文件描述符fd執行操作op。

epfd : epoll_create創建的文件描述符.

op :參數的有效參數為:

EPOLL_CTL_ADD

在文件描述符epfd引用的epoll實例上注冊目標文件描述符fd。

EPOLL_CTL_MOD

修改已注冊描述符fd關聯的事件。

EPOLL_CTL_DEL

從epfd引用的epoll實例中刪除(取消注冊)目標文件描述符fd。該事件將被忽略,并且可以是NULL

fd :待監聽的fd

epoll_event : 描述鏈接到文件描述符fd的對象,它的定義如下

typedef union epoll_data {

void *ptr;

int fd;

uint32_t u32;

uint64_t u64;

} epoll_data_t;

struct epoll_event {

uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

events成員是由以下可用事件類型的零個或多個組合在一起組成的位掩碼:

EPOLLIN :關聯的文件描述符可以讀(包括對端SOCKET正常關閉);

EPOLLOUT:關聯的文件描述符可以寫;

EPOLLPRI:關聯的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來);

EPOLLERR:關聯的文件描述符發生錯誤;

EPOLLHUP:關聯的文件描述符被掛斷;

EPOLLRDHUP:流套接字對等關閉連接,或半關閉寫。(當使用邊緣觸發監視時,此標記對于編寫簡單代碼檢測對等端是否關閉特別有用。2.6.17引入)

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

EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之后,如果還需要繼續監聽這個fd的話,需要再次把這個fd加入到EPOLL隊列里

它們在內核頭文件里的定義如下:

33

34 enum EPOLL_EVENTS

35 {

36 EPOLLIN = 0x001,

37 #define EPOLLIN EPOLLIN

38 EPOLLPRI = 0x002,

39 #define EPOLLPRI EPOLLPRI

40 EPOLLOUT = 0x004,

41 #define EPOLLOUT EPOLLOUT

42 EPOLLRDNORM = 0x040,

43 #define EPOLLRDNORM EPOLLRDNORM

44 EPOLLRDBAND = 0x080,

45 #define EPOLLRDBAND EPOLLRDBAND

46 EPOLLWRNORM = 0x100,

47 #define EPOLLWRNORM EPOLLWRNORM

48 EPOLLWRBAND = 0x200,

49 #define EPOLLWRBAND EPOLLWRBAND

50 EPOLLMSG = 0x400,

51 #define EPOLLMSG EPOLLMSG

52 EPOLLERR = 0x008,

53 #define EPOLLERR EPOLLERR

54 EPOLLHUP = 0x010,

55 #define EPOLLHUP EPOLLHUP

56 EPOLLRDHUP = 0x2000,

57 #define EPOLLRDHUP EPOLLRDHUP

58 EPOLLEXCLUSIVE = 1u << 28,

59 #define EPOLLEXCLUSIVE EPOLLEXCLUSIVE

60 EPOLLWAKEUP = 1u << 29,

61 #define EPOLLWAKEUP EPOLLWAKEUP

62 EPOLLONESHOT = 1u << 30,

63 #define EPOLLONESHOT EPOLLONESHOT

64 EPOLLET = 1u << 31

65 #define EPOLLET EPOLLET

66 };

67

68

69 /* Valid opcodes ( "op" parameter ) to issue to epoll_ctl(). */

70 #define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface. */

71 #define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface. */

72 #define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure. */

epoll_wait

#include

int epoll_wait(int epfd, struct epoll_event *events,

int maxevents, int timeout);

int epoll_pwait(int epfd, struct epoll_event *events,

int maxevents, int timeout,

const sigset_t *sigmask);

等待在epoll監控的事件中已經發生的事件。

epfd : epoll_create() 的返回值.

events : 分配好的epoll_event結構體數組,epoll將會把發生的事件賦值到events數組中(events不可以是空指針,內核只負責把數據復制到這個events數組中,不會去幫助我們在用戶態中分配內存)

maxevents : maxevents告知內核這個events有多大,這個 maxevents的值大于0(否則Error :Invalid argument)

timeout : 超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。如果函數調用成功,返回對應I/O上已準備好的文件描述符數目,如返回0表示已超時,它會阻塞直到

一個文件描述符有事件發生;

信號處理器中斷;

超時;

epoll示例程序

此程序簡單測試一下三個API,注冊標準輸出的描述符到epoll,監視標準輸出的讀事件,觸發后回顯一遍,quit退出程序.

#include

#include

#include

#include

#include

#include

#include

#include

#include

typedef std::vector PollFdList;

int main(int argc ,char **argv)

{

int fd;

char buf[1024];

int i,res,real_read, maxfd;

if((fd=open("/dev/stdin",O_RDONLY|O_NONBLOCK)) < 0)

{

fprintf(stderr,"open data1 error:%s",strerror(errno));

return 1;

}

PollFdList m_pollfds;

int epfd = epoll_create1(EPOLL_CLOEXEC);

struct epoll_event ev;

ev.events = EPOLLIN | EPOLLPRI;

ev.data.fd = fd;

epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);

m_pollfds.resize(1024);

while(1)

{

int ret = epoll_wait(epfd, m_pollfds.data(), m_pollfds.size(), 5000);

if (ret < 0)

{

printf("ePoll error : %s\n",strerror(errno));

return 1;

}

if(ret == 0){

printf("ePoll timeout\n");

continue;

}

for (i = 0; i< 1; i++)

{

if (m_pollfds[i].events & EPOLLIN)

{

memset(buf, 0, 1024);

real_read = read(m_pollfds[i].data.fd, buf, 1024);

if (real_read < 0)

{

if (errno != EAGAIN)

{

printf("read eror : %s\n",strerror(errno));

continue;

}

}

else if (!real_read)

{

close(m_pollfds[i].data.fd);

m_pollfds[i].events = 0;

}

else

{

if (i == 0)

{

buf[real_read] = '\0';

printf("%s", buf);

if ((buf[0] == 'q') || (buf[0] == 'Q'))

{

printf("quit\n");

return 1;

}

}

else

{

buf[real_read] = '\0';

printf("%s", buf);

}

}

}

}

}

exit(0);

}

./test

hello

hello

hello epoll

hello epoll

ePoll timeout

quit

quit

quit

總結

以上是生活随笔為你收集整理的epoll监听文件_epoll使用详解的全部內容,希望文章能夠幫你解決所遇到的問題。

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