linux网络编程系列-select和epoll的区别
select和epoll屬于I/O多路復用模型,用于持續監聽多個socket,獲取其IO事件。
select(輪詢)
該模型輪詢各socket,不管socket是否活躍,隨著socket數的增加,性能逐漸下降。
?
#include <sys/select.h> #include <sys/time.h> int select (int maxfdpl, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout)調用時輪詢一次所有描述字,超時時再輪詢一次。如果沒有描述字準備好,則返回0;中途錯誤返回-1;有描述字準備好,則將其對應位置為1,其他描述字置為0,返回準備好的描述字個數。
fd_set:整數數組,每個數中的每一位對應一個描述字,其具體大小有內核的FD_SETSIZE決定,默認值為1024。
?
epoll(觸發)
epoll采用了中斷注冊回調的方式,socket IO就緒時發出中斷,然后將socket加入就緒隊列。由三個系統調用:epoll_create,epoll_ctl,epoll_wait。
能顯著提高程序在大量并發連接中只有少量活躍的情況下的系統CPU利用率:它會復用文件描述符集合來傳遞結果,不需要每次等待事件之前都重新準備要被偵聽的文件描述符集合;獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內核IO事件異步喚醒而加入Ready隊列的描述符集合。
select/epoll都需要在內核和用戶空間之間復制數據,epoll使用了內存映射(mmap)技術,將內核和用戶空間指向同一塊內存。
系列函數:
創建函數
創建一個epoll句柄,size-監聽套接字的數。當創建好epoll句柄后,會占用一個fd值,所以在使用完epoll后,必須調用close()關閉,否則可能導致fd被耗盡。
?
#include <sys/epoll.h> int epoll_create(int size)?
事件注冊函數
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)第一個參數是epoll_create()的返回值;
第二個參數表示動作,用三個宏來表示:
EPOLL_CTL_ADD:注冊新的fd到epfd中,
EPOLL_CTL_MOD:修改已經注冊的fd的監聽事件,
EPOLL_CTL_DEL:從epfd中刪除一個fd;
第三個參數是需要監聽的fd;
第四個參數是告訴內核監聽事件,struct epoll_event結構如下:
events可以是以下幾個宏的集合:
EPOLLIN :表示對應的文件描述符可以讀(包括對端SOCKET正常關閉);
EPOLLOUT:表示對應的文件描述符可以寫;
EPOLLPRI:表示對應的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來);
EPOLLERR:表示對應的文件描述符發生錯誤;
EPOLLHUP:表示對應的文件描述符被掛斷;
EPOLLET: 將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對于水平觸發(Level Triggered)來說的。ET只有新事件到來時,epoll_wait才能獲得通知,即使緩沖區中還有數據,epoll_wait也無法再獲得改描述符的事件;LT只要對應的緩沖區中還有數據,epoll_wait就可以獲得該描述符對應的事件。
EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之后,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列里
使用如下:
?
struct epoll_event ev; ev.data.fd=listenfd; ev.events=EPOLLIN|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);等待函數
?
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
等待事件的產生,參數events用來從內核得到事件集合。maxevents告之內核這個events有多大,這個maxevents的值不能大于創建epoll_create()時的size,參數timeout是超時時間(毫秒,0會立即返回,-1永久阻塞)。該函數返回需要處理的事件數目,如返回0表示已超時。
注意:某fd上發生事件后,其事件類型會被清空,所以如果下一個循環還要關注這個socket fd的話,則需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)來重新設置socket fd的事件類型。這時不用EPOLL_CTL_ADD,因為socket fd并未清空,只是事件類型清空。這一步非常重要。
實例
?
struct epoll_event ev, *events;for(;;) {nfds = epoll_wait(kdpfd, events, maxevents, -1);for(n = 0; n < nfds; ++n) {if(events[n].data.fd == listener) {client = accept(listener, (struct sockaddr *) &local,&addrlen);if(client < 0){perror("accept");continue;}setnonblocking(client);ev.events = EPOLLIN | EPOLLET;ev.data.fd = client;if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {fprintf(stderr, "epoll set insertion error: fd=%d\n",client);return -1;}} else {do_use_fd(events[n].data.fd);}} }對比:
select - 如果同時建立很多連接,但只有少數事件發生,這種輪詢會造成效率很低;頻繁從內核拷貝、復制描述字;監聽描述字受限于內核的FD_SETSIZE;
epoll - 這種回調觸發式操作會保證效率;不需要頻繁的拷貝;監聽描述字沒有限止,只與系統資源有關;epoll返回時已經明確的知道哪個sokcet fd發生了事件,不用再一個個比對。
?
轉載于:https://www.cnblogs.com/whuqin/archive/2013/05/09/4982011.html
總結
以上是生活随笔為你收集整理的linux网络编程系列-select和epoll的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 评教有感
- 下一篇: 大数据行业人士必知10大数据思维原理