对select()参数fdset的完整理解
虽然写了很多代码,但select我就从没有完整理解过,要用时不过copy paste而已。惭愧!
今天决定要对select()参数fdset有一个完整理解。Go!
先上一段代码(代码1-1),这段代码做的事情是
1. 创建一个socket来listen请求
2. 调用select等待新请求、等待已有请求的数据收发状态READY
3. 当有新连接请求来的时候就调用accpet接受请求
4. 当有数据可读可写的时候就调用do_read/do_write执行数据收发
5. loop回去
注意:上面所有的操作都是在一个线程上做的。
看完代码,再看for循环内几个细节。
细节1. select前每次都要对三个fdset进行初始化。具体初始化策略就是:
- listen fd总是要set,这表明希望select关心listen fd的变化
- 已经accept了的fd总是要set,这表明希望select关心这些fd上是否可读可写
细节2. select后会对三个fdset的状态进行检查。具体检查结果是:
- listen fd如果被set了,表明有新的连接请求来了,需要调用accept
- readfdset中如果有fd被set了,表明那个fd上有新数据可读
- writefdset中如果有fd被set了,表明那个fd上可以写数据进去
从上面两个细节中可以发现
int do_select(int n, fd_set_bits *fds, struct timespec *end_time){ ktime_t expire, *to = NULL; struct poll_wqueues table; poll_table *wait; int retval, i, timed_out = 0; unsigned long slack = 0; rcu_read_lock(); retval = max_select_fd(n, fds); rcu_read_unlock(); if (retval < 0) return retval; n = retval; poll_initwait(&table); [color=Red]wait = &table.pt;[/color] if (end_time && !end_time->tv_sec && !end_time->tv_nsec) { wait = NULL; timed_out = 1; } if (end_time && !timed_out) slack = estimate_accuracy(end_time); retval = 0; for (;;) { unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;// fdset的输入参数,表明关心哪些fd inp = fds->in; outp = fds->out; exp = fds->ex;// fdset的输出参数,表明哪些关心的fd状态变成了可读可写可连接 rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;// 这里看看n,就是外面传进来的max_fd,就很能理解为什么max_fd需要加1了 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; const 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) {// 性能优化代码。一次判断4个字节,32个fd,如果都不关心,则可以一次跳过 i += __NFDBITS; continue; } // 32个fd中至少有一个fd是被用户关心的,得瞧瞧 for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) { int fput_needed; if (i >= n) break; if (!(bit & all_bits)) continue; file = fget_light(i, &fput_needed); if (file) { f_op = file->f_op; mask = DEFAULT_POLLMASK; if (f_op && f_op->poll) //调用pool看这个fd是否ready mask = (*f_op->poll)(file, retval ? NULL : wait); fput_light(file, fput_needed); 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++; } } } if (res_in) *rinp = res_in; if (res_out) *routp = res_out; if (res_ex) *rexp = res_ex; cond_resched(); } return retval;}对select的理解欲是被昨晚看的《编程人生》第一篇文章激发的, Jamie Zawinski:好奇心是驱动一个程序员的终极原动力,唯有好奇心才能让你对code了如指掌。
我的微博:http://weibo.com/raywill2