Chinaunix首页 | 论坛 | 博客
  • 博客访问: 388385
  • 博文数量: 83
  • 博客积分: 1650
  • 博客等级: 上尉
  • 技术积分: 861
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-18 18:22
文章分类
文章存档

2021年(1)

2016年(1)

2015年(2)

2014年(3)

2013年(12)

2012年(16)

2011年(18)

2010年(30)

分类: LINUX

2010-11-04 00:06:33

select

1select函数说明

前面的fcntl函数解决了文件的共享问题,接下来该处理I/O 复用的情况了。

总的来说,I/O 处理的模型有5 种。

· 阻塞 I/O模型在这种模型下,若所调用的I/O 函数没有完成相关的功能就会使进程

挂起,直到相关数据到才会出错返回。如常见对管道设备、终端设备和网络设备进行读写时

经常会出现这种情况。

· 非阻塞模型:在这种模型下,当请求的I/O 操作不能完成时,则不让进程睡眠,

而且返回一个错误。非阻塞I/O 使用户可以调用不会永远阻塞的I/O 操作,如openwrite

read。如果该操作不能完成,则会立即出错返回,且表示该I/O 如果该操作继续执行

就会阻塞。

· I/O多路转接模型:在这种模型下,如果请求的I/O操作阻塞,且它不是真正阻塞I/O

而是让其中的一个函数等待,在这期间,I/O 还能进行其他操作。如本节要介绍的select函数

poll函数,就是属于这种模型。

· 信号驱动 I/O 模型:在这种模型下,通过安装一个信号处理程序,系统可以自动

捕获特定信号的到来,从而启动I/O。这是由内核通知用户何时可以启动一个I/O 操作

决定的。

· 异步 I/O模型:在这种模型下,当一个描述符已准备好,可以启动I/O 时,进程会通

知内核。现在,并不是所有的系统都支持这种模型。

可以看到,selectI/O 多路转接模型是处理I/O 复用的一个高效的方法。它可以具体设

置每一个所关心的文件描述符的条件、希望等待的时间等,从select 函数返回时,内核会通

知用户已准备好的文件描述符的数量、已准备好的条件等。通过使用select 返回值,就可以

调用相应的I/O 处理函数了。

2select函数格式

Select函数的语法格式如表6.8 所示。

6.8 fcntl函数语法要点

所需头文件

#include

#include

#include

函数原型int select(int numfds,fd_set *readfds,fd_set *writefdsfd_set *exeptfds,struct timeval

*timeout)

numfds:需要检查的号码最高的文件描述符加1

readfds:由select()监视的读文件描述符集合

writefds:由select()监视的写文件描述符集合

exeptfds:由select()监视的异常处理文件描述符集合

NULL:永远等待,直到捕捉到信号或文件描述符已准备好为止

具体值:struct timeval类型的指针,若等待为timeout时间还没有文件描

符准备好,就立即返回

函数传入值

timeout

0:从不等待,测试所有指定的描述符并立即返回

函数返回值

成功:准备好的文件描述符

-1:出错

思考

请读者考虑一下如何确定最高的文件描述符?

可以看到,select 函数根据希望进行的文件操作对文件描述符进行了分类处理,这里,

对文件描述符的处理主要涉及到4 个宏函数,如表6.9 所示。

6.9 select文件描述符处理函数

FD_ZERO(fd_set *set) 清除一个文件描述符集

FD_SET(int fd,fd_set *set) 将一个文件描述符加入文件描述符集中

FD_CLR(int fd,fd_set *set) 将一个文件描述符从文件描述符集中清除

FD_ISSET(int fd,fd_set *set) 测试该集中的一个给定位是否有变化

一般来说,在使用select 函数之前,首先使用FD_ZERO FD_SET 来初始化文件描述

符集,在使用了select 函数时,可循环使用FD_ISSET 测试描述符集,在执行完对相关后文

件描述符后,使用FD_CLR来清楚描述符集。

另外,select函数中的timeout是一个struct timeval类型的指针,该结构体如下所示:

struct timeval {

long tv_sec; /* second */

long tv_unsec; /* and microseconds*/

}

可以看到,这个时间结构体的精确度可以设置到微秒级,这对于大多数的应用而言已经

足够了。

3)使用实例

由于 Select函数多用于I/O 操作可能会阻塞的情况下,而对于可能会有阻塞I/O 的管道、

网络编程,本书到现在为止还没有涉及。因此,本例主要表现了如何使用select 函数,而其

中的I/O 操作是不会阻塞的。

本实例中主要实现将文件hello1 里的内容读出,并将此内容每隔10s 写入hello2 中去。

在这里建立了两个描述符集,其中一个描述符集inset1 是用于读取文件内容,另一个描述符

inset2是用于写入文件的。两个文件描述符fds[0]fds[1]分别指向这一文件描述符。在首

先初始化完各文件描述符集之后,就开始了循环测试这两个文件描述符是否可读写,由于在

这里没有阻塞,所以文件描述符处于准备就绪的状态。这时,就分别对文件描述符fds[0]

fsd[1]进行读写操作。该程序的流程图如图6.2 所示。

/*select.c*/

#include

#include

#include

#include

#include

int main(void)

{

int fds[2];

char buf[7];

int i,rc,maxfd;

fd_set inset1,inset2;

struct timeval tv;

/*首先按一定的权限打开hello1文件*/

if((fds[0] = open ("hello1", O_RDWR|O_CREAT,0666))<0)

perror("open hello1");

/*再按一定的权限打开hello2文件*/

if((fds[1] = open ("hello2", O_RDWR|O_CREAT,0666))<0)

perror("open hello2");

if((rc = write(fds[0],"Hello!\n",7)))

printf("rc=%d\n",rc);

lseek(fds[0],0,SEEK_SET);

/*取出两个文件描述符中的较大者*/

maxfd = fds[0]>fds[1] ? fds[0] : fds[1];

/*初始化读集合inset1,并在读集合中加入相应的描述集*/

FD_ZERO(&inset1);

FD_SET(fds[0],&inset1);

/*初始化写集合inset2,并在写集合中加入相应的描述集*/

FD_ZERO(&inset2);

FD_SET(fds[1],&inset2);

tv.tv_sec=2;

tv.tv_usec=0;

/*循环测试该文件描述符是否准备就绪,并调用select函数对相关文件描述符做对应操作*/

while(FD_ISSET(fds[0],&inset1)||FD_ISSET(fds[1],&inset2)){

if(select(maxfd+1,&inset1,&inset2,NULL,&tv)<0)

perror("select");

else{

if(FD_ISSET(fds[0],&inset1)){

rc = read(fds[0],buf,7);

if(rc>0){

buf[rc]='\0';

printf("read: %s\n",buf);

}else

perror("read");

}

if(FD_ISSET(fds[1],&inset2)){

rc = write(fds[1],buf,7);

if(rc>0){

buf[rc]='\0';

printf("rc=%d,write: %s\n",rc,buf);

}else

perror("write");

sleep(10);

}

}

}

exit(0);

}

阅读(1767) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-12-26 14:45:51

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com