当前位置: 代码迷 >> 综合 >> linux poll 和 等待队列休眠的关系
  详细解决方案

linux poll 和 等待队列休眠的关系

热度:79   发布时间:2023-12-15 22:16:03.0

说明:linux版本2.6.37.1

结合相关资料的概括和总结,在此做个记录,有误之处请网友指正,谢谢!


1.poll机制和等待队列

应用层通过系统调用poll函数进入内核,内核执行相应的sys_poll函数。在sys_poll函数中调用do_sys_poll函数。do_sys_poll函数通过调用poll_initwait

函数初始化poll_wqueues变量table,并且将__pollwait函数赋值给table。这里的__pollwait函数将在驱动的poll方法中通过调用poll_wait函数来执行。

poll_initwait函数执行完后,do_sys_poll函数会调用do_poll函数。

在do_poll函数中有一个for的死循环,退出条件为count或者timeout为非零。

count为非零标识do_pollfd函数返回的mask为真,timeout表示定时时间到。

在do_pollfd函数中通过mask = file->f_op->poll来调用驱动中的poll方法,并且获得驱动中操作poll后的mask值。

在for死循环中,首先轮询整条poll_list。在所有等待poll的操作中寻找是否有已满足条件的操作,有则跳出循环

没有则继续向下执行。


上图中的唤醒操作是通过驱动中调用wake_up函数实现的。LDD3上的例子并不完整,

可以通过其他博文来学习驱动中poll的使用。

总结下poll的机制:

1.poll->sys_poll->do_sys_poll->poll_initwait

void poll_initwait(struct poll_wqueues *pwq)
{
    init_poll_funcptr(&pwq->pt, __pollwait);
    pwq->polling_task = current;
    pwq->triggered = 0;
    pwq->error = 0;
    pwq->table = NULL;
    pwq->inline_index = 0;
}

此处需注意init_poll_funcptr(&pwq->pt, __pollwait)

将函数__pollwait函数赋值给&pwq->pt指向的pooll_table结构中的函数指针。

后面的驱动中将通过调用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);
}

此处p->qproc操作指向的就是__pollwait函数

2.do_sys_poll->do_polll

static int do_poll(unsigned int nfds,  struct poll_list *list,
           struct poll_wqueues *wait, struct timespec *end_time)
{   

   for(;;){

     do_pollfd(pfd, pt)

     if (count || timed_out)
            break;

     poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack)

   }
}

在poll_initwait函数初始化完毕后,do_sys_poll函数会调用do_poll函数。

在do_poll函数中首先调用do_pollfd(pfd, pt)函数

static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
{
    mask = file->f_op->poll(file, pwait);
}

通过file->f_op->poll来调用驱动中的poll方法,而驱动中的poll方法会调用poll_wait函数

poll_wait函数调用poll_initwait函数注册的__pollwait函数

static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
                poll_table *p)
{
    struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
    struct poll_table_entry *entry = poll_get_entry(pwq);
    if (!entry)
        return;
    get_file(filp);
    entry->filp = filp;
    entry->wait_address = wait_address;
    entry->key = p->key;
    init_waitqueue_func_entry(&entry->wait, pollwake);
    entry->wait.private = pwq;
    add_wait_queue(wait_address, &entry->wait);
}

可以发现此函数就是完成添加队列的工作。

然后继续向下执行poll_schedule_timeout()

执行完此函数后,进程进入休眠,直到被wake_up或者休眠时间到。


所以总结poll的操作:首先调用poll函数将__pollwait函数注册到系统,然后通过f_ops->poll

调用驱动的poll方法,通过poll->poll_wait来调用之前注册的__pollwait函数,

在__pollwait函数中添加等待队列,并调用poll_schedule_timeout()函数

将其休眠,等待其他线程唤醒。所以使用polll和使用等待队列进行简单休眠一样需要在其他地方使用

wake_up函数来通知


3.等待队列的休眠

驱动中调用wait_event可以将函数置入休眠,看下具体操作

#define wait_event(wq, condition)                     \
do {                                    \
    if (condition)                             \
        break;                            \
    __wait_event(wq, condition);                    \

} while (0)


#define __wait_event(wq, condition)                     \
do {                                    \
    DEFINE_WAIT(__wait);                        \
                                    \
    for (;;) {                            \
        prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);    \
        if (condition)                        \
            break;                        \
        schedule();                        \
    }                                \
    finish_wait(&wq, &__wait);                    \
} while (0)

通过__wait_event中的for(;;)循环后,队列进入休眠。休眠后等待wake_up