1、poll
函數原型:
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
參數說明:
fds:是一個struct pollfd結構類型的數組,用于存放需要檢測其狀態的Socket描述符;每當調用這個函數之后,系統不會清空這個數組,操作起來比較方便;特別是對于socket連接比較多的情況下,在一定程度上可以提高處理的效率;這一點與select()函數不同,調用select()函數之后,select()函數會清空它所檢測的socket描述符集合,導致每次調用select()之前都必須把socket描述符重新加入到待檢測的集合中;因此,select()函數適合于只檢測一個socket描述符的情況,而poll()函數適合于大量socket描述符的情況;
nfds:nfds_t類型的參數,用于標記數組fds中的結構體元素的總數量;
timeout:是poll函數調用阻塞的時間,單位:毫秒;
如果timeout==0,那么poll() 函數立即返回而不阻塞,如果timeout==INFTIM(宏 表示為-1),那么poll() 函數會一直阻塞下去,直到所檢測的socket描述符上的感興趣的事件發生是才返回
函數返回值:
? ? poll函數的返回值與select函數的返回值一樣。
? ? 若返回0:表示超時
? ? 若為-1:錯誤
? ? 若>0:返回就緒事件的個數
使用poll檢測輸入輸出:
#include?<stdio.h>
#include?<poll.h>int?main()
{struct?pollfd?pfd[1];int?len?=?1;pfd[0].fd?=?0;pfd[0].events?=?POLLIN;pfd[0].revents?=?0;int?done?=?0;while(!done){switch(poll(pfd,1,-1)){case?0:printf("timeout");break;case?-1:perror("select");break;default:{char?buf[1024];if(pfd[0].revents?&?POLLIN){ssize_t?_s?=?read(pfd[0].fd,buf,sizeof(buf)-1);if(_s?>?0){buf[_s]?=?'\0';printf("echo:%s\n",buf);}}}break;}}
}
poll函數的缺點:
(1)大量的fd的數組被整體復制于用戶態和內核地址空間之間,而不管這樣的復制是不是有意義。
(2)與select一樣,poll返回后,需要輪詢poolfd來獲取就緒的描述符。
poll函數的優點:
(1)poll函數不要求計算最大文件描述符的大小
(2)poll函數在應付大數目的文件描述符的時候速度更快,相比于select
(3)它沒有最大連接數的限制,原因是它基于鏈表來存儲的。
2. epoll
? ?epoll只有epoll_create,epoll_ctl,epoll_wait3個系統調用。
?(1)int epoll_create(int size)
? 創建一個epoll的句柄。size參數是可以被忽略的。當創建好epoll句柄后,它就會占用一個fd值。所以在使用完epoll后,必須調用close()關閉,否則可能導致fd被耗盡。
?(2)int epoll_ctl(int epfd,int op,int fd,struct epoll_event* event)
??該函數用于控制某個文件描述符上的事件,可以注冊事件,修改事件,刪除事件。
參數:
?? epfd:由 epoll_create 生成的epoll專用的文件描述符;
?? op:要進行的操作,可能的取值EPOLL_CTL_ADD 注冊、EPOLL_CTL_MOD 修改、?? EPOLL_CTL_DEL 刪除;
?? fd:關聯的文件描述符;
?? event:告訴內核需要監聽的事件;
?? 如果調用成功則返回0,不成功則返回-1。
?(3)int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout)
? ?參數:
?? epfd:由epoll_create 生成的epoll專用的文件描述符;
?? epoll_event:用于回傳代處理事件的數組;
?? maxevents:每次能處理的事件數;
?? timeout:等待I/O事件發生的超時值;
#include?<stdio.h>
#include?<string.h>
#include?<stdlib.h>
#include?<errno.h>
#include?<sys/epoll.h>
#include?<sys/socket.h>
#include?<netinet/in.h>
#include?<fcntl.h>
#include?<unistd.h>
void?usage(const?char*?_proc)
{printf("Usage:%s?[ip]?[port]\n",_proc);
}
void?set_nonblock(int?fd)
{int?fl?=?fcntl(fd,F_GETFL);?//讀取文件狀態標志fcntl(fd,F_SETFL,fl|O_NONBLOCK);?//設置文件狀態標志
}
int?startup(const?char*?_ip,int?_port)
{int?sock?=?socket(AF_INET,SOCK_STREAM,0);?//創建套接字if(sock?<?0){perror("socket");exit(2);}int?opt?=?1;setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//設置處于TIME_WAIT時,端口號還可以用struct?sockaddr_in?local;?//設置本地locallocal.sin_family?=?AF_INET;local.sin_port?=?htons(_port);local.sin_addr.s_addr?=?inet_addr(_ip);if(bind(sock,(struct?sockaddr*)&local,sizeof(local))?<?0){perror("bind");exit(3);}if(listen(sock,5)?<?0){perror("listen");exit(4);}return?sock;
}
int?main(int?argc,char*?argv[])
{if(argc?!=?3){usage(argv[0]);exit(1);}int?listen_sock?=?startup(argv[1],atoi(argv[2]));???//創建監聽套接字int?epfd?=?epoll_create(256);//創建一個epoll的句柄,當創建好句柄后,會占用一個fd值if(epfd?<?0){perror("epoll_create");exit(5);}struct?epoll_event?_ev;_ev.events?=?EPOLLIN;?//設置要處理的事件類型_ev.data.fd?=?listen_sock;??//設置與要處理的事件相關的文件描述符epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&_ev);//注冊函數,注冊監聽事件的類型,struct?epoll_event?revs[64];int?timeout?=?-1;??int?num?=?0;int?done?=?0;while(!done){switch(num?=?epoll_wait(epfd,revs,64,timeout))//revs從內核得到的事件集合,返回需要處理的事件數目{case?0:printf("timeout\n");break;case?-1:perror("epoll_wait");break;default:{struct?sockaddr_in?peer;socklen_t?len?=?sizeof(peer);int?i?=?0;for(;i<num;i++){int?rsock?=?revs[i].data.fd;if(rsock?==?listen_sock?&&?\revs[i].events?&?EPOLLIN){int?new_fd?=?accept(listen_sock,\(struct?sockaddr*)&peer,\&len);if(new_fd?>?0){printf("get?a?new?client?:%s:%d\n",\inet_ntoa(peer.sin_addr),\ntohs(peer.sin_port));set_nonblock(new_fd);_ev.events?=?EPOLLIN?|?EPOLLET;_ev.data.fd?=?new_fd;epoll_ctl(epfd,EPOLL_CTL_ADD,\new_fd,&_ev);}}else{if(revs[i].events?&?EPOLLIN){char?buf[1024];ssize_t?_s?=?read(rsock,buf,sizeof(buf)-1);if(_s?>?0){buf[_s]?=?'\0';printf("client:%s\n",buf);_ev.events?=?EPOLLOUT?|?EPOLLET;_ev.data.fd?=?rsock;epoll_ctl(epfd,EPOLL_CTL_MOD,\rsock,&_ev);}else?if(_s?==?0){printf("client?%d?close...\n",rsock);epoll_ctl(epfd,EPOLL_CTL_DEL,\rsock,NULL);close(rsock);}else{perror("read");}}else?if(revs[i].events?&?EPOLLOUT){const?char*?msg?=?"hello\n";//const?char*?msg?=?\"HTTP/1.0?200?OK\r\n\r\n<html><h1>hello?word!+_+||</h1><html>\r\n";write(rsock,msg,strlen(msg));epoll_ctl(epfd,EPOLL_CTL_DEL,\rsock,NULL);close(rsock);}else{}}}}break;}}return?0;
}
測試結果:
epoll的優點:
(1)支持一個進程打開大數目的socket描述符
(2)IO效率不隨socket描述符數目的增加而線性下降
轉載于:https://blog.51cto.com/10810429/1836707
總結
以上是生活随笔為你收集整理的I/O多路转接之poll,epoll的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。