您的位置:首页 > 其它

poll机制之内核实现简要分析

2017-08-07 23:13 387 查看
int poll(struct pollfd *fds, nfds_t nfds, int timeout);



意思就是说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 内核