select 在内核中大致实现的一个解说:http://janfan.github.io/chinese/2015/01/05/select-poll-impl-inside-the-kernel.html
等待队列的一些理解:http://www.cnblogs.com/zhuyp1015/archive/2012/06/09/2542894.html
do_select => poll =>fp_poll.
do_select 会对每个fd执行对应的poll操作,poll是file operation (fp)的一个函数指针,具体实现由指向的fp_poll函数实现。fp_poll的核心是调用poll_wait,poll_wait 把当前进程挂到对应fd的设备等待队列,
具体是执行:poll_table*p;p->_qproc 函数, 这个_qproc 是指向 __pollwait 函数 。 这个函数才是真正意义上把 当前进程挂到设备等待队列里面,主要是 调用 add_wait_queue(wait_queue_head_t*q,wait_queue_t*wait), 主要是把wait 加入到wait_queue_head_t里面。其中wait_queue_t 结构体 包括 private (进程信息相关),wait_queue_func_t (进程唤醒后执行的回调函数,一般由设备驱动程序的中断程序执行)。
当把所有的fd和对应的进程都挂到对应的等待队列后,再检查一下fd有没相关的事件,没有的话,如果没有信号中断的情况下,调用 poll_schedule_timeout 函数 进程就会进入休眠,等待唤醒状态。
等待队列的 等待/唤醒:
等待:
等待其实就是把对应的进程信息、唤醒回调函数 包装进入 wati_queue_t 结构体, 然后再由 add_wait_queue 函数把 wait_queue_t 添加到 wait_queue_head_t 里面, wait_queue_head_t 是个链表结构,用来管理wait_queue_t 单元的。
具体底层操作主要是:wait_event_interruptible 函数实现,这个函数会调用 prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); 通过__add_wait_queue把相关的wait 添加到 wait_queue_head_t , 并且把进程的状态置为:TASK_INTERRUPTIBLE 。
以上的操作并不会使进程进入休眠,只把进程挂入等待队列。
接着调用:schedule_timeout 函数,使进程进入休眠,让出CPU。
唤醒:
对于设备驱动来讲,通常是在中断处理函数内唤醒该设备的等待队列。__wake_up(wait_queue_head_t *q, unsignedintmode,intnr_exclusive,void*key) 函数 会被中断程序执行,此函数调用 __wake_up_common ,遍历 传进来的 wait_queue_head_t , 依次执行 wait_queue_t 对应的唤醒回调函数,。
最后 从唤醒函数执行后,finish_wait 函数会被调用,清理相关的信息和资源(从等待队列删除之类的),然后把进程状态置为:TASK_RUNNING