linux select 内核的实现,linux内核selectpoll,epoll实现与区别.pdf

linux内内核核select/poll,,epoll实实现现与与区区别别

下面文章在这段时间内研究 select/poll/epoll的内核实现的一点心得体会:

select,poll,epoll都是多路 用IO 的函数,简单说就是在一个线程里,可以同时处理多个文件描述符的读写。

select/poll的实现很类似,epoll是从select/poll扩展而来,主要是为了解决select/poll天生的缺陷。

epoll在内核版本2.6 以上才出现的新的函数,而他们在linux 内核中的实现都是十分相似。

这三种函数都需要设备驱动提供poll回调函数,对于套接字而言,他们是 tcp_poll,udp_poll和datagram_poll;

对于自己开发的设备驱动而言,是自己实现的poll接口函数。

select实现 (2.6 的内核,其他版本的内核,应该都相差不多)

应用程序调用select,进入内核调用sys_select,做些简单初始化工作,接着进入 core_sys_select,

此函数主要工作是把描述符集合从用户空间 制到内核空间, 最终进入do_select,完成其主要的功能。

do_select里,调用 poll_initwait,主要工作是注册poll_wait的回调函数为__pollwait,

当在设备驱动的poll回调函数里调用poll_wait,其实就是调用__pollwait,

__pollwait的主要工作是把当前进程挂载到等待队列里,当等待的事件到来就会唤醒此进程。

接着执行for循环,循环里首先遍历每个文件描述符,调用对应描述符的poll回调函数,检测是否就绪,

遍历完所有描述符之后,只要有描述符处于就绪状态,信号中断, 出错或者超时,就退出循环,

否则会调用schedule_xxx 函数,让当前进程睡眠,一直到超时或者有描述符就绪被唤醒。

接着又会再次遍历每个描述符,调用poll再次检测。

如此循环,直到符合条件才会退出。

以下是 2.6.3 内核的有关select函数的部分片段:

他他们们调调用用关关系系::

select --> sys_select --> core_sys_select --> do_select

int do_select(int n, fd_set_bits *fds, struct timespec *end_time)

{

ktime_t expire, *to = NULL;

struct poll_wqueues t ble;

poll_t ble *w it;

int retv l, i, timed_out = 0;

unsigned long sl ck = 0;

///这里为了获得集合中的最大描述符,这样可减少循环中遍历的次数。

///也就是为什么linux中select第一个参数为何如此重要了

rcu_re d_lock();

retv l = m x_select_fd(n, fds);

rcu_re d_unlock();

if (retv l < 0)

return retv l;

n = retv l;

初始化 poll_t ble结构,其中一个重要任务是把 __pollw it函数地址赋值给它,

poll_initw it(&t ble);

w it = &t ble.pt;

if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {

w it = NULL;

timed_out = 1;

}

if (end_time && !timed_out)

sl ck = estim te_ ccur cy(end_time);

retv l = 0;

///主循环,将会在这里完成描述符的状态轮训

for (;;) {

unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;

inp = fds->in; outp = fds->out; exp = fds->ex;

rinp = fds->res_in; routp = fds->re

你可能感兴趣的:(linux,select,内核的实现)