Linux: I/O多路转接之poll(有图有代码有真相!!!)
2017-06-13 13:19
489 查看
一、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函数的事件标志符值:
注:后三个只能作为描述字的返回结果存储在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()就会永远阻塞下去;
== -1 : poll函数调用失败,同时会自动设置全局变量errno;
二、poll应用实例
实例一:用poll监视输入输出
事例二: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 socket
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return 2;
}
//port multiplexing
int 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);
//bind
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
return 3;
}
//listen
if(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)、事实上,同时连接的⼤大量客户端在⼀一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。
不同与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 | 描述字不是一 个打开的文件 |
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()就会永远阻塞下去;
== -1 : poll函数调用失败,同时会自动设置全局变量errno;
二、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 socket
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return 2;
}
//port multiplexing
int 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);
//bind
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
return 3;
}
//listen
if(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多路转接之select(有图有代码有真相!!!)
- Linux: I/O多路转接之epoll(有图有代码有真相!!!)
- Linux下高级I/O多路转接之poll服务器
- 【Linux网络编程】基于TCP流 I/O多路转接(poll) 的高性能http服务器
- Linux【网络编程】——I/O多路转接之Poll服务器
- Linux高性能服务器之多路转接(2)---poll模型
- Linux I/O多路转接----poll模型
- 【Linux】多路 IO转接服务器 — poll
- 【Linux】I/O多路转接poll
- 【Linux】中多路转接之poll
- linux device drive 第六章代码示例-scullpipe的实验(poll和fasync方法的实现)之一
- linux高级字符设备驱动之 四 poll方法(select多路监控原理与实现)
- 深入研究socket编程(2)——I/O多路转接(select、pselect和poll)
- [nginx] Linux下多路复用IO接口 epoll select poll 的区别
- I/O多路转接(select、pselect和poll)使用
- Linux下套接字详解(九)---poll模式下的IO多路复用服务器
- 多路转接模型之poll
- linux的I/O多路转接select的fd_set数据结构和相应FD_宏的实现分析
- 【Nginx】I/O多路转接之select、poll、epoll
- Linux高级字符设备驱动 poll方法(select多路监控原理与实现)