1 简介
select()
允许一个程序监听多个文件描述符,等待一个或者多个文件描述符的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);
#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);
int nfds
参数表示待监听的集合里的最大文件描述符的值 + 1。
fd_set *readfds
、fd_set *writefds
、fd_set *exceptfds
三个集合分别存放需要监听读、写、异常三个操作的文件描述符。
struct timeval *timeout
表示超时时间。设为0则立刻扫描并返回,设为NULL则永远等待,直到有文件描述符就绪。
2 SYSCALL_DEFINE5(select, …
阅读的Linux内核版本:linux-2.6.32.68
select源码位于fs/select.c文件
SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp,
fd_set __user *, exp, struct timeval __user *, tvp)
{
struct timespec end_time, *to = NULL;
struct timeval tv;
int ret;
if (tvp) {
if (copy_from_user(&tv, tvp, sizeof(tv)))
return -EFAULT;
to = &end_time;
if (poll_select_set_timeout(to,
tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
(tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC)) //微秒转纳秒
return -EINVAL;
}
ret = core_sys_select(n, inp, outp, exp, to);
ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);
return ret;
}
select函数执行从此开始,关键调用流程如下: select -> core_sys_select() -> do_select() 。
selcet的主要操作在do_select()
函数中完成。
在上述函数中,主要把超时时间tvp
的值从用户空间复制到内核空间,并且调用poll_select_set_timeout()
函数把超时时间的长度加到当前时间上,获得最终的结束时间点to
。由于poll_select_set_timeout()
的时间精度是纳秒,所以需要转换。
之后调用core_sys_select()
函数执行主要逻辑。
在主要程序执行完之后,还会调用poll_select_copy_remaining()
把等待时间中的剩余时间返回给用户态的tvp
。
3 core_sys_select()
/*
* We can ac