Linux poll机制

一、poll机制功能

poll的是一种查询的方式,英文解释 :民意调查

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

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

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

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

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

structpollfd {     

                       intfd;             /* 文件描述符 */ (待查询的设备)

                  shortevents;   /* 等待的事件 */(待查询的设备的状态)

                  short revents; /* 实际发生了的事件*/

             }

功能过程描述:应用程序中调用poll查询文件的状态,首先将fds里面的每个设备文件fd取出,调用它们驱动程序的poll函数,查询是否出现我们期望状态,查询完fds里面所有的设备文件得到满足期望状态的设备文件的数量,如果这个数为0,则poll调用将导致进程就进入睡眠状态,睡眠时间由poll函数设定,如果程序在睡眠状态中fds的某个文件出现我们期望状态,那么poll立即返回,否则一直睡眠到睡眠时间结束为止,返回值为0;如果这个数大于0 ,poll返回满足条件的设备数量。

poll相当于open("/dev/xxx",O_RDWR)阻塞打开文件,区别在于当设备文件无数据可读时poll只导致程序休眠固定时间,而open将导致程序一直休眠到有数据为止。

 

二、poll应用举例

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;            //查询的设备文件描述符为fd,也就是查询的设备是/dev/buttons

     fds[0].events = POLLIN;   //查询事件是POLLIN,也就是/dev/buttons是否按下

     while (1)

     {     

          ret = poll(fds, 1, 5000);   //查询的设备队列是fds,里面有1个设备,查询不到就睡眠5s,在睡眠中如果有期望状态出现也是可以返回

          if (ret == 0)    

             printf("time out\n");        //没有查询到按键按下,睡眠中也没有按键按下

          else

             {    

                 read(fd, &key_val, 1);       //查询到按键按下,读取这个按键的值

                 printf("key_val =0x%x\n", key_val);

             }

     }

         return 0;

}

 

三、poll内核实现过程(kernel-2.6.30.4)

(应用程序)poll->sys_poll->do_sys_poll->do_poll->do_pollfd->f_op->poll(驱动)

在do_poll中:

for (;;) 

{                                            //逐个取出待查询数组中的每个文件描述符来查询

        for(; pfd != pfd_end; pfd++) 

        {

           if (do_pollfd(pfd, pt)) 

            {

              count++;

              pt= NULL;

           }

        }    

        pt= NULL;

        if(!count)                                       //如果没有一个设备文件发生可读

        {

          count = wait->error;

          if (signal_pending(current))

              count= -EINTR;

        }

       if(count || timed_out)                   //超时或者有设备文件可读,程序直接返回

          break;

       if(end_time && !to) 

       {

          expire = timespec_to_ktime(*end_time);

          to = &expire;

       }

                                                            //程序睡眠

       if(!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack))

           timed_out = 1;

}

在do_pollfd中:

  if(file->f_op && file->f_op->poll)
     mask = file->f_op->poll(file, pwait);                     //调用设备的poll函数,返回是否发生期望的设备状态   

在f_op->poll中(针对按键驱动举例):

 unsigned intmask = 0;
  poll_wait(file, &button_waitq, wait);                      //将当前的设备加入到等待队列中,当它不是马上就休眠

  if(ev_press)
     mask |= POLLIN | POLLRDNORM;//返回设备文件现在的状态

  returnmask;

按键驱动poll机制驱动实现:(可在TQ2440运行)

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#define DEVICE_NAME   "buttons_poll"
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

/*中断事件标志*/
static volatile int ev_press = 0;

struct pin_desc

{
    unsigned int pin;
    unsigned int key_val;
};

/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;

/* K1,K2,K3,K4对应GPF1、GPF4、GPF2、GPF0*/
struct pin_desc pins_desc[4] = 

{
       {S3C2410_GPF1, 0x01},
       {S3C2410_GPF4, 0x02},
       {S3C2410_GPF2, 0x03},
       {S3C2410_GPF0, 0x04},
};

/*确定按键值*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
       struct pin_desc * pindesc = (structpin_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);
}
static int button_drv_open(struct inode *inode, struct file *file)
{
       /* GPF1、GPF4、GPF2、GPF0为中断引脚 */
       request_irq(IRQ_EINT1, buttons_irq,IRQ_TYPE_EDGE_BOTH, "K1", &pins_desc[0]);
       request_irq(IRQ_EINT4, buttons_irq,IRQ_TYPE_EDGE_BOTH, "K2", &pins_desc[1]);
       request_irq(IRQ_EINT2, buttons_irq,IRQ_TYPE_EDGE_BOTH, "K3", &pins_desc[2]);
       request_irq(IRQ_EINT0, buttons_irq,IRQ_TYPE_EDGE_BOTH, "K4", &pins_desc[3]);    
       return 0;
}
ssize_t button_drv_read(struct file *file, char __user *buf, size_t size, loff_t*ppos)
{
       if (size != 1)
              return -EINVAL;
       /* 如果没有按键动作, 休眠 */
       wait_event_interruptible(button_waitq,ev_press);
       /* 如果有按键动作, 返回键值 */
       copy_to_user(buf, &key_val, 1);
       ev_press = 0;
       return 1;
}
int button_drv_close(struct inode *inode, struct file *file)
{
       free_irq(IRQ_EINT1, &pins_desc[0]);
       free_irq(IRQ_EINT4, &pins_desc[1]);
       free_irq(IRQ_EINT2, &pins_desc[2]);
       free_irq(IRQ_EINT0, &pins_desc[3]);
       return 0;
}
static unsigned button_drv_poll(struct file *file, poll_table *wait)
{
       unsigned int mask = 0;
       /*不会立即休眠
        *当调用完所有的设备文件的poll无结果后,程序开始睡眠
        *由驱动程序唤醒
        */
       poll_wait(file, &button_waitq,wait);

       if (ev_press)
         mask |= POLLIN | POLLRDNORM;
       return mask;
}

static struct file_operations dev_fops = 

{
    .owner   = THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open    = button_drv_open,    
    .read    =  button_drv_read,         
    .release =  button_drv_close,
    .poll    = button_drv_poll,
};

static struct miscdevice misc = 

{
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &dev_fops,
};
static int __init dev_init(void)
{
    int ret;
    /*杂项设备注册,和register_chrdev差不多
     *不同点在于它的主设备号固定为10,并且自动在/dev目录下创建好设备
     *注册后次设备号、设备名、设备的fops就都融为一体了
     */
     ret = misc_register(&misc);
     printk (DEVICE_NAME"initialized\n");
     return ret;
}
static void __exit dev_exit(void)
{
     misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

 

四、开发板测试过程(TQ2440)

在PC机上编译好驱动和测试程序在开发板上

#rz                                                  (传送PC上的文件到开发板)

#chmod 777 *                                  (改变文件的属性为可执行)

#insmodbuttondrv_poll.ko                       (加载模块)

#ls /dev                                            (可以看到buttons)

#./buttondrvtest

                                                        按下开发板上的按键就会打印出按键信息了

 

五、源代码下载链接

http://pan.baidu.com/share/link?shareid=373773&uk=101680913

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值