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

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
这里写图片描述
意思就是说poll函数功能类似select函数,即等待文件描述符可进行I/O操作,比如数据的读写,标准输入之类的。文件描述符被fds所监听,当发生fds中的事件poll就返回。(即将调用poll的进程加入对应内核驱动的等待队列,等待数据可读,或超时就唤醒)

  1. 先来个大概的流程:当我们在应用中调用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)

  2. 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函数就被执行,将当前进程加入等待队列。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值