select源码剖析
来源:互联网 发布:java实现国际化 编辑:程序博客网 时间:2024/06/09 20:08
linux 2.6.11
select的底层是通过调用sys_select实现的,它将用户的数据拷贝到内核态,select中一个描述符对应一个位,通过给每个事件分配一个bitmap,来对用户的读写,异常事件进行监听,然后由do_select函数去完成链表的建立,回调函数的设置,最后将就绪事件返回给了sys_select,由sys_select把发生就绪事件的描述符返回给用户;
主要用到的数据结构:fd_set_bits,poll_wqueues,
poll_table,poll_table_page,poll_table_entry;
接下来我们进入到sys_select这个函数:(节选部分重要代码)
fd_set_bits fds;char *bits;long timeout;int ret, size, max_fdset;size = FDS_BYTES(n);//判断需要分配多少个longbits = select_bits_alloc(size);//分配6*size个字节;//给fds的成员变量赋值fds.in = (unsigned long *) bits;fds.out = (unsigned long *) (bits + size);fds.ex = (unsigned long *) (bits + 2*size);fds.res_in = (unsigned long *) (bits + 3*size);fds.res_out = (unsigned long *) (bits + 4*size);fds.res_ex = (unsigned long *) (bits + 5*size);//将用户传进来的发生可读可写或者异常的描述符拷贝到内核空间;if ((ret = get_fd_set(n, inp, fds.in)) || (ret = get_fd_set(n, outp, fds.out)) || (ret = get_fd_set(n, exp, fds.ex))) goto out;//对返回字段清零zero_fd_set(n, fds.res_in);zero_fd_set(n, fds.res_out);zero_fd_set(n, fds.res_ex);ret = do_select(n, &fds, &timeout);//将发生事件的描述符拷贝给用户传进来的参数,返回给用户if (set_fd_set(n, inp, fds.res_in) || set_fd_set(n, outp, fds.res_out) || set_fd_set(n, exp, fds.res_ex)) ret = -EFAULT;select_bits_free(bits, size);//释放为每一个事件分配的bitmapreturn ret;
然后进入sys_select的核心函数do_select:
struct poll_wqueues table;poll_table *wait;int retval, i;retval = max_select_fd(n, fds);//根据已经设置好的fd位图检查用户打开的fd, 要求对应fd必须打开, 并且返回最大的fdpoll_initwait(&table);//初始化回调函数,在fop->poll时被调用for (;;) { set_current_state(TASK_INTERRUPTIBLE);//设置当前进程为阻塞状态,直到某一个信号将其唤醒如果当前进程不阻塞了,说明是有就绪事件产生了,或者时间超时了,或者发生异常事件了; for (i = 0; i < n; ++rinp, ++routp, ++rexp) { //检测每一个描述符对应的是否有可读、可写、异常事件发生,如果有,执行下面的for循环 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) { mask = (*f_op->poll)(file, retval ? NULL : wait);//在poll成功后会将本进程唤醒执行,在poll函数里面调用回调函数__pollwait /*retval为返回给用户空间的描述符的总数,由源码可以看出这是in,out,ex集合的总和;*/ 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++; } }}poll_freewait(&table);//释放Poll_table_entry节点,poll_table_page节点;return retval;}
重点说一下f_op->poll操作的作用:
1、查看文件操作状态,有完成或者异常发生,在对应的fdset中标记这些文件描述符;
2、如果retval == 0,并且没有超时,那么通知这些文件,让他们在文件操作完成时唤醒本进程;
3、在f_op->poll中调用了回调函数__pollwait,它的主要作用是将进程放入等待队列中;
接下来让我们看看__pollwait回调函数:
struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt);struct poll_table_page *table = p->table;//获取poll_wqueues中的tablestruct poll_table_page *new_table;//定义一个局部的new_table;new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);//为new_table分配一个页大小的空间new_table->entry = new_table->entries;以头插的方式将new_table插入poll_wqueues中的table单链表中;new_table->next = table;p->table = new_table;table = new_table;/* 添加一个新的entry */{ struct poll_table_entry * entry = table->entry; table->entry = entry+1;//直接加到柔性数组所在地址; get_file(filp); entry->filp = filp; entry->wait_address = wait_address; init_waitqueue_entry(&entry->wait, current); add_wait_queue(wait_address,&entry->wait);}
总结:通过以上源码剖析,我们就能够看出select需要遍历所有的文件描述符,一位一位进行判断,就时间复杂度来说为O(N),在检测用户传进来的文件描述符的时候,我们还需要建立poll_wqueues表,来保存所有等待队列的节点信息,还需要注册回调函数,还记录所有等待队列节点;当把检测到的文件描述符所发生的事件返回给用户的时候,这些表的结构就得释放,等到下一次还需要监听描述符的时候,还需要把每一个描述符及其所对应的事件一起重新注册到内核,内核需要再重新建立表结构,这样的话,就增大了内核的负担;所以说select所监听的描述符是有限制的,一般是1024个文件描述符。select主要适用于连接少,活动连接多的情况。
总结就告一段落了。。。
- select源码剖析
- select源码剖析
- select源码剖析
- select源码剖析
- select、epoll源码剖析与对比
- I/O复用--select函数源码剖析
- 《stl源码剖析》剖析
- Orchid select 剖析
- 【源码】ArrayList源码剖析
- 【源码】LinkedList源码剖析
- 【源码】HashMap源码剖析
- 【源码】HashMap源码剖析
- 【源码】Hashtable源码剖析
- 【源码】LinkedHashMap源码剖析
- 【源码】LruCache源码剖析
- 【源码】TreeMap源码剖析
- 【源码】LinkedHashMap源码剖析
- 【源码】LruCache源码剖析
- tomcat的maxThreads、acceptCount(最大线程数、最大排队数)
- Python copy与deepcopy的区别
- 启动mysql提示Starting MySQL... ERROR! 解决方法
- Unity Joystick 虚拟摇杆的实现
- Java动态代理(主要是对六个参数的理解)
- select源码剖析
- Audio 环形buffer
- 时隔好几个好几天,又到js
- 插入排序(内附代码)
- 使用xshell链接Linux
- SSM(spring +springmvc +mybatis)框架搭建
- day21桶排序一个无序数组+求一个无序数组中的中位数
- HDU 4912 LCA+策略
- Java中Synchronized的用法