select() - Unix、Linux 系统调用
广告
名称
select、pselect、FD_CLR、FD_ISSET、FD_SET、FD_ZERO - 同步 I/O 多路复用
语法
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
#define _XOPEN_SOURCE 600
#include <sys/select.h>
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask);
|
描述
select() 和 pselect() 允许程序监视多个文件描述符,等待一个或多个文件描述符准备就绪以进行某种类型的 I/O 操作(例如,输入可能)。如果可以执行相应的 I/O 操作(例如,read(2))而不会阻塞,则文件描述符被认为已准备就绪。
select() 和 pselect() 的操作相同,但有三个区别
序号 | 描述 |
(i) | select() 使用一个 struct timeval(秒和微秒)类型的超时,而 pselect() 使用一个 struct timespec(秒和纳秒)类型的超时。 |
(ii) | select() 可能会更新 timeout 参数以指示剩余时间。pselect() 不会更改此参数。 |
(iii) | select() 没有 sigmask 参数,并且其行为类似于使用 NULL sigmask 调用 pselect()。 |
监视三组独立的文件描述符。列在 readfds 中的文件描述符将被监视,以查看是否有字符可供读取(更准确地说,查看读取是否不会阻塞;特别是,文件描述符在文件结束时也已准备就绪),列在 writefds 中的文件描述符将被监视,以查看写入是否不会阻塞,列在 exceptfds 中的文件描述符将被监视,以查看异常。退出时,集合会在原地修改以指示哪些文件描述符实际上更改了状态。如果没有任何文件描述符要监视相应的事件类别,则三组文件描述符中的每一组都可以指定为 NULL。
提供了四个宏来操作集合。
这在 select() 返回后很有用。
nfds 是三组集合中编号最高的描述符加 1。
timeout 是 select() 返回之前经过时间的上限。它可以为零,导致 select() 立即返回。(这对于轮询很有用。)如果 timeout 为 NULL(无超时),则 select() 可以无限期阻塞。
sigmask 是指向信号掩码的指针(请参阅 sigprocmask(2));如果它不为 NULL,则 pselect() 首先用 sigmask 指向的信号掩码替换当前信号掩码,然后执行“select”函数,然后恢复原始信号掩码。
除了 timeout 参数精度上的差异外,以下 pselect() 调用
ready = pselect(nfds, &readfds, &writefds, &exceptfds,
timeout, &sigmask);
|
等效于原子地执行以下调用
sigset_t origmask;
sigprocmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
sigprocmask(SIG_SETMASK, &origmask, NULL);
|
需要 pselect() 的原因是,如果要等待信号或文件描述符准备就绪,则需要原子测试以防止竞争条件。(假设信号处理程序设置全局标志并返回。然后,在调用 select() 之前测试此全局标志可能会无限期挂起,如果信号恰好在测试后但调用之前到达。相反,pselect() 允许首先阻塞信号,处理已到达的信号,然后使用所需的 sigmask 调用 pselect(),从而避免了竞争条件。)
超时
涉及的时间结构在 <sys/time.h> 中定义,如下所示
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
|
和
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
|
(但是,请参见下面关于 POSIX.1-2001 版本的内容。)一些代码使用所有三组都为空、n 为零以及非 NULL timeout 调用 select() 作为以亚秒精度休眠的相当便携的方式。
在 Linux 上,select() 会修改 timeout 以反映未休眠的时间;大多数其他实现不会这样做。(POSIX.1-2001 允许这两种行为。)这会导致在将读取 timeout 的 Linux 代码移植到其他操作系统时出现问题,以及在将代码移植到 Linux 时出现问题,这些代码在循环中对多个 select() 重复使用 struct timeval 而无需重新初始化它。在 select() 返回后,认为 timeout 未定义。
返回值
如果成功,select() 和 pselect() 将返回三个返回的描述符集中包含的文件描述符的数量(即,在 readfds、writefds、exceptfds 中设置的位的总数),如果在发生任何有趣的事情之前超时过期,则该数量可能为零。如果出错,则返回 -1,并且 errno 被相应地设置;集合和 timeout 变得未定义,因此在错误后不要依赖其内容。
错误
标签 | 描述 |
EBADF | 在一组中给出了无效的文件描述符。(可能是已关闭的文件描述符,或已发生错误的文件描述符。) |
EINTR | 捕获到信号。 |
EINVAL | nfds 为负或 timeout 中包含的值无效。 |
ENOMEM | 无法为内部表分配内存。 |
示例
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
fd_set rfds;
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Don’t rely on the value of tv now! */
if (retval == -1)
perror("select()");
else if (retval)
printf("Data is available now.\n");
/* FD_ISSET(0, &rfds) will be true. */
else
printf("No data within five seconds.\n");
return 0;
}
|
符合标准
select() 符合 POSIX.1-2001 和 4.4BSD
pselect() 在 POSIX.1g 和 POSIX.1-2001 中定义。
广告
|