i/o多路復用除了之前我們提到的select
和poll
外,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_ctl
中op
支援的操作包括以下以種:
上面我們多處提到了ev
,ev
是指向結構體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只需...