Linux 平台 select 操作
#include
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)
select()
操作用于程序监控相应的文件描述符(file descriptor,fd)是否准备好,即能否进行无阻塞地读取操作。
文件描述符集(file descriptor set,fds)是函数最主要的参数,它是一个 fd_set
类型的结构体,里面可以存储最大 FD_SETSIZE(1024)
个 fd。为了安全地操作 fds,系统提供了相应的宏:
-
void FD_ZERO(fd_set *set)
- 用于清空 fds 里存储的 fd,一般在初始化时清空 fds 。 -
void FD_SET(int fd, fd_set *set)
- 用于向 fds 里添加一个 fd 。 -
void FD_CLR(int fd, fd_set *set)
- 用于从 fds 里清除一个 fd 。 -
int FD_ISSET(int fd, fd_set *set)
- 用于判断 fd 是否在 fds 里,一般在 select() 调用结束之后使用。
函数参数有三个 fds:
fds | 函数调用前 | 函数调用后 |
---|---|---|
readfds | 监听 readfds 里的 fd 是否有可读的 | 可无阻塞读的 fd |
writefds | 监听 writefds 里的 fd 是否有可写的 | 可无阻塞写的 fd |
exceptfds | 监听 exceptfds 里的 fd 是否有发送异常的(一般未使用) | 发生异常的 fd |
select()
函数内部会修改 readfds, writefds 和 exceptfds 内存储的 fd,函数返回时这些 fds 内存储的是可以进行相应操作的 fd 。因此若希望在一个 loop 里重复监听某一个 fd,需要每次调用 select()
之前重新设置 fds 。
函数参数 nfds
是三个 fds 里序号最大的 fd 的序号值 + 1 。
timeout
是一个struct timeval
的结构体指针,它代表希望阻塞等待的时间。函数返回时,Linux 平台下的 timeout
会被重写,用于存储阻塞剩余未使用的时间,而其他平台的实现一般不会这样做。因此考虑到兼容性,一般认为 select() 返回后,timeout 对象就没用了,下次调用 select() 需要重新初始化 timeout 。
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
- 当传入的 timeout 里的字段值大于 0 ,select() 函数阻塞相应的时间。当一个 fd 准备好,被信号处理中断,以及阻塞时间超时时,函数返回。
- 当传入的 timeout 里的
tv_sec
和tv_usec
都为 0 时,select() 函数立即返回,用于轮询的场景。 - 当传入的 timeout 为 NULL 时,select() 永久阻塞,直到有 fd 准备好。
函数返回值可以为:
- EBADF - 错误的 fd 。
- EINTR - 捕获到信号。
- EINVAL -
nfds
为负数或大于 RLIMIT_NOFILE 的限制,timeout
的值不合理。 - ENOMEN - 函数内部动态分配内存失败。
一个 demo 如下,简单地监控标准输入:
#include
#include
#include
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");
exit(EXIT_SUCCESS);
}
无任何输入场景:
tangjia@FA001334:~/Jackistang$ ./tmp
No data within five seconds.
输入数据场景:
tangjia@FA001334:~/Jackistang$ ./tmp
123456798
Data is available now.
参考:
- select(2) — Linux manual page