poll机制之内核实现简要分析
2017-08-07 23:13
387 查看
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
![](https://img-blog.csdn.net/20170807223810609?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGVhZDU1NQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
意思就是说poll函数功能类似select函数,即等待文件描述符可进行I/O操作,比如数据的读写,标准输入之类的。文件描述符被fds所监听,当发生fds中的事件poll就返回。(即将调用poll的进程加入对应内核驱动的等待队列,等待数据可读,或超时就唤醒)
先来个大概的流程:当我们在应用中调用poll函数,因为poll函数是系统调用,进而调用内核的sys_poll——>do_sys_poll——>poll_iniiwait()
——>init_poll_funcptr(&pwq->pt, __pollwait); poll_initwait函数注册一个回调函数__pollwait,它就是我们的驱动程序执行poll_wait时,真正被调用的函数,调用poll的进程加入对应内核驱动的等待队列,但不立即休眠。
do_sys_poll->poll_iniiwait()后 do_sys_poll->do_poll(nfds, head, &table, timeout)
sys_poll
未加粗代码只是对参数timeout_msecs的处理。为do_sys_poll函数使用
3、do_sys_poll
do_sys_poll函数也位于位于fs/select.c文件中,我们忽略其他代码:
do_sys_poll函数中最为重要的就是上面的两个函数。
3.1poll_initwait函数:这个函数非常简单但很重要,它初始化一个poll_wqueues变量table:这个结构体是poll机制中最为重要的一个数据结构,poll_wqueues在它的内部有另外一个结构体为poll_table_entry,这个结构体就是包含了驱动程序poll函数中的poll_wait(file, &button_waitq, wait)函数中传进来的三个参数
即table->pt->qproc = __pollwait,复杂在这个__pollwait函数,这是一个回调函数,__pollwait将在驱动的poll函数里调用poll_wait时调用到。
tatic inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && wait_address)
p->qproc(filp, wait_address, p);
}
这是根据p->qproc(filp, wait_address, p)中的参数执行回调函数。这些参数又是哪里来的呢?
首先
1、wait_queue_head_t * wait_address这个是我们自己在驱动编写时,自己定义的wait_queue_head_t变量(到时我们的进程就是挂载在这个变量中的的相关成员链表上)
2、对于struct file * filp和poll_table *p:首先想想,驱动中的poll是我们自己编写的,里面执行poll_wait函数,那驱动的poll函数是谁调用执行的呢,其实是我们待会要分析的do_poll函数中相关函数调用的,待会进行分析。
所以poll_initwait就是对struct poll_wqueues table变量进行相关初始化。
`
3.2 do_poll
在do_poll中,会进入一个for死循环,
进行do_pollfd(pfd, pt);
分析:
获得文件描述符——>获得file结构体——>判断是否有编写驱动poll函数——>调用驱动poll并传入file, pwait参数
这里调用poll驱动函数,接着调用poll_wait函数,上面有说的讨论的poll_wait函数参数哪来的就是这样来说。所以执行:
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && wait_address)
p->qproc(filp, wait_address, p);
}
因为table->pt->qproc = __pollwait,所以__pollwait函数就被执行,将当前进程加入等待队列。
意思就是说poll函数功能类似select函数,即等待文件描述符可进行I/O操作,比如数据的读写,标准输入之类的。文件描述符被fds所监听,当发生fds中的事件poll就返回。(即将调用poll的进程加入对应内核驱动的等待队列,等待数据可读,或超时就唤醒)
先来个大概的流程:当我们在应用中调用poll函数,因为poll函数是系统调用,进而调用内核的sys_poll——>do_sys_poll——>poll_iniiwait()
——>init_poll_funcptr(&pwq->pt, __pollwait); poll_initwait函数注册一个回调函数__pollwait,它就是我们的驱动程序执行poll_wait时,真正被调用的函数,调用poll的进程加入对应内核驱动的等待队列,但不立即休眠。
do_sys_poll->poll_iniiwait()后 do_sys_poll->do_poll(nfds, head, &table, timeout)
sys_poll
asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, long timeout_msecs) { s64 timeout_jiffies; if (timeout_msecs > 0) { #if HZ > 1000 /* We can only overflow if HZ > 1000 */ if (timeout_msecs / 1000 > (s64)0x7fffffffffffffffULL / (s64)HZ) timeout_jiffies = -1; else #endif timeout_jiffies = msecs_to_jiffies(timeout_msecs); } else { /* Infinite (< 0) or no (0) timeout */ timeout_jiffies = timeout_msecs; } return do_sys_poll(ufds, nfds, &timeout_jiffies); }
未加粗代码只是对参数timeout_msecs的处理。为do_sys_poll函数使用
3、do_sys_poll
do_sys_poll函数也位于位于fs/select.c文件中,我们忽略其他代码:
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) { struct poll_wqueues table; …… poll_initwait(&table); …… fdcount = do_poll(nfds, head, &table, timeout); …… }
do_sys_poll函数中最为重要的就是上面的两个函数。
3.1poll_initwait函数:这个函数非常简单但很重要,它初始化一个poll_wqueues变量table:这个结构体是poll机制中最为重要的一个数据结构,poll_wqueues在它的内部有另外一个结构体为poll_table_entry,这个结构体就是包含了驱动程序poll函数中的poll_wait(file, &button_waitq, wait)函数中传进来的三个参数
void poll_initwait(struct poll_wqueues *pwq) { init_poll_funcptr(&pwq->pt, __pollwait); pwq->error = 0; pwq->table = NULL; pwq->inline_index = 0; } static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc) { pt->qproc = qproc; }
即table->pt->qproc = __pollwait,复杂在这个__pollwait函数,这是一个回调函数,__pollwait将在驱动的poll函数里调用poll_wait时调用到。
tatic inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && wait_address)
p->qproc(filp, wait_address, p);
}
这是根据p->qproc(filp, wait_address, p)中的参数执行回调函数。这些参数又是哪里来的呢?
首先
1、wait_queue_head_t * wait_address这个是我们自己在驱动编写时,自己定义的wait_queue_head_t变量(到时我们的进程就是挂载在这个变量中的的相关成员链表上)
2、对于struct file * filp和poll_table *p:首先想想,驱动中的poll是我们自己编写的,里面执行poll_wait函数,那驱动的poll函数是谁调用执行的呢,其实是我们待会要分析的do_poll函数中相关函数调用的,待会进行分析。
所以poll_initwait就是对struct poll_wqueues table变量进行相关初始化。
`
3.2 do_poll
在do_poll中,会进入一个for死循环,
进行do_pollfd(pfd, pt);
static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait) { unsigned int mask; int fd; mask = 0; fd = pollfd->fd;//获得文件描述符 if (fd >= 0) { int fput_needed; struct file * file; file = fget_light(fd, &fput_needed);//获得file结构体 mask = POLLNVAL; if (file != NULL) { mask = DEFAULT_POLLMASK; if (file->f_op && file->f_op->poll)//判断是否有编写驱动poll函数 mask = file->f_op->poll(file, pwait);//调用驱动poll并传入file, pwait参数 mask &= pollfd->events | POLLERR | POLLHUP; fput_light(file, fput_needed); } } pollfd->revents = mask; return mask; }
分析:
获得文件描述符——>获得file结构体——>判断是否有编写驱动poll函数——>调用驱动poll并传入file, pwait参数
这里调用poll驱动函数,接着调用poll_wait函数,上面有说的讨论的poll_wait函数参数哪来的就是这样来说。所以执行:
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && wait_address)
p->qproc(filp, wait_address, p);
}
因为table->pt->qproc = __pollwait,所以__pollwait函数就被执行,将当前进程加入等待队列。
相关文章推荐
- poll系统调用的内核态实现机制分析
- poll系统调用的内核态实现机制分析
- linux路由内核实现分析(四)---路由缓存机制(3)
- select,poll,epoll实现分析—结合内核源代码 http://blog.csdn.net/vividonly/article/details/7539342
- 从ScrollView嵌套EditText的滑动事件冲突分析触摸事件的分发机制以及TextView的简要实现和冲突的解决办法
- 唯快不破:结合内核实现源码分析 select poll epoll区别
- select,poll,epoll实现分析—结合内核源代码
- Linux 内核驱动--阻塞与非阻塞机制及Poll/Select分析
- linux路由内核实现分析(四)---路由缓存机制(4)
- Linux内核抢占实现机制分析(转)
- windows的IO管理器内核实现机制原理分析
- Linux内核抢占实现机制分析(转)
- select,poll,epoll实现分析—结合内核源代码
- Linux 内核机制--阻塞与非阻塞机制及Poll/Select分析
- mmap内核空间映射结合kfifo,poll机制的实现例子
- 从ScrollView嵌套EditText的滑动事件冲突分析触摸事件的分发机制以及TextView的简要实现和冲突的解决办法
- Linux内核抢占实现机制分析
- 事件触发机制:Poll,Select和Epoll实现原理分析
- Linux内核抢占实现机制分析
- Linux2.6内核 ACL 机制数据结构和实现分析