I O多路復用之 epoll 系統呼叫

2021-07-31 17:11:48 字數 4516 閱讀 3959

i/o多路復用除了之前我們提到的selectpoll外,epoll 也可以檢查多個檔案描述符的就緒狀態,以達到i/o多路復用的目的。

epoll 系統呼叫是 linux 系統專有的,在 linux 核心 2.6 版本新增,epoll 的主要優點有:

其中,ready list 是 interest list 的子集。

epoll 程式設計介面由以下3個系統呼叫組成:

系統呼叫epoll_create建立乙個新的 epoll 例項,其對應的 interest list 初始化為空。

#include 

int epoll_create(int size);

引數size指定了我們想要通過 epoll 例項來檢查的檔案描述符個數,該引數並不是乙個上限,而是告訴核心應該如何為內部資料結構劃分初始大小。epoll_create返回新建立 epoll 例項的檔案描述符,這個檔案描述符在其他幾個 epoll 系統呼叫中會被用來表示 epoll 例項。當這個檔案描述符不再使用時,應該通過close來關閉。

從 linux 2.6.27 版核心以來,linux 支援了乙個新的系統呼叫epoll_create1。該系統呼叫執行的任務同epoll_create,但是去掉了無用的引數size,並增加了乙個可用來修改系統呼叫行為的flag標誌。

系統呼叫epoll_ctl能夠修改由檔案描述符epfd所代表的 epoll 例項中的 interest list。

#include 

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

epoll_ctlop支援的操作包括以下以種:

上面我們多處提到了evev是指向結構體epoll_event的指標,該結構體的定義如下:

struct epoll_event ;
結構體epoll_event中的data欄位的型別為epoll_data,其定義以下:

typedef

union epoll_data epoll_data_t;

引數ev為檔案描述符fd所做的設定如下:

系統呼叫epoll_wait返回 epoll 例項中處於就緒狀態的檔案描述符的資訊。單個epoll_wait呼叫能返回多個就緒態檔案描述符的資訊,這也正是i/o多路復用的體現。

#include 

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

epoll_wait呼叫成功後,返回資料evlist中的元素個數,即就緒的描述符個數。

我們以編寫乙個 tcp 伺服器為例子,說明 epoll 的用法,該 tcp 伺服器列印出所有接收到的訊息。

我們先來看建立和繫結 tcp 監聽套接字的函式。

static

intcreate_and_bind (char *port)

for (rp = result; rp != null; rp = rp->ai_next)

close (sfd);

}if (rp == null)

freeaddrinfo (result);

return sfd;

}

create_and_bind接受port引數(表示監聽的埠),其作用是建立並繫結監聽套接字。

getaddrinfo函式既可以用於ipv4,也可以用於ipv6,能夠處理名字到位址以及服務到埠這兩種轉換,它返回addrinfo結構體陣列的指標。關於getaddrinfo詳細介紹,可以參考《unix網路程式設計》的有關描述。

create_and_bind返回結構體addrinfo陣列的指標(儲存在reslut指標中)接下來,我們對result進行遍歷,直到將監聽套接字成功繫結為止。

接下來,我們再來看將乙個套接字設定為非阻塞套接字的函式。

static

intmake_socket_non_blocking (int sfd)

flags |= o_nonblock;

s = fcntl (sfd, f_setfl, flags);

if (s == -1)

return

0;}

最後我們來看下main函式的實現。

int

main (int argc, char *argv)

sfd = create_and_bind (argv[1]);

if (sfd == -1)

abort ();

s = make_socket_non_blocking (sfd);

if (s == -1)

abort ();

s = listen (sfd, somaxconn);

if (s == -1)

efd = epoll_create1 (0);

if (efd == -1)

event.data.fd = sfd;

// et 模式

event.events = epollin | epollet;

s = epoll_ctl (efd, epoll_ctl_add, sfd, &event);

if (s == -1)

// 用來儲存epoll_wait返回的就緒檔案描述符列表

events = calloc (maxevents, sizeof event);

// 主迴圈

while (1)

else

if (sfd == events[i].data.fd)

else

}s = getnameinfo (&in_addr, in_len,

hbuf, sizeof hbuf,

sbuf, sizeof sbuf,

ni_numerichost | ni_numericserv);

if (s == 0)

// 設定已連線套接字為非阻塞,並且加入到 epoll 例項監測中

s = make_socket_non_blocking (infd);

if (s == -1)

abort ();

event.data.fd = infd;

// et 模式

event.events = epollin | epollet;

s = epoll_ctl (efd, epoll_ctl_add, infd, &event);

if (s == -1)

}continue;

}else

break;

}else

if (count == 0)

// 列印到標準輸出

s = write (1, buf, count);

if (s == -1)

}if (done)}}

}free (events);

close (sfd);

return exit_success;

}

main函式首先呼叫create_and_bind建立並繫結監聽套接字,接下來呼叫make_socket_non_blocking設定監聽套接字為非阻塞模式,並呼叫listen系統呼叫監聽客戶端的連線請求。

接下來,我們建立了乙個 epoll 例項,並將監聽套接字加入到該 epoll 例項的 interest list,當監聽套接字可讀時,說明有新的客戶端請求連線。

在主迴圈中,我們呼叫epoll_wait等待就緒事件的發生。timeout引數設定為-1說明主線程會一直阻塞到事件就緒。這些就緒事件包括以下型別:

linux/unix 系統程式設計手冊,michael kerrisk 著,郭光偉譯,人民郵電出版社

unix網路程式設計,卷1:套接字聯網api,第3版,人民郵電出版社

Linux系統IO多路復用之epoll

epoll create函式int epoll create int size 建立乙個epoll的控制代碼,size指定需要監聽描述符的最大數量。該函式返回乙個fd,在使用完epoll之後需要呼叫close 關閉,否則可能導致fd耗盡。epoll ctl函式int epoll ctl int ep...

IO多路復用之epoll總結

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

IO多路復用之epoll總結

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