select是在一定时间内,监听用户注册的可读、可写、异常事件。它的可读、可写、异常事件分别对应着文件描述符的集合。当有事件发生的时候,内核就会修改这些参数来告诉应用程序哪些文件描述符以及就绪了。这样的话下次调用select时就需要重新设置可读、可写、异常事件的文件描述符。
select系统调用的顺序是:
select () -> sys_select() -> do_select ()
所以,select是系统调用,它进入内核态就调用sys_select()
typedef struct
{
unsigned long *in, *out, *ex;
unsigned long *res_in, *res_out, *res_ex;
}fd_set_bits;
/*
in out ex分别保存用户注册的感兴趣的事件,res_in,res_out,res_ex分别保存这个文件描述符上的用户感兴趣的事件,
返回的时候把res_in res_out res_ex的值赋给in,out,ex,这就是从用户空间拷贝到内核空间,然后再从内核空间拷贝到用户空间。
因为select这样每次调用的时候需要来回拷贝,所以造成效率问题。
当fd_set_bits这个结构体整合好之后当做参数,作为参数传递给do_select()
*/
//sys_select是处理时间函数
asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp)
{
fd_set_bits fds;//这个结构体保存用户传进来的参数
char *bits;
long timeout;
int ret, size, max_fdset;
//从用户进程拷贝超时时间,将超时时间换成时钟周期数
timeout = MAX_SCHEDULE_TIMEOUT;//检查事件会不会永远等待下去
if (tvp) {
//对timeval超时时间参数处理
time_t sec, usec;
if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp)))
|| (ret = __get_user(sec, &tvp->tv_sec))
|| (ret = __get_user(usec, &tvp->tv_usec)))
goto out_nofds;
ret = -EINVAL;
if (sec < 0 || usec < 0)
goto out_nofds;
//进行单位换算
if ((unsigned long) sec < MAX_SELECT_SECONDS) {
timeout = ROU