linux设备驱动之POLL机制

poll的是一种查询的方式
poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。
一言以弊之:在指定的时间内去查询是否有时间发生

函数原型:
int poll(struct pollfd *fds,nfds_t nfds, int timeout);

函数参数:
fds为指向待查询的设备文件数组;

nfds描述第一个参数fds中有多少个设备;

timeout为查询不到我们期望的结果进程睡眠的时间;

返回值:查询到期望状态的设备文件个数

总的来说,Poll机制会判断fds中的文件是否可读,如果可读则会立即返回,返回的值就是可读fd的数量,如果不可读,那么就进程就会休眠timeout这么长的时间,然后再来判断是否有文件可读,如果有,返回fd的数量,如果没有,则返回0.

在内核中大致上实现过程:

APP:poll
函数彻底分析:
APP:poll调用过程!
     sys_poll
          return do_sys_poll(ufds, nfds, &timeout_jiffies);
               poll_initwait(&table);          Garmen:初始化函数     
                    init_poll_funcptr(&pwq->pt, __pollwait);     <==做了这个事情
               do_poll(nfds, head, &table, timeout);
                    for (;;)
                    {
                         for (; pfd != pfd_end; pfd++)     Garmen:查询多个驱动程序! 
                         {          
                               /*Garmen:判断do_pollfd,最终调用到驱动程序的POLL函数,如果驱动程序中的poll函数返回值不为0的话,count++*/
                               if (do_pollfd(pfd, pt))     
                               /*<== mask = file->f_op->poll(file, pwait);   return mask;     
                              Garmen:然后调用到poll函数中的poll_wait(file, &button_waitq, wait);
                              函数作用:把当前进程挂到button_waitq队列里去,并不会立即休眠*/
{                                                                            
                                count++;                                                                        
                                pt = NULL;
                              }
                             /*Garmen:如果count不为0 或者超时 或者有信号在等待处理,就跳出,返回到APP:poll*/
                             if (count || !*timeout || signal_pending(current))
                                    break;
                             /*Garmen:假设上面三个条件都不成立的话,就进行休眠!休眠__timeout这么一段时间!休眠是在这里休眠!而且在休眠的这段时间没有事情发生的话,就会重头进行循环,进入 for (;;)这个大循环,然后此时上面三个条件的超时条件成立,返回APP:poll*/
                             __timeout = schedule_timeout(__timeout);
                         }
                    }

Poll函数:

static unsigned fifth_drv_poll(struct file *file, poll_table *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &button_waitq, wait); // 不会立即休眠

    if (ev_press)
        mask |= POLLIN | POLLRDNORM;

    return mask;
}

poll应用程序举例:


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>

int main(int argc, char **argv)
{
    int fd;
    unsigned char key_val;
    int ret;
    struct pollfd fds[1];

    fd = open("/dev/buttons", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
    }

    fds[0].fd     = fd;
    fds[0].events = POLLIN;
    while (1)
    {
        ret = poll(fds, 1, 5000);
        if (ret == 0)
        {
            printf("time out\n");
        }
        else
        {
            read(fd, &key_val, 1);
            printf("key_val = 0x%x\n", key_val);
        }
    }

    return 0;
}

分析:
fds[0].fd = fd; //查询的文件
fds[0].events = POLLIN; //我们期待获得的值 POLLIN:有数据读取

驱动程序:

/* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */
static volatile int ev_press = 0;

static DECLARE_WAIT_QUEUE_HEAD(**button_waitq**);
/*
  * 按键中断程序确定按键值
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    struct pin_desc * pindesc = (struct pin_desc *)dev_id;
    unsigned int pinval;

    pinval = s3c2410_gpio_getpin(pindesc->pin);

    if (pinval)
    {
        /* 松开 */
        key_val = 0x80 | pindesc->key_val;
    }
    else
    {
        /* 按下 */
        key_val = pindesc->key_val;
    }

    ev_press = 1;                  /* 表示中断发生了 */
    wake_up_interruptible(&**button_waitq**);   /* 唤醒休眠的进程 */


    return IRQ_RETVAL(IRQ_HANDLED);
}

//在fops结构体中加入poll
static struct file_operations xxx_drv_fops = {
    .poll    =  xxx_drv_poll,
};

//poll驱动程序大致
static unsigned xxx_drv_poll(struct file *file, poll_table *wait)
{
    unsigned int mask = 0;
 // 不会立即休眠。将当前的设备加入到等待队列中
    poll_wait(file, &**button_waitq**, wait);
//如果有数据进来
    if (ev_press)
        mask |= POLLIN | POLLRDNORM;
//返回设备文件现在的状态
    return mask;
}



//通过poll_wait来调用上面说到的__pollwait函数,pollwait的代码如下:
staticinline 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函数,从它的代码可知,它只是把当前进程挂入我们驱动程序里定义的一个队列里而已。它的代码如下:
staticvoid __pollwait(struct file *filp, wait_queue_head_t *wait_address,  poll_table *p)
{
         struct poll_table_entry *entry =poll_get_entry(p);
         if (!entry)
             return;
         get_file(filp);
         entry->filp = filp;
         entry->wait_address = wait_address;
         init_waitqueue_entry(&entry->wait,current);
         add_wait_queue(wait_address,&entry->wait);
} 

总结一下poll机制:
1. poll > sys_poll > do_sys_poll >poll_initwait,poll_initwait函数注册一下回调函数__pollwait,它就是我们的驱动程序执行poll_wait时,真正被调用的函数。
2. 接下来执行file->f_op->poll,即我们驱动程序里自己实现的poll函数,它会调用poll_wait把自己挂入某个队列,这个队列也是我们的驱动自己定义的;
它还判断一下设备是否就绪。
3. 如果设备未就绪,do_sys_poll里会让进程休眠一定时间,这个时间是应用提供的“超时时间”
4. 进程被唤醒的条件有两点:一是上面说的“一定时间”到了,二是被驱动程序唤醒。驱动程序发现条件就绪时,就把“某个队列”上挂着的进程唤醒,这个队列,就是前面通过poll_wait把本进程挂过去的队列。
5. 如果驱动程序没有去唤醒进程,那么chedule_timeout(__timeou)超时后,会重复2、3动作1次,直到应用程序给定的时间, 然后返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值