#1、poll機制的做用html
在前面的使用中斷的的方式來讀取按鍵值(linux 中斷管理(四))。使用這種方式讀取按鍵,若是按鍵沒有按下的時候,應用程序會一直處於睡眠的狀態。若是想要即便按鍵沒有按下,在必定的時間後也能返回,要實現這種功能,可使用
poll機制
。(select IO複用
和epoll
也能夠實現這種功能,這裏只寫poll機制) #2、poll機制的應用編程 ##1.應用層函數接口 ###1).API:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);node
###2).Paramet:linux
fds | nfds | timeout |
---|---|---|
參數類型:<br> struct pollfd {<br> int fd; /* 文件描述符 / <br> short events; / 等待的發生事件類型 / <br> short revents; / 實際返回的事件類型 */ <br> };<br>參數描述:<br> fds是一個結構體指針,也就是poll函數能夠同時等待一個或者多個文件描述符的事件 | 參數類型:<br> nfds_t,其實就是int型<br>參數描述:<br> 用來講明poll同時監聽fds的個數 | 參數類型:<br> int<br>參數描述:<br> 等於-1:永久等待<br> 等於0:當即返回<br> 大於0:等待超時時間,以毫秒爲單位 |
###3).Return: |
返回值 | 描述 |
---|---|
<0 | 錯誤返回 |
=0 | 超時返回 |
>0 | 返回結構體中 revents 域不爲 0 的文件描述符個數 |
##2.應用程序 |
應用程序主要使用poll的方式讀取按鍵的值,而且設置5000ms超時等待編程
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> /* poll機制測試 */ int main(int argc, char **argv) { int fd; unsigned char key_val; int ret; /* 定義一個 struct pollfd 結構體數組*/ struct pollfd fds[1]; /* 打開一個設備文件 */ fd = open("/dev/my_button", O_RDWR); if (fd < 0) { printf("can't open!\n"); } /**************** 初始化 struct pollfd 結構體 *************/ /* 初始化文件描述符 */ fds[0].fd = fd; /* 初始化監聽的事件事件 */ fds[0].events = POLLIN; while (1) { /* 調用poll函數,超時時間是5000ms */ ret = poll(fds, 1, 5000); if (ret == 0){ /* 超時返回 */ printf("time out\n"); }else if(ret<0){ /* 出錯返回 */ printf("poll error\n"); }else{ /* 有數據可讀,讀取數據 */ /* 這裏爲了簡單就不對返回的事件revents,作判斷和重置了 */ read(fd, &key_val, 1); printf("key_val = %d\n", key_val); } } return 0; }
#3、驅動程序數組
驅動程序的編寫主要在
file_operations
的poll
成員添加一個函數接口button_poll
。下面的程序是在linux中斷管理(四)更改的(不過本章節只使用了一個按鍵)。更改的代碼以下:函數
#include <linux/device.h> #include <linux/interrupt.h> #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 <linux/cdev.h> #include <linux/poll.h> /* 申請一個等待隊列頭 */ DECLARE_WAIT_QUEUE_HEAD (button_waitq); /* 事件觸發標誌 */ int ev_press; /* 用一個結構體來描述一個按鍵,抽象的或許不是很好 */ struct but_irq_desc{ /* 中斷名稱 */ char *button_nane; /* 按鍵的狀態,1鬆開,0按下 */ unsigned char key_state; /* 按鍵所接的GPIO口 */ unsigned long pin; /* 按鍵的中斷號 */ unsigned int irq; }; struct but_irq_desc but_irq_descs={ .button_nane="button1", .key_state = 1, .pin = S3C2410_GPF0, .irq=IRQ_EINT0, }; static struct cdev *button_cdev; static struct class *button_class; struct file_operations *button_fops; /* 中斷處理函數 */ static irqreturn_t button_irq(int irq, void * dev_id) { /* 獲取這個按鍵的結構體 */ struct but_irq_desc *btndesc = (struct but_irq_desc *)dev_id; unsigned int pinval; /* 讀取按鍵的電平 */ pinval = s3c2410_gpio_getpin(btndesc->pin); /* 若是是高電平*/ if(pinval){ /* 按鍵鬆開 */ btndesc->key_state = 1; }else{ btndesc->key_state = 0; /* 按鍵按下 */ } /* 喚醒該等待隊列裏的進程 */ wake_up_interruptible(&button_waitq); /* 將標誌置1 */ ev_press = 1; return IRQ_HANDLED; } int button_open (struct inode * inode, struct file *file){ /* 註冊驅動,中斷爲上升沿和降低沿觸發 */ request_irq(but_irq_descs.irq, button_irq,\ IRQF_TRIGGER_RISING| IRQF_TRIGGER_FALLING,\ but_irq_descs.button_nane,(void*)(&but_irq_descs)); return 0; } int button_release (struct inode *inode, struct file * file){ /* 釋放中斷 */ free_irq(but_irq_descs.irq,(void*)(&but_irq_descs)); return 0; } /* 驅動讀函數 */ ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t * offset){ unsigned char key_val; if(size!=1){ return -EINVAL; } /* 等待按鍵按下,若是按鍵按下,則這個進程會被喚醒(之後有時間系一章等待隊列的源碼分析) */ /* 若是ev_press等於1的時候則這個進程不會被掛起,等於0的時候這個進程纔會被掛起 */ wait_event_interruptible(button_waitq, ev_press); key_val = but_irq_descs.key_state; /* 將按鍵值返回給應用層 */ copy_to_user(buf, &key_val, 1); /* 將標誌置0將進程掛起,等待下一次喚醒 */ ev_press = 0; return 1; } /*********************本章中增長的函數****************************/ static unsigned int button_poll(struct file *file, poll_table *wait) { unsigned int res = 0; poll_wait(file, &button_waitq, wait); /************************ poll_wait函數定義以下 **********************/ /* static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) * { * if (p && wait_address) * p->qproc(filp, wait_address, p); * //本質調用的是__pollwait(filp, wait_address, p);這個函數 * } */ /***********************************************************************/ /* p->qproc 是一個函數指針,指向 __pollwait函數。 * __pollwait 作的事情也就是將當前的進程添加到 button_waitq 等待隊列中。 * 這裏只是添加到等待隊列而已,並不會當即休眠。 * (下一章分析poll機制源碼的時候會詳細分析) */ /* 若是當前有按鍵按下,則返回POLLIN 和 POLLRDNORM事件, * 不然返回0 * 返回非0則當前的進程不會休眠,返回0當前的進程會休眠 */ if (ev_press) res = POLLIN | POLLRDNORM; /* 返回res */ return res; } /*********************本章中增長的函數****************************/ static dev_t dev_id; /* 模塊入口函數 */ static int __init my_button_init(void){ /* 分配一個file_operations 結構體 */ button_fops = kmalloc(sizeof(struct file_operations), GFP_KERNEL); /* 初始化接口函數 */ button_fops->open = button_open; button_fops->release = button_release; button_fops->read = button_read; button_fops->poll = button_poll; /* 動態分配一個設備號 */ alloc_chrdev_region(&dev_id, 0, 1, "my_button"); /* 分配一個cdev結構體 */ button_cdev = cdev_alloc(); /* 將cdev結構體和 fops結構體綁定*/ cdev_init(button_cdev, button_fops); /* 將驅動註冊到內核中 */ cdev_add(button_cdev, dev_id,1); /* 建立一個class */ button_class = class_create(THIS_MODULE, "my_button"); /* 根據class內容建立一個設備節點 my_button*/ class_device_create(button_class, NULL, dev_id, NULL,"my_button"); return 0; } /* 模塊出口函數 */ static void __exit my_button_exit(void){ /* 銷燬設別節點 */ class_device_destroy(button_class, dev_id); /* 銷燬設備節點 */ class_destroy(button_class); /* 釋放cdev結構體空間 */ cdev_del(button_cdev); /* 註銷設備號 */ unregister_chrdev_region(dev_id,1); /* 釋放fops空間 */ kfree(button_fops); } /* 聲明模塊入口 */ module_init(my_button_init); /* 聲明模塊出口 */ module_exit(my_button_exit); /* 遵循GPL協議 */ MODULE_LICENSE("GPL");
#四:實驗現象源碼分析
1.按照前面的幾章的步驟,編譯驅動程序和應用程序。而後將編譯好的內核模塊和應用程序copy到文件系統。而後使用
insmod xxx.ko
插入內核模塊,執行應用程序。 2.觀察實驗現象(以下圖):測試
- 按鍵按下時打印:
key_val=0
- 按鍵鬆開時打印:
key_val=1
- 若是5000毫秒後,沒有按鍵按下也沒有按鍵鬆開則打印
time out