源码一进来就可以发现,它的事件是通过宏来实现的
#define FDS_IN(fds, n) (fds->in + n) //读事件
#define FDS_OUT(fds, n) (fds->out + n) //写事件
#define FDS_EX(fds, n) (fds->ex + n) //异常事件
#define BITS(fds, n) (FDS_IN(fds, n)|FDS_OUT(fds, n)|*FDS_EX(fds, n))//通过一个位图可以同时监听三种事件
这就是位图本来的样子
typedef struct {
unsigned long *in, *out, *ex;
unsigned long *res_in, *res_out, *res_ex;
} fd_set_bits;
这是select的函数主体
int do_select(int n, fd_set_bits *fds, long *timeout);
首先找出最大的文件描述符
spin_lock(¤t->files->file_lock);
retval = max_select_fd(n, fds);
spin_unlock(¤t->files->file_lock);
if (retval < 0)
return retval; //如果最大值还是小于0,报错
n = retval; //将n设置成它最大值加1
这里可以看一下max_select_fd()函数,目的是找出最大的fd的值+1
static int max_select_fd(unsigned long n, fd_set_bits *fds)
{
unsigned long *open_fds;
unsigned long set;
int max;
/* handle last in-complete long-word first */
set = ~(~0UL << (n & (__NFDBITS-1))); //将传入的n最高位往后取1(究竟有多少要检查的fd)
n /= __NFDBITS; //类似于哈希函数一样,对n取一个下标位置
open_fds = current->files->open_fds->fds_bits+n; //打开fd
max = 0;
if (set) {
set &= BITS(fds, n); //寻找一下n在fds位图中的位置
if (set) { //如果在容器中已经存在
if (!(set & ~*open_fds)) //检查一下文件是不是已经打开?
goto get_max; //打开都OK的话那么找到最大值开始遍历吧
return -EBADF;
}
}
//开始遍历
while (n) {
open_fds--;
n--;
set = BITS(fds, n); //检查在n处有没有注册的fd
if (!set) //如果没有,抬走下一位
continue;
if (set & ~*open_fds) //如果有,但是文件没有打开的话报错
return -EBADF;
if (max) //
continue;
get_max:
do {
max++;
set >>= 1;
} while (set); //max出来是最高fd的位数加1
max += n * __NFDBITS; //max出来是最大fd的值加一
}
return max;
}
进行初始化变量
poll_initwait(&table);
wait = &table.pt;
if (!__timeout) //如果没有超时的话,wait置空
wait = NULL;
retval = 0;
核心部分
for (;;) {
unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
set_current_state(TASK_INTERRUPTIBLE);//将此进程设为可中断阻塞
inp = fds->in; outp = fds->out; exp = fds->ex;
rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
//核心中的核心……………………词穷。。。真正开始遍历
for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
unsigned long in, out, ex, all_bits, bit = 1, mask, j;
unsigned long res_in = 0, res_out = 0, res_ex = 0;
struct file_operations *f_op = NULL;
struct file *file = NULL;
in = *inp++; out = *outp++; ex = *exp++;
all_bits = in | out | ex; //所有注册事件
if (all_bits == 0) { i += __NFDBITS;
continue;
}
for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
if (i >= n) //到了最大描述符+1的位置,循环结束
break;
if (!(bit & all_bits)) //如果当前位置没有注册,进入下一次
continue;
file = fget(i); //拿取当前的文件描述符
if (file) { //检测三种事件
f_op = file->f_op;
mask = DEFAULT_POLLMASK;
if (f_op && f_op->poll)
mask = (*f_op->poll)(file, retval ? NULL : wait);
fput(file);
if ((mask & POLLIN_SET) && (in & bit)) {
res_in |= bit;
retval++;
}
if ((mask & POLLOUT_SET) && (out & bit)) {
res_out |= bit;
retval++;
}
if ((mask & POLLEX_SET) && (ex & bit)) {
res_ex |= bit;
retval++;
}
}
cond_resched();
}
if (res_in)
*rinp = res_in;
if (res_out)
*routp = res_out;
if (res_ex)
*rexp = res_ex;
}
//循环退出部分
wait = NULL;
//对所有的文件描述符进行询问后,检查是否有事件就绪、超时或者收到信号,就跳出循环
if (retval || !__timeout || signal_pending(current))
break;
//检查是否出错,如果出错,就跳出循环
if(table.error) {
retval = table.error;
break;
}
__timeout = schedule_timeout(__timeout);
//继续当前进程
__set_current_state(TASK_RUNNING);
//释放位图
poll_freewait(&table);
//更新超时时间
*timeout = __timeout;
return retval;
}