select, poll和epoll的区别
select()系统调用提供一个机制来实现同步多元I/O:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select (int n,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);
调用select()将阻塞,直到指定的文件描述符准备好执行I/O,或者可选参数timeout指定的时间已经过去。
监视的文件描述符分为三类set,每一种对应等待不同的事件。readfds中列出的文件描述符被监视是否有数据可供读取(如果读取操作完成则不会阻塞)。writefds中列出的文件描述符则被监视是否写入操作完成而不阻塞。最后,exceptfds中列出的文件描述符则被监视是否发生异常,或者无法控制的数据是否可用(这些状态仅仅应用于套接字)。这三类set可以是NULL,这种情况下select()不监视这一类事件。
select()成功返回时,每组set都被修改以使它只包含准备好I/O的文件描述符。例如,假设有两个文件描述符,值分别是7和9,被放在readfds中。当select()返回时,如果7仍然在set中,则这个文件描述符已经准备好被读取而不会阻塞。如果9已经不在set中,则读取它将可能会阻塞(我说可能是因为数据可能正好在select返回后就可用,这种情况下,下一次调用select()将返回文件描述符准备好读取)。
第一个参数n,等于所有set中最大的那个文件描述符的值加1。因此,select()的调用者负责检查哪个文件描述符拥有最大值,并且把这个值加1再传递给第一个参数。
timeout参数是一个指向timeval结构体的指针,timeval定义如下:
#include <sys/time.h>
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* 10E-6 second */
};
如果这个参数不是NULL,则即使没有文件描述符准备好I/O,select()也会在经过tv_sec秒和tv_usec微秒后返回。当select()返回时,timeout参数的状态在不同的系统中是未定义的,因此每次调用select()之前必须重新初始化timeout和文件描述符set。实际上,当前版本的Linux会自动修改timeout参数,设置它的值为剩余时间。因此,如果timeout被设置为5秒,然后在文件描述符准备好之前经过了3秒,则这一次调用select()返回时tv_sec将变为2。
如果timeout中的两个值都设置为0,则调用select()将立即返回,报告调用时所有未决的事件,但不等待任何随后的事件。
文件描述符set不会直接操作,一般使用几个助手宏来管理。这允许Unix系统以自己喜欢的方式来实现文件描述符set。但大多数系统都简单地实现set为位数组。FD_ZERO移除指定set中的所有文件描述符。每一次调用select()之前都应该先调用它。
fd_set writefds;
FD_ZERO(&writefds);
FD_SET添加一个文件描述符到指定的set中,FD_CLR则从指定的set中移除一个文件描述符:
FD_SET(fd, &writefds); /* add 'fd' to the set */
FD_CLR(fd, &writefds); /* oops, remove 'fd' from the set */
设计良好的代码应该永远不使用FD_CLR,而且实际情况中它也确实很少被使用。
FD_ISSET测试一个文件描述符是否指定set的一部分。如果文件描述符在set中则返回一个非0整数,不在则返回0。FD_ISSET在调用select()返回之后使用,测试指定的文件描述符是否准备好相关动作:
if (FD_ISSET(fd, &readfds))
/* 'fd' is readable without blocking! */
因为文件描述符set是静态创建的,它们对文件描述符的最大数目强加了一个限制,能够放进set中的最大文件描述符的值由FD_SETSIZE指定。在Linux中,这个值是1024。本章后面我们还将看到这个限制的衍生物。
返回值和错误代码
select()成功时返回准备好I/O的文件描述符数目,包括所有三个set。如果提供了timeout,返回值可能是0;错误时返回-1,并且设置errno为下面几个值之一:
EBADF
给某个set提供了无效文件描述符。
EINTR
等待时捕获到信号,可以重新发起调用。
EINVAL
参数n为负数,或者指定的timeout非法。
ENOMEM
不够可用内存来完成请求。
--------------------------------------------------------------------------------------------------------------
poll()系统调用是System V的多元I/O解决方案。它解决了select()的几个不足,尽管select()仍然经常使用(多数还是出于习惯,或者打着可移植的名义):
#include <sys/poll.h>
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
和select()不一样,poll()没有使用低效的三个基于位的文件描述符set,而是采用了一
相关新闻>>
- 发表评论
-
- 最新评论 更多>>