900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Linux的I/O多路复用机制之--selectpoll

Linux的I/O多路复用机制之--selectpoll

时间:2021-04-06 18:03:54

相关推荐

Linux的I/O多路复用机制之--selectpoll

1. Linux下的五种I/O模型

1)阻塞I/O(blocking I/O)

2)非阻塞I/O(nonblocking I/O)

3) I/O复用(select 和poll)(I/O multiplexing)

4)信号驱动I/O(signal driven I/O (SIGIO))

5)异步I/O(asynchronous I/O (the POSIX aio_functions))

前四种都是同步,只有最后一种才是异步IO。

五种I/O模型的比较:

2.多路复用--select

系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE *结构的表示就是stdin、stdout、stderr。

select函数

intselect(intmaxfd,fd_set*rdset,fd_set*wrset,\fd_set*exset,structtimeval*timeout);

参数说明:

参数maxfd是需要监视的最大的文件描述符值+1;rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合。struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。

下面的宏提供了处理这三种描述词组的方式:

FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位

FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真

FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位

FD_ZERO(fd_set *set);用来清除描述词组set的全部位

参数timeout为结构timeval,用来设置select()的等待时间,其结构定义如下:

structtimeval{time_ttv_sec;//secondtime_ttv_usec;//minisecond};

如果参数timeout设为:

NULL,则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件。

0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。

特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。

函数返回值:

执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,没有返回;当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。

理解select模型:

理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。

(1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。

(2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)

(3)若再加入fd=2,fd=1,则set变为0001,0011

(4)执行select(6,&set,0,0,0)阻塞等待

(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

基于上面的讨论,可以轻松得出select模型的特点:

可监控的文件描述符个数取决与sizeof(fd_set)的值。

将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个 参数。

可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。

select缺点:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

(3)select支持的文件描述符数量太小,默认是1024

3.多路复用--poll

poll与select非常相似,不同之处在于,select使用三个位图来表示三种不同的事件,而poll使用一个 pollfd的指针实现。

poll函数

#include<poll.h>intpoll(structpollfd*fds,unsignedintnfds,inttimeout);

参数说明:

fds:是一个struct pollfd结构类型的数组,其结构如下:

structpollfd{intfd;/*filedescriptor*/shortevents;/*requestedeventstowatch*/shortrevents;/*returnedeventswitnessed*/};

该结构用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于 socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select() 函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因 此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;

nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;

timeout:是poll函数调用阻塞的时间,单位:毫秒;如果timeout>0那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回。如果timeout==0,那么 poll() 函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发 生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;

返回值:

>0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;

==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的 socket描述符上没有任何事件发生的话,

-1: poll函数调用失败,同时会自动设置全局变量errno;

poll() 函数的功能和返回值的含义与 select() 函数的功能和返回值的含义是完全一样的,两者之间的差别就是内部实现方式不一样。

4.select实例之网络服务器(poll实现类似)

服务器端

#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/select.h>#include<string.h>#define_MAX_LISTEN_5#define_MAX_SIZE_10#define_BUF_SIZE_1024intfd_arr[_MAX_SIZE_];intmax_fd=-1;staticvoidinit_fd_arr(){inti=0;for(;i<_MAX_SIZE_;++i){fd_arr[i]=-1;}}staticintadd_fd_arr(intfd){inti=0;for(;i<_MAX_SIZE_;++i){if(fd_arr[i]==-1){fd_arr[i]=fd;return0;}}return1;}staticvoidremove_fd_arr(intfd){inti=0;for(;i<_MAX_SIZE_;++i){if(fd_arr[i]==fd){fd_arr[i]=-1;break;}}}staticvoidreload_fd_arr(fd_set*pset){inti=0;max_fd=-1;for(;i<_MAX_SIZE_;++i){if(fd_arr[i]!=-1){FD_SET(fd_arr[i],pset);if(fd_arr[i]>max_fd)max_fd=fd_arr[i];}}}staticprintf_msg(inti,constchar*msg){printf("client%d#%s\n",fd_arr[i],msg);}voidUsage(constchar*proc){printf("%susage:[ip][port]\n",proc);}intstartup(constchar*_ip,constchar*_port){intsock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){perror("socket");exit(1);}intopt=1;if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))<0){perror("setsockopt");exit(2);}structsockaddr_inlocal;local.sin_family=AF_INET;local.sin_port=htons(atoi(_port));local.sin_addr.s_addr=inet_addr(_ip);if(bind(sock,(structsockaddr*)&local,sizeof(local))<0){perror("bind");exit(3);}if(listen(sock,_MAX_LISTEN_)<0){perror("listen");exit(4);}returnsock;}intmain(intargc,char*argv[]){if(argc!=3){Usage(argv[0]);return1;}intlisten_sock=startup(argv[1],argv[2]);init_fd_arr();add_fd_arr(listen_sock);fd_setrfds;FD_ZERO(&rfds);structtimevaltv={5,0};while(1){reload_fd_arr(&rfds);intfds=select(max_fd+1,&rfds,NULL,NULL,NULL);switch(fds){case-1:perror("select");exit(5);break;case0:printf("timeout\n");break;default:{intindex=0;for(;index<_MAX_SIZE_;++index){if(fd_arr[index]==listen_sock&&FD_ISSET(fd_arr[index],&rfds))//newaccept{structsockaddr_inpeer;socklen_tlen=sizeof(peer);intnew_fd=accept(listen_sock,(structsockaddr*)&peer,&len);if(new_fd<0){perror("accept");exit(6);}printf("getanewclient%d->ip:%sport:%d\n",new_fd,inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));if(1==add_fd_arr(new_fd)){perror("fd_arrisfull\n");close(new_fd);exit(7);}continue;}if(fd_arr[index]!=-1&&FD_ISSET(fd_arr[index],&rfds))//newreadfd{charbuf[_BUF_SIZE_];memset(buf,'\0',sizeof(buf));ssize_t_s=read(fd_arr[index],buf,sizeof(buf)-1);if(_s>0){buf[_s]='\0';printf_msg(index,buf);}elseif(_s==0)//clientclosed{printf("client%disclosed...\n",fd_arr[index]);FD_CLR(fd_arr[index],&rfds);close(fd_arr[index]);//mustbeforeremove!!!remove_fd_arr(fd_arr[index]);}else{}}}}break;}}return0;}

客户端

#include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<unistd.h>#include<string.h>#include<stdlib.h>voidUsage(constchar*proc){printf("usage:%s[ip][port]\n",proc);}intmain(intargc,char*argv[]){if(argc!=3){Usage(argv[0]);exit(1);}intconn_sock=socket(AF_INET,SOCK_STREAM,0);structsockaddr_inconn;conn.sin_family=AF_INET;conn.sin_port=htons(atoi(argv[2]));conn.sin_addr.s_addr=inet_addr(argv[1]);if(connect(conn_sock,(conststructsockaddr*)&conn,sizeof(conn))<0){perror("connect");exit(2);}charbuf[1024];memset(buf,'\0',sizeof(buf));while(1){printf("pleaseenter#");fflush(stdout);ssize_t_s=read(0,buf,sizeof(buf)-1);if(_s>0){buf[_s-1]='\0';write(conn_sock,buf,strlen(buf));}}return0;}

程序演示

使用Telnet测试

使用客户端测试

使用浏览器测试

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。