linux网络编程系列-select和epoll的区别
select和epoll屬于I/O多路復(fù)用模型,用于持續(xù)監(jiān)聽(tīng)多個(gè)socket,獲取其IO事件。
select(輪詢)
該模型輪詢各socket,不管socket是否活躍,隨著socket數(shù)的增加,性能逐漸下降。
?
#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)調(diào)用時(shí)輪詢一次所有描述字,超時(shí)時(shí)再輪詢一次。如果沒(méi)有描述字準(zhǔn)備好,則返回0;中途錯(cuò)誤返回-1;有描述字準(zhǔn)備好,則將其對(duì)應(yīng)位置為1,其他描述字置為0,返回準(zhǔn)備好的描述字個(gè)數(shù)。
fd_set:整數(shù)數(shù)組,每個(gè)數(shù)中的每一位對(duì)應(yīng)一個(gè)描述字,其具體大小有內(nèi)核的FD_SETSIZE決定,默認(rèn)值為1024。
?
epoll(觸發(fā))
epoll采用了中斷注冊(cè)回調(diào)的方式,socket IO就緒時(shí)發(fā)出中斷,然后將socket加入就緒隊(duì)列。由三個(gè)系統(tǒng)調(diào)用:epoll_create,epoll_ctl,epoll_wait。
能顯著提高程序在大量并發(fā)連接中只有少量活躍的情況下的系統(tǒng)CPU利用率:它會(huì)復(fù)用文件描述符集合來(lái)傳遞結(jié)果,不需要每次等待事件之前都重新準(zhǔn)備要被偵聽(tīng)的文件描述符集合;獲取事件的時(shí)候,它無(wú)須遍歷整個(gè)被偵聽(tīng)的描述符集,只要遍歷那些被內(nèi)核IO事件異步喚醒而加入Ready隊(duì)列的描述符集合。
select/epoll都需要在內(nèi)核和用戶空間之間復(fù)制數(shù)據(jù),epoll使用了內(nèi)存映射(mmap)技術(shù),將內(nèi)核和用戶空間指向同一塊內(nèi)存。
系列函數(shù):
創(chuàng)建函數(shù)
創(chuàng)建一個(gè)epoll句柄,size-監(jiān)聽(tīng)套接字的數(shù)。當(dāng)創(chuàng)建好epoll句柄后,會(huì)占用一個(gè)fd值,所以在使用完epoll后,必須調(diào)用close()關(guān)閉,否則可能導(dǎo)致fd被耗盡。
?
#include <sys/epoll.h> int epoll_create(int size)?
事件注冊(cè)函數(shù)
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)第一個(gè)參數(shù)是epoll_create()的返回值;
第二個(gè)參數(shù)表示動(dòng)作,用三個(gè)宏來(lái)表示:
EPOLL_CTL_ADD:注冊(cè)新的fd到epfd中,
EPOLL_CTL_MOD:修改已經(jīng)注冊(cè)的fd的監(jiān)聽(tīng)事件,
EPOLL_CTL_DEL:從epfd中刪除一個(gè)fd;
第三個(gè)參數(shù)是需要監(jiān)聽(tīng)的fd;
第四個(gè)參數(shù)是告訴內(nèi)核監(jiān)聽(tīng)事件,struct epoll_event結(jié)構(gòu)如下:
events可以是以下幾個(gè)宏的集合:
EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀(包括對(duì)端SOCKET正常關(guān)閉);
EPOLLOUT:表示對(duì)應(yīng)的文件描述符可以寫;
EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來(lái));
EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤;
EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷;
EPOLLET: 將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式,這是相對(duì)于水平觸發(fā)(Level Triggered)來(lái)說(shuō)的。ET只有新事件到來(lái)時(shí),epoll_wait才能獲得通知,即使緩沖區(qū)中還有數(shù)據(jù),epoll_wait也無(wú)法再獲得改描述符的事件;LT只要對(duì)應(yīng)的緩沖區(qū)中還有數(shù)據(jù),epoll_wait就可以獲得該描述符對(duì)應(yīng)的事件。
EPOLLONESHOT:只監(jiān)聽(tīng)一次事件,當(dāng)監(jiān)聽(tīng)完這次事件之后,如果還需要繼續(xù)監(jiān)聽(tīng)這個(gè)socket的話,需要再次把這個(gè)socket加入到EPOLL隊(duì)列里
使用如下:
?
struct epoll_event ev; ev.data.fd=listenfd; ev.events=EPOLLIN|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);等待函數(shù)
?
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
等待事件的產(chǎn)生,參數(shù)events用來(lái)從內(nèi)核得到事件集合。maxevents告之內(nèi)核這個(gè)events有多大,這個(gè)maxevents的值不能大于創(chuàng)建epoll_create()時(shí)的size,參數(shù)timeout是超時(shí)時(shí)間(毫秒,0會(huì)立即返回,-1永久阻塞)。該函數(shù)返回需要處理的事件數(shù)目,如返回0表示已超時(shí)。
注意:某fd上發(fā)生事件后,其事件類型會(huì)被清空,所以如果下一個(gè)循環(huán)還要關(guān)注這個(gè)socket fd的話,則需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)來(lái)重新設(shè)置socket fd的事件類型。這時(shí)不用EPOLL_CTL_ADD,因?yàn)閟ocket fd并未清空,只是事件類型清空。這一步非常重要。
實(shí)例
?
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);}} }對(duì)比:
select - 如果同時(shí)建立很多連接,但只有少數(shù)事件發(fā)生,這種輪詢會(huì)造成效率很低;頻繁從內(nèi)核拷貝、復(fù)制描述字;監(jiān)聽(tīng)描述字受限于內(nèi)核的FD_SETSIZE;
epoll - 這種回調(diào)觸發(fā)式操作會(huì)保證效率;不需要頻繁的拷貝;監(jiān)聽(tīng)描述字沒(méi)有限止,只與系統(tǒng)資源有關(guān);epoll返回時(shí)已經(jīng)明確的知道哪個(gè)sokcet fd發(fā)生了事件,不用再一個(gè)個(gè)比對(duì)。
?
轉(zhuǎn)載于:https://www.cnblogs.com/whuqin/archive/2013/05/09/4982011.html
總結(jié)
以上是生活随笔為你收集整理的linux网络编程系列-select和epoll的区别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 评教有感
- 下一篇: nload实时查看linux服务器网络流