poll机制:为了减小CPU资源的占用率,在编写驱动函数中添加poll机制node
select,poll,epoll都是IO多路复用的机制。I/O多路复用就经过一种机制,能够监视多个描述符,一旦某个描述符就绪(通常是读就绪或者写就绪),可以通知程序进行相应的读写操做。linux
POLL:应用程序在必定时间内没有事件发生回返回来执行其它下面函数
先说poll,poll或select为大部分Unix/Linux程序员所熟悉,这俩个东西原理相似,性能上也不存在明显差别,但select对所监控的文件描述符数量有限制,因此这里选用poll作说明。
poll是一个系统调用,其内核入口函数为sys_poll,sys_poll几乎不作任何处理直接调用do_sys_poll,do_sys_poll的执行过程能够分为三个部分:
1,将用户传入的pollfd数组拷贝到内核空间,由于拷贝操做和数组长度相关,时间上这是一个O(n)操做,这一步的代码在do_sys_poll中包括从函数开始到调用do_poll前的部分。
2,查询每一个文件描述符对应设备的状态,若是该设备还没有就绪,则在该设备的等待队列中加入一项并继续查询下一设备的状态。查询完全部设备后若是没有一个设备就绪,这时则须要挂起当前进程等待,直到设备就绪或者超时,挂起操做是经过调用schedule_timeout执行的。设备就绪后进程被通知继续运行,这时再次遍历全部设备,以查找就绪设备。这一步由于两次遍历全部设备,时间复杂度也是O(n),这里面不包括等待时间。相关代码在do_poll函数中。
3,将得到的数据传送到用户空间并执行释放内存和剥离等待队列等善后工做,向用户空间拷贝数据与剥离等待队列等操做的的时间复杂度一样是O(n),具体代码包括do_sys_poll函数中调用do_poll后到结束的部分。
程序员
poll实现步骤:数组
一、在驱动函数file_operation结构体上添加一个.poll函数,而后在函数里执行poll_wait,这个函数用来判断硬件事件是否发生函数
二、测试程序须要调用ret = poll(fds, 1, 5000)函数来获取事件发生信息。性能
好比一个按键事件:测试
一、查询方法:一直在查询,不断去查询是否有事件发生,整个过程都是占用CPU资源,消耗CPU资源很是打。this
二、中断方式:当有事件发生时,就去跳转到相应事件去处理,CPU占用时间少。spa
三、poll方式: 中断方式虽然占用CPU资源少,可是在应用程序上须要不断在死循环里面执行读取函数,应用程序不能去作其它事情。poll机制解决了这个问题,当有事件发生时,才去执行读read函数,按键事件没有按下时,超过期间后返回,去执行其它的处理函数。code
如下为poll按键事件的例子:
forth_drv.c:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #include <linux/poll.h> static struct class *forthdrv_class; static struct class_device *forthdrv_class_dev; volatile unsigned long *gpfcon; volatile unsigned long *gpfdat; volatile unsigned long *gpgcon; volatile unsigned long *gpgdat; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */ 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; struct pin_desc pins_desc[4] = { {S3C2410_GPF0, 0x01}, {S3C2410_GPF2, 0x02}, {S3C2410_GPG3, 0x03}, {S3C2410_GPG11, 0x04}, }; /* * 肯定按键值 */ 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); } static int forth_drv_open(struct inode *inode, struct file *file) { /* 配置GPF0,2为输入引脚 */ /* 配置GPG3,11为输入引脚 */ request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); return 0; } ssize_t forth_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 forth_drv_close(struct inode *inode, struct file *file) { free_irq(IRQ_EINT0, &pins_desc[0]); free_irq(IRQ_EINT2, &pins_desc[1]); free_irq(IRQ_EINT11, &pins_desc[2]); free_irq(IRQ_EINT19, &pins_desc[3]); return 0; } static unsigned forth_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; } static struct file_operations sencod_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动建立的__this_module变量 */ .open = forth_drv_open, .read = forth_drv_read, .release = forth_drv_close, .poll = forth_drv_poll, }; int major; static int forth_drv_init(void) { major = register_chrdev(0, "forth_drv", &sencod_drv_fops); forthdrv_class = class_create(THIS_MODULE, "forth_drv"); forthdrv_class_dev = class_device_create(forthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1; gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); gpgdat = gpgcon + 1; return 0; } static void forth_drv_exit(void) { unregister_chrdev(major, "forth_drv"); class_device_unregister(forthdrv_class_dev); class_destroy(forthdrv_class); iounmap(gpfcon); iounmap(gpgcon); return 0; } module_init(forth_drv_init); module_exit(forth_drv_exit); MODULE_LICENSE("GPL");
forthdrvtest.c:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> /* forthdrvtest */ 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 { r fvbbfdfnb gb' fdd b'b'bbfvdv'v cv' v' df'd'ead(fd, &key_val, 1); printf("key_val = 0x%x\n", key_val); } } return 0; }