IO复用编程基础第七章本章目标IO复用基本概念select函数I/O复用如果一个或多个I/O条件满足(例如:输入已准备好被读,或者描述字可以承接更多输出的时候)我们就能够被通知到,这样的能力被称为I/O复用,是由函数select和poll支持的I/O复用网络应用场合当客户处理多个描述字一个客户同时处理多个套接口如果一个tcp服务器既要处理监听套接口,又要处理连接套接口如果一个服务器既要处理TCP,又要处理UDP如果一个服务器要处理多个服务或多个协议五个I/O模型阻塞I/O非阻塞I/OI/O复用(select和poll)信号驱动I/O异步I/O阻塞I/O模型最流行的I/O模型是阻塞I/O模型,缺省时,所有的套接口都是阻塞的非阻塞I/O模型(2-1)当我们把一个套接口设置为非阻塞方式时,即通知内核:当请求的I/O操作非得让进程睡眠不能完成时,不要让进程睡眠,而应返回一个错误非阻塞I/O模型(2-2)应用程序连续不断地查询内核,看看某操作是否准备好,这对cpu时间是极大的浪费,一般只在专门提供某种功能的系统中才会用到I/O复用模型有了I/O复用,我们就可以调用select或poll,在这两个系统调用的某一个上阻塞,而不是真正阻塞于真正的I/O系统调用信号驱动I/O模型我们也可以用信号,让内核在描述字准备好时用信号SIGIO通知我们,我们将此方法称为信号驱动I/O异步I/O模型异步I/O是Posix.1的1993版本中的新内容,我们让内核启动操作,并在整个操作完成后通知我们select函数作用这个函数允许进程指示内核等待多个事件中的任一个发生,并仅在一个或多个事件发生或经过某指定的时间后才唤醒进程select函数什么情况下返回作为一个例子,我们可以调用函数select并通知内核仅在下列情况发生时才返回:集合{1,4,5}中的任何描述子准备好读或集合{2,7}中的任何描述字准备好写或集合{1,4}中的任何描述字有异常条件待处理或已经过了10.2秒也就是说,通知内核我们对哪些描述字感兴趣(读、写或异常条件)以及等待多长时间。select函数原型intselect(intmaxfdp1,fd_set*readset,fd_set*writeset,fd_set*except,conststructtimeval*timeout);timeval结构structtimeval(longtv_sec;//秒longtv_usec;//微秒);虽然结构timeval为我们指定了一个微秒级的分辨率,但内核支持的分辨率却要粗糙得多。例如,很多UNIX内核将超时值向上舍入成10ms的倍数。另外还有调度延迟现象,即定时器时间到后内核还需花一点时间调度相应进程的运行timeout参数timeout参数有三种可能永远等待下去:仅在有一个描述字准备好I/O时才返回,为此,我们将timeout设置为空指针等待固定时间:在有一个描述字准备好I/O是返回,但不超过由timeout参数所指timeval结构中指定的秒数和微秒数根本不等待:检查描述字后立即返回,这称为轮询。定时器的值必须为0fd_set参数select使用描述字集,它一般是一个整数数组,每个数中的每一位对应一个描述字。使用fd_set数据类型来表示这个描述字集,我们不用去关心具体的实现细节。操作fd_set的四个宏voidFD_ZERO(fd_set*fdset);//清空描述字集合voidFD_SET(intfd,fd_set*fdset);//添加一个描述字到集合中voidFD_CLR(intfd,fd_set*fdset);//从集合中删除一个描述字intFD_ISSET(intfd,fd_set*fdset);//描述字是否在该集合中fd_set使用对集合的初始化是很重要的,如果集合作为一个自动变量分配而未初始化,那将导致不可预测的后果fd_setrset;FD_ZERO(&rset);FD_SET(1,&rset);FD_SET(4,&rset);FD_SET(5,&rset);select函数的三个中间参数中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件所需的描述字如果我们对某个条件不感兴趣,这三个参数中相应的参数就可以设为空指针maxfdp1参数Maxfdp1指定被测试的描述字个数,它的值是要被测试的最大描述字加1,描述字0、1、2…….一直到maxfdp1均被测试头文件sys/select.h中定义的常值FD_SETSIZE,是数据类型fd_set的描述字数量,其值通常是1024select函数返回值•当返回时,结果指示哪些描述字已准备好。•返回时,我们用宏FD_ISSET来测试结构fd_set中的描述字。描述字集中任何与没有准备好的描述字相对应的位返回时清成0。为此,每次调用select时,我们都得将所有描述字集中关心的都置为1•如果在任何描述字准备好之前定时器时间到,则返回0,•返回-1表示有错套接口准备好读•套接口接收缓冲区中的数据字节数大于等于套接口接收缓冲区低潮限度的当前值•连接的读这一半关闭。读操作将不阻塞且返回0•套接口是一个监听套接口且已完成的连接数为非0•有一个套接口错误待处理,对这样的套接口的读操作将不阻塞且返回一个错误(-1)套接口准备好写•套接口发送缓冲区中的可用空间字节数大于等于套接口发送缓冲区低潮限度的当前值•连接的写这一半关闭,对这样的套接口的写操作将产生信号(SIGPIPE)•有一个套接口错误待处理。对这样的套接口的写操作将不阻塞且返回一个错误(-1)shutdown函数intshutdown(intsockfd,inthowto);该函数的值依赖于参数howto的值•SHUT_RD关闭连接的读这一半,不再接收套接口中的数据且现留在套接口接收缓冲区中的数据都作废•SHUT_WR关闭连接的写这一半,在TCP场合下,这称为为半关闭。当前留在套接口发送缓冲区中的数据都被发送,后跟正常的tcp连接终止序列•SHUT_RDWR连接的读这一半和写这一半都关闭shutdown与close的区别终止网络连接的正常方法是调用close,但close有两个限制可由函数shutdown来避免•close将描述字的访问计数减1,仅在此计数为0时才关闭套接口;用shutdown我们可以激发TCP的正常连接终止序列,而不管访问计数•Close终止了数据传送的两个方向:读和写。由于TCP连接是全双工的,有很多时候我们要通知另一端我们已完成了数据发送,即使一端仍有许多数据要发送也是如此.函数poll原型返回:准备好描述字的个数,0-超时,-1:出错#includepoll.hintpoll(structpollfd*fdarray,unsignedlongnfds,inttimeout);pollfd结构Struct{intfd;//要检查的描述符shortevent;//要测试的条件shortrevents;//返回描述字的状态);要测试的条件由成员events规定,函数在相应的revents成员中返回描述字的状态。events的一些常值参数timeout参数timeout指定函数返回前等待多长时间。它是一个指定应等待的毫秒数的正值。总结IO复用基本概念select函数POLL函数