Select函数源码剖析
I/O复用函数——select
select是最基础的IO复用函数,对于其实现,做了一定的了解,记录如下:
源码一进来就可以发现,它的事件是通过宏来实现的
#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; }
源码剖析就到这了,流程总结一下:
- selectIO复用函数,首先需要定义位图(struct fd_set),如果有超时事件还需要超时结构(struct timeval)。
- 首先必须对容器进行清空!——(FD_ZERO(fds)),利用FD_SET(fd, fds)来添加好描述符,做好准备工作
- 调用int select(int maxfdp1,fd_set readset,fd_set writeset,fd_set exceptset,const struct timeval timeout); 返回值会返回就绪描述符的数目,超时返回0,出错返回-1。
- 检测具体是否有数据流动FD_ISSET(fd,&fds);
相关推荐
专注前端开发 2020-10-21
苏康申 2020-11-13
vitasfly 2020-11-12
oraclemch 2020-11-06
liuyang000 2020-09-25
FellowYourHeart 2020-10-05
赵继业 2020-08-17
whyname 2020-08-16
Seandba 2020-08-16
dbasunny 2020-08-16
拼命工作好好玩 2020-08-15
langyue 2020-08-15
写程序的赵童鞋 2020-08-03
Accpcjg 2020-08-02
tydldd 2020-07-30
好记忆也需烂 2020-07-28
jianghero 2020-07-28