Linux: I/O多路转接之poll(有图有代码有真相!!!)
一、poll()函數解析
不同與select使?用三個位圖來表?示三個fdset的?方式,poll使?用?一個 pollfd的指針實現。pollfd結構包含了要監視的event和發?生的event,??
不再使?用select“參數-值”傳遞的?方式。同時,pollfd并沒有最?大數量限制(但是數量過?大后性能也是會下降)。 和select函數?一樣,
?poll返回后,需要輪詢pollfd來獲取就緒的描述符。
?
poll()函數:這個函數是某些Unix系統提供的用于執行與select()函數同等功能的函數,下面是這個函數的聲明: #include <poll.h> int poll(struct pollfd fds[], nfds_t nfds, int timeout); 參數說明: struct pollfd:
struct pollfd{
int fd;????????????? //文件描述符
short events;??? //等待的事件
short revents;?? //實際發生的的事件
};
events和revents是通過對代表各種事件的標志進行邏輯或運算構建而成的。events包括要監視的事件,
?poll用已經發生的事件填充revents。poll函數通過在revents中設置標志字符POLLHUP、POLLERR、POLLNVAL等來反映相關 ? ? ? ? 條件的存在。
不需要在events中對于這些標志符相關的比特位進行設置。如果fd小于0, 則events字段被忽略,而revents被置為0.
標準中沒有說明如何處理文件結束。文件結束可以通過revents的標識符POLLHUP或返回0字節的常規讀操作來傳達。
即使POLLIN或POLLRDNORM指出還有數據要讀,POLLHUP也可能會被設置。因此,應該在錯誤檢驗之前處理正常的讀操作。
poll函數的事件標志符值:
| 常量 | 說明 |
| POLLIN | 普通或優先 帶數據可讀 |
| POLLRDNORM ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? | 普通數據可讀 |
| POLLRDBAND | 優先級帶數據 ? ? ? 可讀 |
| POLLPRI | 高優先級數據 ? ? ? 可讀 |
| POLLOUT | 普通數據可寫 |
| POLLWRNORM | 普通數據可寫 |
| POLLWRBAND | 優先級帶數據 ? ? ? 可寫 |
| POLLERR | 發生錯誤 |
| POLLHUP | 發生掛起 |
| POLLNVAL | 描述字不是一 個打開的文件 |
注:后三個只能作為描述字的返回結果存儲在revents中,而不能作為測試條件用于events中。
? ? ??fds:是一個struct pollfd結構類型的數組,用于存放需要檢測其狀態的Socket描述符;每當調用這個函數之后,系統不會清空這個數組,操作起來比較方便;特別是對于socket連接比較多的情況下,在一定程度上可以提高處理的效率;這一點與select()函數不同,調用select()函數之后,select()函數會清空它所檢測的socket描述符集合,導致每次調用select()之前都必須把socket描述符重新加入到待檢測的集合中;因此,select()函數適合于只檢測一個socket描述符的情況,而poll()函數適合于大量socket描述符的情況; nfds:nfds_t類型的參數,用于標記數組fds中的結構體元素的總數量; timeout:是poll函數調用阻塞的時間,單位:毫秒;
返回值:
> 0 :數組fds中準備好讀、寫或出錯狀態的那些socket描述符的總數量;
== 0 :數組fds中沒有任何socket描述符準備好讀、寫,或出錯;此時poll超時,超時時間是timeout毫秒;換句話說,如果所檢測的socket描述符上沒有任何事件發生的話,那么poll()函數會阻塞timeout所指定的毫秒時間長度之后返回,如果timeout==0,那么poll() 函數立 即返回而不阻塞,如果timeout==INFTIM,那么poll() 函數會一直阻塞下去,直到所檢測的socket描述符上的感興趣的事件發生是才返回,如果感興趣的事件永遠不發生,那么poll()就會永遠阻塞下去;
二、poll應用實例 實例一:用poll監視輸入輸出 #include<stdio.h> #include<poll.h> #include<stdlib.h> #include<string.h> int main(int argc,char *argv[]) {struct pollfd _poll[1];_poll[0].fd=0;_poll[0].events=POLLIN;_poll[0].revents=0;int timeout=3000;int i=0;char buf[1024]; while(1) {switch(poll(_poll,1,timeout)){case 0:printf("timeout");break;case -1:printf("poll");break;default:{for(i=0;i<2;i++){if((_poll[0].fd==0)&&(_poll[0].revents)&POLLIN){ssize_t s=read(0,buf,sizeof(buf)-1);if(s>0){buf[s]=0;if(strncmp(buf,"hello poll",10)==0){close(_poll[i].fd);return 1;}printf("this is test:poll:%s\n",buf);}}}}break;} } return 0; }
事例二:poll實現服務器端:
#include<stdio.h> #include<stdlib.h> #include<poll.h> #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<netinet/in.h> #include<string.h> static void usage(char *proc) {printf("usage:%s [local_ip] [local_port]\n",proc); } int startup(char* _ip,int _port) {//create socketint sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){perror("socket");return 2;}//port multiplexingint flg=1; setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&flg,sizeof(flg));struct sockaddr_in local;local.sin_family=AF_INET;local.sin_addr.s_addr=inet_addr(_ip);local.sin_port=htons(_port);//bindif(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){perror("bind");return 3;}//listenif(listen(sock,10)<0){perror("listen");return 4;}return sock; } int main(int argc,char *argv[]) {if(argc!=3){usage(argv[0]);return 1;}int listen_sock=startup(argv[1],atoi(argv[2]));struct pollfd fd_set[2];fd_set[0].fd=listen_sock;fd_set[0].events=POLLIN;fd_set[0].revents=0;int timeout=2000;int n=sizeof(fd_set)/sizeof(fd_set[0]);struct sockaddr_in client;socklen_t len=sizeof(client);int i=1;for(;i<n;i++){fd_set[i].fd=-1;}int maxfd=0;while(1){switch(poll(fd_set,maxfd+1,timeout)){case 0:printf("timeout..\n");break;case -1:printf("poll");break;default:{for(i=0;i<n;i++){if((fd_set[i].fd==listen_sock)&& \(fd_set[i].revents)&POLLIN){int new_sock=accept(listen_sock,\(struct sockaddr*)&client,&len);if(new_sock<0){perror("accept");continue;}printf("get a new client:\n");int j=0;for(j=0;j<n;j++){if(fd_set[j].fd==-1){fd_set[j].fd=new_sock;fd_set[j].events=POLLIN;fd_set[j].revents=0;break;}} if(j==n){close(new_sock);}if(j>maxfd){maxfd=j;}}else if((fd_set[i].fd>0)&& (fd_set[i].revents)&POLLIN){char buf[1024];ssize_t s=read(fd_set[i].fd,buf,sizeof(buf)-1);if(s>0){buf[s]='\0';printf("client: %s\n",buf);write(fd_set[i].fd,buf,strlen(buf));}else if(s==0){close(fd_set[i].fd);int p=1;for(;p<n;i++){if((fd_set[p].fd!=-1)&&(p!=i)){int temp=fd_set[i].fd;fd_set[i].fd=fd_set[p].fd;fd_set[p].fd=temp;}}}}}}break;}}return 0; }
三、總結 1、poll的優點 1)poll() 在應付大數目的文件描述符的時候相比于select速度更快,相對于select更加高效。 2)解決了select處理文件描述符有上限(默認1024)的限制。 2、poll缺點 1)、大量的fd的數組被整體復制于用戶態和內核地址空間之間,而不管這樣的復制是不是有意義。? 2)、與select一樣,poll返回后,需要輪詢pollfd來獲取就緒的描述符 ?3)、事實上,同時連接的?大量客戶端在?一時刻可能只有很少的處于就緒狀態,因此隨著監視的描述符數量的增長,其效率也會線性下降。
總結
以上是生活随笔為你收集整理的Linux: I/O多路转接之poll(有图有代码有真相!!!)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最近想入门股票,请问要如何操作?转载(博
- 下一篇: linux 其他常用命令