参看——UNIX环境高级编程中p606I/O多路转接
Unix中的函数select和poll用来,支持Unix中I/O复用的功能,在Unix中I/O模型可以分为以一几种:
(1)阻塞I/O
(2)非阻塞I/O
(3)I/O复用(select和poll)
(4)信号驱动I/O(SIGIO)
(5)异步I/O
现在比较流行的I/O模型是阻塞I/O模型.阻塞I/O是当应用程序和内核交换数据时,由于内核还没有准备好数据,那么应用程序必须进行阻塞,不能继续执行,直到内核的数据准备好!应用程序取到数据返回后,阻塞过程结束!但返回的结果也并不一定是正确的!这里只是举一个简单的例子!也许情况会更加的 复杂!
非阻塞I/O,例如在和内核交换数据时,如果内核的数据没有准备好,那么应用程序不会一真等待,会有一个返回信息,以判断是那里出了问题!这样有助于确认是在那个阶段出了问题!
这里我们介绍另一种,I/O复用之select
select的API介绍
#include <sys/types.h>
#include<sys/time.h>
int select (int maxfdp1,fd_set *readset,fd_set * writeset,fd_set excpetset,const struct timeval *timeout);
select 是休眠检测,由内核唤醒,占用CPU内存少,能够同时监视多个文件描述符(可读,可写,异常文件)的变法, 也支持超时返回。当返回值大于0时,表示说明所检测的文件至少有一个变动了。
参数maxfdp1:最大文件符+1
参数fd_set *readset:检测的可读文件
参数fd_set excpetset:检测的异常文件
参数 timeval *timeout:设置超时返回
timeval结构体定义如下:
struct timeval
{
long tv_sec; /*秒 */
long tv_usec; /*微秒 */
};
返回值:超时返回0;失败返回-1;成功返回大于0的整数,这个整数表示就绪描述符的数目。
select使用范例:
当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零。之后将我们所感兴趣的描述符所对应的位置位,操作如下:
系统提供了4个宏对描述符集进行操作:
fd_set set;
FD_ZERO(&set); /*将set清零使集合中不含任何fd*/
FD_SET(fd, &set); /*将fd加入set集合*/
FD_CLR(fd, &set); /*将fd从set集合中清除*/
FD_ISSET(fd, &set); /*在调用select()函数后,用FD_ISSET来检测fd是否在set集合(可读,可写,异常)中,当检测到fd在set中则返回真,否则,返回假(0)*/
操作如下:
fd_set rset;
int fd;
FD_ZERO(&rset);
FD_SET(fd, &rset);
FD_SET(stdin, &rset);
然后调用select函数,拥塞等待文件描述符事件的到来;如果超过设定的时间,则不再等待,继续往下执行。
select(fd+1, &rset, NULL, NULL,NULL);
select返回后,用FD_ISSET测试给定位是哪个参数(可读,可写,异常文件)被置位:
if(FD_ISSET(fd, &rset)
{
...
//do something
}
下面是一个最简单的select的使用例子:
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
fd_set rd;
struct timeval tv;
int err;
FD_ZERO(&rd);
FD_SET(0,&rd);
tv.tv_sec = 5;
tv.tv_usec = 0;
err = select(1,&rd,NULL,NULL,&tv);
if(err == 0) //超时
{
printf("select time out!\n");
}
else if(err == -1) //失败
{
printf("fail to select!\n");
}
else //成功
{
printf("data is available!\n");
}
return 0;
}
//FD_SET(stdin, &rset);文件描述符0、1和2分别代表stdin、stdout和stderr。
其中FD_SET(0,&rd);是标准输入
我们运行该程序并且随便输入一些数据,程序就提示收到数据了。
select来解决socket中的多客户问题
使用select的优势还有用户可以在一个线程内同时处理多个socket的IO请求。在网络编程中,当涉及到多客户访问服务器的情况,我们首先想到的办法就是fork出多个进程来处理每个客户连接。现在,我们同样可以使用select来处理多客户问题,而不用fork。