天天看點

kernel 阻塞等待隊列輪詢

等待隊列

  • wake_up() 可喚醒處于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 的程序; wake_up_interruptible() 隻能喚醒處于 TASK_INTERRUPTIBLE 的程序
static ssize_t xx_write(struct file *filp,
    const char __user *buf, size_t size, loff_t *ppos)
{
    ...
    /* initial element in wait queue, priv = current */
    DECLARE_WAITQUEUE(wait, current);
    /* insert element into wait queue */
    add_wait_queue(&xxx_wait, &wait);

    do {
        avail = device_writable(...);
        if (avail < ) {
            if (filp->f_flags & O_NONBLOCK) {
                ret = -EAGAIN;
                goto out;
            }
            set_current_state(TASK_INTERRUPTIBLE);
            schedule();

            /* be waken up by signal */
            if (signal_pending(current)) {
                ret = -ERESTARTSYS;
                goto out;
            }
        }
    } while(avail < );

    device_write(...);

out:
    remove_wait_queue(&xxx_wait, &wait);
    set_current_state(TASK_RUNNING);

    return ret;
}
           

輪詢

  • select(),poll(),epoll() 最終調用驅動中的 poll() 函數
  • 多路複用的檔案數量龐大、I/O流量頻繁,一般不适用 select() 和 poll() ,宜用 epoll()
  • EPOLLIN 可讀; EPOLLOUT 可寫
  • EPOLLET edge triggered, 相對于預設的level triggered, 當fd從未就緒變為就緒時,核心通過epoll告訴使用者,并且不會為該fd繼續發送更多就緒通知
  • 驅動程式中 poll() 應完成兩項任務:
    1. 調用 poll_wait() 将等待隊列頭部添加到 poll_table 中
    2. 傳回表示能對裝置進行無阻塞讀寫的掩碼
  • poll_wait() 本身并不引起阻塞,隻是将等待隊列頭部添加到 poll_table 中,是讓喚醒等待隊列可以喚醒因 select() 而睡眠的程序
static unsigned int xxx_poll(struct file *filp, poll_table *wait)
{
    unsigned int mask = ;
    struct xxx_dev *dev = flip->private_data;

    poll_wait(filp, dev->r_wait, wait);
    poll_wait(filp, dev->w_wait, wait);

    if (...)
        mask |= POLLIN | POLLRDNORM;

    if (...)
        mask |= POLLOUT | POLLWRNORM;

    return mask;
}
           

繼續閱讀