总览
本文使用 linux-2.6.22.6 内核, 使用jz2440开发板.
POLL机制分析 三种按键驱动的比较
查询方式: 依赖于应用程序的编写方式, 写的不好会非常耗费CPU资源
中断方式: 解决了CPU资源问题, 但是应用程序会阻塞在读取函数上
poll方式: 应用程序上, 实现了非阻塞读取. 先poll判断是否发生事件, 有事件处理, 无事件可以做其它事情.
核心流程 基本流程如下:
应用程序调用poll > sys_poll > do_sys_poll
> do_poll > do_pollfd > 驱动程序poll
从 do_sys_poll
函数深入分析:
先调用 poll_initwait
, 注册一下 __pollwait
函数.
再调用 do_poll
判断条件. 这里会调用驱动函数的poll
调用 do_pollfd
函数, 实际上就是调用驱动的poll函数
返回值为1或等待超时或触发信号, 执行 __set_current_state
继续运行当前进程.
返回值为0, 则执行 schedule_timeout
当前进程睡眠, 切换到其它进程.
驱动函数里的poll会调用 poll_wait
, 只是把当前的进程加入到 button_waitq 队列里去, 并没有立刻切换进程
poll_wait
调用 p->qproc(filp, wait_address, p);
, 实际就是调用了 __pollwait
函数
进程并不会阻塞在 poll_wait
函数. (这个函数的命名容易让人误解, 因此特此强调!)
整个进程会根据驱动的poll返回值确定是休眠还是继续运行.
app: poll, 会去调用 sys_poll. 共三个参数 kernel: sys_poll do_sys_poll(..., timeout_jiffies) poll_initwait(&table); init_poll_funcptr(&pwq->pt, __pollwait); do_poll(nfds, head, &table, timeout) for (;;) { if (do_pollfd(ftd, pt)) { mask = file->f_op->poll(file, pwait); pollwait(filp, &button_waitq, p) { p->qproc(filp, wait_address, p); } return mask; count++; pt = NULL ; } if (count || !*timeout || signal_pending(current)) break ; __timeout = schedule_timeout(__timeout); } __set_current_state(TASK_RUNNING);
更多资料
驱动源码 驱动源码基于 驱动之基于中断设计按键驱动 增加poll函数即可. 然后应用层的测试文件改动较大.注意驱动层和应用层的poll函数写法就可以了, 都是固定的结构
drv_key_poll.c #include "drv_key_poll.h" #define DRV_KEY_INT_NODE_NAME "key_poll" static unsigned drv_key_poll (struct file *file, poll_table *wait) { unsigned int mask = 0 ; poll_wait(file, &key_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } static const struct file_operations drv_key_int_fops = { .owner = THIS_MODULE, .open = drv_key_int_open, .release = drv_key_int_release, .read = drv_key_int_read, .poll = drv_key_poll, };
drv_key_poll.h #define DRIVER_NAME "drv_key_poll" #define PDEBUG(fmt,args...) printk(KERN_DEBUG"%s:" fmt,DRIVER_NAME, ##args) #define PERR(fmt,args...) printk(KERN_ERR"%s:" fmt,DRIVER_NAME,##args) #define PINFO(fmt,args...) printk(KERN_INFO"%s:" fmt,DRIVER_NAME, ##args) #include <linux/cdev.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/kdev_t.h> #include <linux/module.h> #include <linux/types.h> #include <linux/uaccess.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/poll.h> // 新增poll头文件 #include <asm/irq.h> #include <asm/arch-s3c2410/irqs.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h>
Makefile obj-m := drv_key_poll.o KERN_SRC := /home/draapho/share/kernel/linux-2.6.22.6/ PWD := $(shell pwd) modules: make -C $(KERN_SRC) M=$(PWD) modules install: make -C $(KERN_SRC) M=$(PWD) modules_install depmod -a clean: make -C $(KERN_SRC) M=$(PWD) clean
测试文件 test_drv_key_int.c #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, ret; unsigned char keys_val; struct pollfd fds [1]; fd = open("/dev/key_poll0" , O_RDWR); if (fd < 0 ) { printf ("can't open!\n" ); return 0 ; } 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, &keys_val, 1 ); printf ("keys_val = 0x%x\n" , keys_val); } } return 0 ; }
编译并测试 Ubuntu主机端 $ make clean $ make modules $ arm-linux-gcc test_drv_key_poll.c -o test_drv_key_poll
开发板端 $ insmod drv_key_poll.ko drv_key_int:INIT $ ./test_drv_key_poll drv_key_poll:In char driver open() function $ ./test_drv_key_poll & $ top