linux的poll的工作机制

poll的作用:同时探测n个drivers,找到可以直接使用的driver,从而尽量block进程。

以下kernel源代码来自于:<linux/poll.h> 与 fs/select.c

static unsigned int scull_p_poll(struct file *filp, poll_table *wait)
{
    struct scull_pipe *dev = filp->private_data;
    unsigned int mask = 0;
    /*
     * The buffer is circular; it is considered full
     * if "wp" is right behind "rp" and empty if the
     * two are equal.
     */
    down(&dev->sem);
    poll_wait(filp, &dev->inq,  wait);
    poll_wait(filp, &dev->outq, wait);
    if (dev->rp != dev->wp)
        mask |= POLLIN | POLLRDNORM;    /* readable */
    if (spacefree(dev))
        mask |= POLLOUT | POLLWRNORM;   /* writable */
    up(&dev->sem);
    return mask;
}

poll调用之后, kernel针对每个driver进入其相应的poll函数 。poll_wait负责将当前进程放入wait_queue,( 对于每个driver,每个queue,申请wait_queue_t, 放入相应的queue,由kernel完成) ,但是现在并不阻塞current进程,直到所有的driver最后都没有合适的mask的时候,阻塞poll系统调用, 当有信号将当前进程唤醒后,说明某一条件满足了。阻塞结束,返回将current从wait_queue挪出来


具体poll系统调用的内部数据结构为:

struct poll_wqueues{

  poll_table pt;
  struct poll_table_page * table;
  int error;

};
//每一个poll系统调用只有一个poll_wqueues。是总的结构。
//但是对外接口是poll_table pt的地址。进入此模块后,使用container_of求出poll_wqueues的地址。绝对,面向对象的用法。
减少了耦合性。

上面poll_table的的数据结构。

typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
typedef struct poll_table_struct {
  poll_queue_proc qproc;

}poll_table;
//poll_table的变量是一个函数指针,先不用管。

然后poll_table_page, poll_table_entry.

struct poll_table_entry {
    struct file * filp;
    wait_queue_t wait;
    wait_queue_head_t * wait_address;
};

struct poll_table_page {
    struct poll_table_page * next;
    struct poll_table_entry * entry;
    struct poll_table_entry entries[0]; //指向结构体的下一个地址,不占空间。
};

#define POLL_TABLE_FULL(table) \
    ((unsigned long)((table)->entry+1) > PAGE_SIZE + (unsigned long)(table))

具体poll的数据结构的示意图。

struct poll_wqueues ---> struct poll_table_page // poll_wqueues指向一个poll_table_page的单链表,每一个page占1个PAGE_SIZE大小的区域。
struct poll_table_page ---> struct poll_table_entry // 每个poll_table_page在申请的一个PAGE_SIZE的头,entry的空间都在剩下的空间中

请看poll_wait是怎么操作poll的数据结构的。

void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *_p)
{
    struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt);
    struct poll_table_page *table = p->table;

    if (!table || POLL_TABLE_FULL(table)) {
        struct poll_table_page *new_table;

        new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);  //如果空间不够,一下子申请PAGE_SIZE的大小。
        if (!new_table) {
            p->error = -ENOMEM;
            __set_current_state(TASK_RUNNING);
            return;
        }
        new_table->entry = new_table->entries;
        new_table->next = table;
        p->table = new_table;
        table = new_table;
    }

    /* Add a new entry */
    {
        struct poll_table_entry * entry = table->entry;
        table->entry = entry+1;  //直接在当前页中往下给entry分配内存。
         get_file(filp);
         entry->filp = filp;
        entry->wait_address = wait_address;
        init_waitqueue_entry(&entry->wait, current);
        add_wait_queue(wait_address,&entry->wait);  //将当前进程放入wait_queue,并且entry中都有wait_queue_head_t记录。
    }
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值