poll機制:為了減少CPU資源的占用率,在編寫驅動函數中添加poll機制
select,poll,epoll都是IO多路復用的機制。I/O多路復用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。
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實現步驟:
1、在驅動函數file_operation結構體上添加一個.poll函數,然后在函數里執行poll_wait,這個函數用來判斷硬件事件是否發生
2、測試程序需要調用ret = poll(fds, 1, 5000)函數來獲取事件發生信息。
比如一個按鍵事件:
1、查詢方法:一直在查詢,不斷去查詢是否有事件發生,整個過程都是占用CPU資源,消耗CPU資源非常打。
2、中斷方式:當有事件發生時,就去跳轉到相應事件去處理,CPU占用時間少。
3、poll方式: 中斷方式雖然占用CPU資源少,但是在應用程序上需要不斷在死循環里面執行讀取函數,應用程序不能去做其它事情。poll機制解決了這個問題,當有事件發生時,才去執行讀read函數,按鍵事件沒有按下時,超過時間后返回,去執行其它的處理函數。
以下為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; }