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