在 Linux 编程时遇到问题最方便的就是应用
man
命令, 每一个知识点都讲解得很详细, 所以遇到问题最佳的办法是先man
读懂之后再去网络中搜资料。
为方便阅读,此文翻译了man select
的内容。
函数原型及相关说明
/* 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);`enter code here`
void FD_ZERO(fd_set *set);
#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 操作的 ready
状态。
select()
和 pselect()
只有三点区别:
select()
的 timeout
结构体为 struct timeval
(包含秒和毫秒)而 pselect
使用的 timeout
结构体为struct timespec
(包含秒和纳秒)。
select()
可能会更新timeout
参数来指示还剩多长时间,pselect()
则不会修改 timeout
参数。
select()
没有 sigmask
参数与 pselect()
的 sigmask
参数为NULL
时功能完全相同。
监听的三个独立文件描述符集:
readfds
监听文件描述符是否可读,不监听可以传入 NULL
。
writefds
监听文件描述符是否可写 ,不监听可以传入 NULL
。
exceptfds
监听文件描述符是否有异常,不监听可以传入 NULL
。
提供了四个宏定义来操作描述符集:
FD_ZERO()
- 清空描述符集
FD_SET()
- 添加文件描述符到描述符集
FD_CLR()
- 从描述符集中移除文件描述符
FD_ISSET()
- 测试一个文件描述符是否在当前描述符集中 - 用于 select
返回后
nfds
是 select
监控的三个描述符中的最大值加1
timeout
指定 select
的等待阻塞时间,如果 timeout
结构体指定的时间为0则 select
会立即返回,如果 timeout
为 NULL
则 select
会一直等待直到有描述符状态被改变。
sigmask
是一个信号掩码的指针,如果指针不为空,则 pselect
先屏蔽 sigmask
指定的信号,然后执行 select
函数,最后再恢复sigmask指定的信号。
ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask);
相当于
sigset_t origmask;
pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
pthread_sigmask(SIG_SETMASK, &origmask, NULL);
在需要避免竞争的状态下使用 pselecet
。
超时结构体
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
和
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
注:每次调用 select
之前都应该重新初始化 timeval
结构体变量。
返回值
成功时返回包含三种描述符集的文件描述符的个数(已经准备好的描述符个数)
超时时返回 0
错误时返回 -1,并且将描述符集清空。
错误
EBADF
— 指定的需要检测的文件描述符集不合法(一个已经关闭的描述符,或者一个出现错误的描述符)
EINTR
— 信号被捕捉,详见 signal
EINVAL
— nfds
是负数或者 timeout
中的值不合法
ENOMEM
— 不能为内部表分配内存
注意
文件描述 fd
必须小于 FD_SETSIZE
否则可能会导致未知错误。
当 select
监听的文件描述符在其他线程中被关闭时,会导致未知的结果。在一些 Unix 系统中会导致 select()
函数不阻塞立即返回,并且指定这个文件描述符是 ready
状态。在 Linux 将不会对 select
造成影响。因此,在程序设计时应考虑这种现象以防止留下隐患。
Linux 提供了使用用例详见:
man select_tut