天天看點

i.MX6ULL終結者Linux阻塞和非阻塞IO實驗非阻塞IO實驗

文章目錄

    • 1 編寫驅動程式
    • 2 編寫應用測試程式
    • 3 運作測試

1 編寫驅動程式

本實驗例程路徑:i.MX6UL終結者CD光牒資料/06_Linux驅動例程/12_key_poll

建立key_poll.c檔案,具體内容如下(在阻塞實驗的驅動檔案上修改,内容有省略):

1 #include <linux/types.h>
  2 #include <linux/kernel.h>
  3 #include <linux/delay.h>
......
 21 #include <asm/uaccess.h>
 22 #include <asm/io.h>
 23 
 24 #define IMX6UIRQ_CNT            1               /* 裝置号個數   */
 25 #define IMX6UIRQ_NAME           "noblockio"     /* 名字 */
 26 #define KEY0VALUE               0X01            /* KEY0按鍵值   */
 27 #define INVAKEY                 0XFF            /* 無效的按鍵值 */
 28 #define KEY_NUM                 1               /* 按鍵數量     */
......
178  /*
179   * @description     : 從裝置讀取資料 
180   * @param - filp    : 要打開的裝置檔案(檔案描述符)
181   * @param - buf     : 傳回給使用者空間的資料緩沖區
182   * @param - cnt     : 要讀取的資料長度
183   * @param - offt    : 相對于檔案首位址的偏移
184   * @return          : 讀取的位元組數,如果為負值,表示讀取失敗
185   */
186 static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
187 {
188         int ret = 0;
189         unsigned char keyvalue = 0;
190         unsigned char releasekey = 0;
191         struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
192 
193         if (filp->f_flags & O_NONBLOCK) { /* 非阻塞通路 */
194                 if(atomic_read(&dev->releasekey) == 0)  /* 沒有按鍵按下,傳回-EAGAIN */
195                         return -EAGAIN;
196    } else {                                    /* 阻塞通路 */
197                 /* 加入等待隊列,等待被喚醒,也就是有按鍵按下 */
198                 ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));
199                 if (ret) {
200                         goto wait_error;
201                 }
202         }
......
220 wait_error:
221         return ret;
222 data_error:
223         return -EINVAL;
224 }
225 
226  /*
227   * @description     : poll函數,用于處理非阻塞通路
228   * @param - filp    : 要打開的裝置檔案(檔案描述符)
229   * @param - wait    : 等待清單(poll_table)
230   * @return          : 裝置或者資源狀态,
231   */
232 unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct *wait)
233 {
234         unsigned int mask = 0;
235         struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
236 
237         poll_wait(filp, &dev->r_wait, wait);    /* 将等待隊列頭添加到poll_table中 */
238 
239         if(atomic_read(&dev->releasekey)) {             /* 按鍵按下 */
240                 mask = POLLIN | POLLRDNORM;        /* 傳回PLLIN */
241         }
242         return mask;
243 }
244 
245 /* 裝置操作函數 */
246 static struct file_operations imx6uirq_fops = {
247         .owner = THIS_MODULE,
248         .open = imx6uirq_open,
249         .read = imx6uirq_read,
250         .poll = imx6uirq_poll,
251 };
252 
253 /*
254  * @description : 驅動入口函數
255  * @param               : 無
256  * @return              : 無
257  */
258 static int __init imx6uirq_init(void)
259 {
......
286         /* 5、始化按鍵 */
287         atomic_set(&imx6uirq.keyvalue, INVAKEY);
288         atomic_set(&imx6uirq.releasekey, 0);
289         keyio_init();
290         return 0;
291 }
292 
293 /*
294  * @description : 驅動出口函數
295  * @param               : 無
296  * @return              : 無
297  */
298 static void __exit imx6uirq_exit(void)
299 {
300         unsigned i = 0;
301         /* 删除定時器 */
302         del_timer_sync(&imx6uirq.timer);        /* 删除定時器 */
303 
304         /* 釋放中斷 */
305         for (i = 0; i < KEY_NUM; i++) {
306                 free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);
307         }
308         cdev_del(&imx6uirq.cdev);
309         unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);
310         device_destroy(imx6uirq.class, imx6uirq.devid);
311         class_destroy(imx6uirq.class);
312 }
313 
314 module_init(imx6uirq_init);
315 module_exit(imx6uirq_exit);
316 MODULE_LICENSE("GPL");
           

第 25 行,修改裝置檔案名字為“noblockio”,當驅動程式加載成功以後就會在根檔案系統中出現一個名為“/dev/noblockio”的檔案。

第 193~195 行,判斷是否為非阻塞式讀取通路,如果是的話就判斷按鍵是否有效,也就是判斷一下有沒有按鍵按下,如果沒有的話就傳回-EAGAIN。

第 232~243 行,imx6uirq_poll 函數就是 file_operations 驅動操作集中的 poll 函數,當應用程式調用 select 或者 poll 函數的時候 imx6uirq_poll 函數就會執行。第 237 行調用 poll_wait 函數将等待隊列頭添加到 poll_table 中,第 239~241 行判斷按鍵是否有效,如果按鍵有效的話就向應用程式傳回 POLLIN 這個事件,表示有資料可以讀取。

第 250 行,設定 file_operations 的 poll 成員變量為 imx6uirq_poll。

2 編寫應用測試程式

建立key_poll_test.c檔案,具體内容如下:

1 #include "stdio.h"
  2 #include "unistd.h"
  3 #include "sys/types.h"
  4 #include "sys/stat.h"
  5 #include "fcntl.h"
  6 #include "stdlib.h"
  7 #include "string.h"
  8 #include "poll.h"
  9 #include "sys/select.h"
 10 #include "sys/time.h"
 11 #include "linux/ioctl.h"
 12 
 13 /*
 14  * @description         : main主程式
 15  * @param - argc        : argv數組元素個數
 16  * @param - argv        : 具體參數
 17  * @return             : 0 成功;其他 失敗
 18  */
 19 int main(int argc, char *argv[])
 20 {
 21         int fd;
 22         int ret = 0;
 23         char *filename;
 24         struct pollfd fds;
 25         fd_set readfds;
 26         struct timeval timeout;
 27         unsigned char data;
 28 
 29         if (argc != 2) {
 30                 printf("Error Usage!\r\n");
 31                 return -1;
 32         }
 33 
 34         filename = argv[1];
 35         fd = open(filename, O_RDWR | O_NONBLOCK);     /* 非阻塞通路 */
 36         if (fd < 0) {
 37                 printf("Can't open file %s\r\n", filename);
 38                 return -1;
 39         }
 40 
 41 #if 0
 42         /* 構造結構體 */
 43         fds.fd = fd;
 44         fds.events = POLLIN;
 45                 
 46         while (1) {
 47                 ret = poll(&fds, 1, 500);
 48                 if (ret) {      /* 資料有效 */
 49                         ret = read(fd, &data, sizeof(data));
 50                         if(ret < 0) {
 51                                 /* 讀取錯誤 */
 52                         } else {
 53                                 if(data)
 54                                         printf("key value = %d \r\n", data);
 55                         }       
 56                 } else if (ret == 0) {  /* 逾時 */
 57                         /* 使用者自定義逾時處理 */
 58                 } else if (ret < 0) {   /* 錯誤 */
 59                         /* 使用者自定義錯誤處理 */
 60                 }
 61         }
 62 #endif
 63 
 64         while (1) {
 65                 FD_ZERO(&readfds);
 66                 FD_SET(fd, &readfds);
 67                 /* 構造逾時時間 */
 68                 timeout.tv_sec = 0;
 69                 timeout.tv_usec = 500000; /* 500ms */
 70                 ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
 71                 switch (ret) {
 72                         case 0:         /* 逾時 */
 73                                 /* 使用者自定義逾時處理 */
 74                                 break;
 75                         case -1:        /* 錯誤 */
 76                                 /* 使用者自定義錯誤處理 */
 77                                 break;
 78                         default:  /* 可以讀取資料 */
 79                                 if(FD_ISSET(fd, &readfds)) {
 80                                         ret = read(fd, &data, sizeof(data));
 81                                         if (ret < 0) {
 82                                                 /* 讀取錯誤 */
 83                                         } else {
 84                                                 if (data)
85                                              printf("key value=%d\r\n", data);
 86                                         }
 87                                 }
 88                                 break;
 89                 }
 90         }
 91 
 92         close(fd);
 93         return ret;
 94 }
           

第 41~62行,這段代碼使用 poll 函數來實作非阻塞通路,在 while 循環中使用 poll 函數不斷的輪詢,檢查驅動程式是否有資料可以讀取,如果可以讀取的話就調用 read 函數讀取按鍵資料。

第 64~90行,這段代碼使用 select 函數來實作非阻塞通路。

3 運作測試

1、編譯驅動程式

和前面章節中驅動測試程式一樣需要一個Makefile檔案,隻是将obj-m的值改為key_poll.o,Makefile檔案内容如下:

KERNELDIR := /home/topeet/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := key_poll.o

build: kernel_modules
kernel_modules: 
        $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
        $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
           

首先我們在終端輸入兩個指令(設定兩個環境變量):

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
           

然後執行“make”指令編譯子產品,編譯完成生成key_poll.ko子產品檔案。

2、 編譯應用測試程式

輸入如下指令編譯應用測試程式:

arm-linux-gnueabihf-gcc -o key_poll_test key_poll_test.c

編譯完成後,會生成key_poll_test可執行檔案。

3、運作測試

啟動開發闆,将編譯好的key_poll.ko驅動子產品檔案和key_poll_test應用測試檔案拷貝到/lib/modules/4.1.15目錄下(檢查開發闆根檔案系統中有沒有“/lib/modules/4.1.15”這個目錄,如果沒有的話需要自行建立一下。開發闆中使用的是CD光牒資料裡面提供的busybox檔案系統,CD光牒資料的“i.MX6UL終結者CD光牒資料\08_開發闆系統鏡像\03_檔案系統鏡像\01_Busybox檔案系統”目錄下)。輸入下面指令加載子產品:

depmod
modprobe key_poll
           

驅動加載成功後,然後使用key_poll_test應用測試檔案來測試驅動檔案,使用下面的指令:

./key_poll_test /dev/noblockio &

按下開發闆上的KEY0按鍵,結果如圖 3.1所示:

i.MX6ULL終結者Linux阻塞和非阻塞IO實驗非阻塞IO實驗

圖 3.1

按下按鍵KEY0後,有資訊輸出說明驅動正常,然後使用“top”指令檢視key_poll_test應用程式的CPU使用率,如圖 3.2所示:

i.MX6ULL終結者Linux阻塞和非阻塞IO實驗非阻塞IO實驗

圖 3.2

可以看出采用非阻塞IO的情況和阻塞IO基本一緻,CPU使用率都非常低。使用“q”或者“ctrl + c”退出top指令。

在解除安裝子產品驅動之前先使用“ps”加“kill”指令關閉key_poll_test應用程序,然後解除安裝子產品:

rmmod key_poll

i.MX6ULL終結者Linux阻塞和非阻塞IO實驗非阻塞IO實驗

繼續閱讀