linux设备驱动之POLL机制

来源:互联网 发布:php最新面试题 编辑:程序博客网 时间:2024/05/24 15:41

poll的是一种查询的方式
poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。

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

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

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

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

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

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

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

kernel: //先运行sys_poll();sys_polldo_sys_poll(...,timeout_jiffiles)    oll_initwait(&table);    init_poll_funcptr(&pwq->pt,_pollwait);    >table->qproc=_pollwait//最终调用do_poll(),该机制里有个死循环        do_poll(nfds,head,&table,timeout)         for(;;)            {                if(do_pollfd(pfd,pt))                {                >mask=file->f_op->poll(file,pwait);                      return mask;   //驱动的POLL       //把当前进程挂到button_waitq队列里去                _pollwait(file,&buton_waitq,p);                count++;//如果驱动的poll返回非0值,那么count++                pt=NULL                                                     }//break的条件:count非0,超时,有信号在等待处理;if(count||!*timeout||signal_pending(current))    break;      //休眠_timeout 5s     _timeout=schedule_timeout(_timeout);           }//1.****sys_poll函数位于fs/select.c文件中,代码如下:asmlinkagelong sys_poll(struct pollfd __user *ufds, unsigned int nfds, long timeout_msecs){         s64 timeout_jiffies;         if (timeout_msecs > 0) {#ifHZ > 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);}它对超时参数稍作处理后,直接调用do_sys_poll。 //2.****do_sys_poll函数也位于位于fs/select.c文件中,我们忽略其他代码:intdo_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout){……poll_initwait(&table);……         fdcount = do_poll(nfds, head,&table, timeout);……} poll_initwait函数非常简单,它初始化一个poll_wqueues变量table:poll_initwait> init_poll_funcptr(&pwq->pt, __pollwait); > pt->qproc = qproc;即table->pt->qproc= __pollwait,__pollwait将在驱动的poll函数里用到。//3.****o_sys_poll函数位于fs/select.c文件中,代码如下:static int do_poll(unsigned int nfds,  struct poll_list *list,   struct poll_wqueues *wait, s64 *timeout){01 ……02   for (;;){03 ……04                   if(do_pollfd(pfd, pt)) {05                           count++;06                           pt = NULL;07                   }08 ……09       if(count || !*timeout || signal_pending(current))10           break;11       count= wait->error;12       if(count)13           break;1415       if(*timeout < 0) {16           /*Wait indefinitely */17           __timeout= MAX_SCHEDULE_TIMEOUT;18       }else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT-1)) {19           /*20           * Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in21           * a loop22           */23           __timeout= MAX_SCHEDULE_TIMEOUT - 1;24           *timeout-= __timeout;25       }else {26           __timeout= *timeout;27           *timeout= 0;28       }2930       __timeout= schedule_timeout(__timeout); // 休眠时间由应用提供31       if(*timeout >= 0)32           *timeout+= __timeout;33   }34   __set_current_state(TASK_RUNNING);35   returncount;36 } 分析其中的代码,可以发现,它的作用如下:① 从02行可以知道,这是个循环,它退出的条件为:        a. 09行的3个条件之一(count非0,超时、有信号等待处理)        count顺0表示04行的do_pollfd至少有一个成功。        b. 1112行:发生错误② 重点在do_pollfd函数,后面再分析③ 第30行,让本进程休眠一段时间,注意:应用程序执行poll调用后,如果①②的条件不满足,进程就会进入休眠。那么,谁唤醒呢?除了休眠到指定时间被系统唤醒外,还可以被驱动程序唤醒──记住这点,这就是为什么驱动的poll里要调用poll_wait的原因,后面分析。 //4.****do_pollfd函数位于fs/select.c文件中,代码如下:static inline unsigned int do_pollfd(struct pollfd*pollfd, poll_table *pwait){……             if(file->f_op && file->f_op->poll)                     mask= file->f_op->poll(file, pwait);……} 可见,它就是调用我们的驱动程序里注册的poll函数。 

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;}

驱动程序:

/* 中断事件标志, 中断服务程序将它置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结构体中加入pollstatic 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次,直到应用程序给定的时间, 然后返回。

0 0
原创粉丝点击