现在的位置: 首页 > 综合 > 正文

select() 和poll() 方法的用法

2013年10月11日 ⁄ 综合 ⁄ 共 3151字 ⁄ 字号 评论关闭

select()poll()方法是复用网络套接字的强大工具。采用这两个方法可以表明过程在什么时候可以安全地执行打开的文件描述符而没有任何延迟。比方说,程序员就可以用这些函数调用得知某个套接字上何时有数据被读取。在给select()poll()指定任务之后你就不必经常性地检查套接字是否有数据要读取了。实际上,select()poll()还可以置于操作系统的后台运行,一旦满足特定事件或者时间超时就会被唤醒。这个进程可以显著地增加程序的效率(假如你更关心程序的性能而非可以移植性,我们在本文的末尾简单讨论了两种替代select()poll()的方法)。

就象你看到的那样,select()poll()在功能上非常相似。在很多情况下,select()poll()的方法实现其实是互相映射的。比方说,在Apache 2.0的核心组件Apache可移植运行时内,可移植接口(portable interface)就是模仿poll()语法提供的。在没有本地poll()实现的平台上, poll()的语法就被映射为select()。在FreeBSD系统上,select()的libc_r实现就完全是对poll()系统调用的简单封装。

select() 说明

Single UNI规范第2版(SUSv2)是这样定义select() 的:
int select(intnfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, structtimeval *timeout);

该函数调用有以下参数:

  • intnfds – 所有给定集合内最高文件描述符加1
  • fd_set *readfds –在有数据读取时触发函数返回的文件描述符
  • fd_set *writefds – 在有数据准备写入时触发函数返回的文件描述符
  • fd_set *errorfds – 发生错误反常时触发函数返回的文件描述符
  • structtimeval *timeoutselect()必须等待事件的最大周期

返回值表示其请求事件满足条件的文件描述符的数目。

你不能通过直接改变fd_set结构值这种方式来修改fd_set结构。设置或者获值的唯一可移植方式是采用FD_*宏:

  • FD_ZERO(fd_set *) –把fd_set初始化为空
  • FD_CLR(intfd, fd_set *) – 从fd_set中删除关联的fd
  • FD_SET(intfd, fd_set *) – 在fd_set中增加关联的fd
  • FD_ISSET(intfd, fd_set *) – 假如fd在fd_set内即可返回非0值

在从select()返回时,FD_ISSET()可以针对给定集合内的每个fd而被调用来表明其条件是否满足。

timeout值的含义是这样的,你可以指定select()等待事件的时间需要多久。假如timeout的值是NULL,那么select()将无限期地等待事件。假如超时的timeval结构设置为0,那么select()将立即返回而不会等待任何事件的发生。否则,timeout则具体定义select()函数等待的时间长度。SUSv2规定所有遵守规范的函数实现都应该支持至少31天的超时。不妨查阅 清单A 了解select()函数的具体应用示例。

poll()说明

poll()方法试图合并select()函数的参数,同时提供范围更广的事件通知。SUSv2 如下定义poll() 函数:
int poll(structpollfdfds[ ], nfds_tnfds, int timeout);

参数含义如下:

  • structpollfdfds[ ] - pollfd结构数组
  • nfds_tnfds - fds[ ]中文件描述符集合的数目
  • int timeout - poll()等待事件发生的时间长度(单位是毫秒)

返回值表示多少fds有事件发生。

pollfd结构通常包括以下结构成员:

  • intfd – 表示某个事件由哪个fd监视
  • short events – 表示哪些事件将被监视的位字段
  • short revents – 表示调用poll()时检测到的事件的比特位

SUSv2规范对以上内容和事件位字段的含义进行了详细的说明。同select()函数相比,poll()在确定被处理的事件类型方面具有更大一些的灵活性。除了读、写和错误通知以外,poll()函数还支持带外和高优先级数据的直接识别。

不同于select()poll()timeout参数是一个简单的整数,表示poll()等待事件的时长。可以赋以特殊的值,通常是 -1 或者常数INFTIM,这是很多旧系统常用的参数,指示poll()永远等待事件。同select()函数一样,0 超时表示 poll()函数必须立即返回。

清单B中,清单A中的select()示例就用poll()代替了。

可替代select()和poll()的方法

即便通过select()或者 poll()函数复用事件通知具有突出的优点,不过,其他具有类似功能的函数实现也可以达到同样的性能。然而,这些实现在跨平台方面没有实现标准化。你必须在使用这些特定函数实现同丧失可移植性之间进行权衡。我们现在就讨论一下两个替代方法:Solaris系统下的/dev/poll和FreeBSD系统下的kqueue。

如你看到的那样,以上两种实现通过以下办法提高了关键的性能,实际上,在现实的应用环境中,开发人员几乎总是针对 select()或者 poll()函数连续地采用同一fds。在每次采用同样参数的时候为了消除这一重复性所带来的麻烦,本来需要检查的fds的就被缓冲了。这一措施在监视大量fds的时候效率特别高,因为某些select()poll()实现存在可伸缩性方面的问题。

Solaris系统下的/dev/poll

在Solaris 7系统上,Sun引入了/dev/poll设备。在使用 /dev/poll的时候,你首先要打开/dev/poll作为一个普通文件。然后构造pollfd结构,方式同普通的poll()函数调用一样。这些pollfd结构随后写入到打开的 /dev/poll 文件描述符。在打开句柄的生存周期内, /dev/poll会根据pollfd结构返回事件(注意,pollfd结构内的事件字段中的特定POLLREMOVE将从/dev/poll的列表中删除对应的fd)。通过调用特定的ioctl (DP_POLL) 和dvpoll,程序就可以从/dev/poll获得需要的信息。在使用dvpoll结构的情况下,发生的事件就可以被检测到了。

Linux支持和其他资源

增加/dev/poll对Linux 2.4系列的支持是有相应补丁程序的,但是,目前这些补丁程序还没有被主流Linux内核接受。在FreeBSD网站上,你可以查阅Solaris、Red Hat以及其他操作系统的有关手册信息了解这方面的情况。

FreeBSD系统下的kqueue

FreeBSD 4.1中推出。FreeBSD的kqueue API设计为比其他对应函数提供更为广泛的事件通知能力。kqueue API提供了一套通用过滤器,可以模仿poll()语法(EVFILT_READEVFILT_WRITE)。不过,它还实现了文件系统变化(EVFILT_VNODE)、进程状态变更(EVFILT_PROC)和信号交付(EVFILT_SIGNAL)的有关通知。要了解有关kqueue的详细情况可以从BSDCon 2000下载Jonathan Lemon的论文(PDF格式)“Kqueue: A generic and scalable event notification facility”

为了进一步了解本文所介绍的select()poll()方法调用,我建议你阅读W. Richard Stevens 撰写的UNIX环境高级编程一书。

 

抱歉!评论已关闭.