天天看點

i.MX6ULL終結者Linux并發與競争實驗信号量實驗

文章目錄

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

信号量可以導緻休眠,是以信号量保護的臨界區沒有運作時間限制,可以在驅動的 open 函數申請信号量,然後在release 函數中釋放信号量。但是信号量不能用在中斷中,本節實驗我們不會在中斷中使用信号量。

1 編寫驅動程式

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

建立gpioled_semaphore.c檔案,因為和前面的實驗驅動檔案部分是相同不用修改的,下面是部分内容:

1 #include <linux/types.h>
  2 #include <linux/kernel.h>
......
 17 #include <linux/semaphore.h>
 18 
 19 #define GPIOLED_CNT 1 /* 裝置号個數 */
 20 #define GPIOLED_NAME "gpioled" /* 名字 */
 21 #define LEDOFF 0 /* 關燈 */
 22 #define LEDON 1 /* 開燈 */
 23 
 24 /* gpioled 裝置結構體 */
 25 struct gpioled_dev{
 26         dev_t devid; /* 裝置号 */
 27         struct cdev cdev; /* cdev */
 28         struct class *class; /* 類 */
 29         struct device *device; /* 裝置 */
 30         int major; /* 主裝置号 */
 31         int minor; /* 次裝置号 */
 32         struct device_node *nd; /* 裝置節點 */
 33         int led_gpio; /* led 所使用的 GPIO 編号 */
 34         struct semaphore sem; /* 信号量 */
 35 };
 36 
 37 struct gpioled_dev gpioled; /* led 裝置 */
 38 
 39 /*
 40  * @description : 打開裝置
 41  * @param – inode : 傳遞給驅動的 inode
 42  * @param - filp : 裝置檔案,file 結構體有個叫做 private_data 的成員變量
 43  * 一般在 open 的時候将 private_data 指向裝置結構體。
 44  * @return : 0 成功;其他 失敗
 45  */
 46 static int led_open(struct inode *inode, struct file *filp)
 47 {
 48         filp->private_data = &gpioled; /* 設定私有資料 */
 49 
 50         /* 擷取信号量,進入休眠狀态的程序可以被信号打斷 */
 51         if (down_interruptible(&gpioled.sem)) {
 52                 return -ERESTARTSYS;
 53         }
 54 #if 0
 55         down(&gpioled.sem); /* 不能被信号打斷 */
 56 #endif
 57 
 58         return 0;
 59 }
......
 
105 /*
106  * @description : 關閉/釋放裝置
107  * @param – filp : 要關閉的裝置檔案(檔案描述符)
108  * @return : 0 成功;其他 失敗
109  */
110 static int led_release(struct inode *inode, struct file *filp)
111 {
112         struct gpioled_dev *dev = filp->private_data;
113 
114         up(&dev->sem); /* 釋放信号量,信号量值加 1 */
115 
116         return 0;
117 }
118 
119 /* 裝置操作函數 */
120 static struct file_operations gpioled_fops = {
121         .owner = THIS_MODULE,
122         .open = led_open,
123         .read = led_read,
124         .write = led_write,
125         .release = led_release,
126 };
127 
128 /*
129  * @description : 驅動入口函數
130  * @param : 無
131  * @return : 無
132  */
133 static int __init led_init(void)
134 {
135         int ret = 0;
136 
137         /* 初始化信号量 */
138         sema_init(&gpioled.sem, 1);
......
198         return 0;
199 }
200 /*
201  * @description : 驅動出口函數
202  * @param : 無
203  * @return : 無
204  */
205 static void __exit led_exit(void)
206 {
207         /* 登出字元裝置驅動 */
208         cdev_del(&gpioled.cdev);/* 删除 cdev */
209         unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
210 
211         device_destroy(gpioled.class, gpioled.devid);
212         class_destroy(gpioled.class);
213 }
214 
215 module_init(led_init);
216 module_exit(led_exit);
217 MODULE_LICENSE("GPL");
218 MODULE_AUTHOR("topeet");
           

第 17 行,要使用信号量必須添加<linux/semaphore.h>頭檔案。

第 34 行,在裝置結構體中添加一個信号量成員變量 sem。

第 46~59行,在 open函數中申請信号量,可以使用 down 函數,也可以使用 down_interruptible函數。如果信号量值大于等于 1 就表示可用,那麼應用程式就會開始使用 LED 燈。如果信号量值為 0 就表示應用程式不能使用 LED 燈,此時應用程式就會進入到休眠狀态。等到信号量值大于 1 的時候應用程式就會喚醒,申請信号量,擷取 LED 燈使用權。

第 114 行,在 release 函數中調用 up 函數釋放信号量,這樣其他因為沒有得到信号量而進入休眠狀态的應用程式就會喚醒,擷取信号量。

第 138 行,在驅動入口函數中調用 sema_init 函數初始化信号量 sem 的值為 1,相當于 sem是個二值信号量。

總結一下,當信号量 sem 為 1 的時候表示 LED 燈還沒有被使用,如果應用程式 A 要使用LED 燈,先調用 open 函數打開/dev/gpioled,這個時候會擷取信号量 sem,擷取成功以後 sem 的值減 1 變為 0。如果此時應用程式 B 也要使用 LED 燈,調用 open 函數打開/dev/gpioled 就會因為信号量無效(值為 0)而進入休眠狀态。當應用程式 A 運作完畢,調用 close 函數關閉/dev/gpioled的時候就會釋放信号量 sem,此時信号量 sem 的值就會加 1,變為 1。信号量 sem 再次有效,表示其他應用程式可以使用 LED 燈了,此時在休眠狀态的應用程式 A 就會擷取到信号量 sem,擷取成功以後就開始使用 LED 燈。

2 編寫應用測試程式

應用測試程式使用38.1.2中的應用測試程式即可。

3 運作測試

1、編譯驅動程式

添加Makefile檔案,修改obj-m的值為 gpioled_semaphore.o,内容如下:

KERNELDIR := /home/topeet/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := gpioled_semaphore.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”指令進行編譯,生成gpioled_semaphore.ko驅動子產品檔案。

2、運作測試

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

depmod
modprobe gpioled_semaphore
           

驅動子產品加載成功如圖 3.1所示:

i.MX6ULL終結者Linux并發與競争實驗信号量實驗

圖 3.1

驅動子產品加載成功後,使用gpioled_atomic_test應用測試程式進行測試,測試流程和原子變量測試一樣。使用下面的指令打開LED燈:

./gpioled_atomic_test /dev/gpioled 1 &

然後緊接着輸入LED燈關閉指令:

./gpioled_atomic_test /dev/gpioled 0 &

注意兩個指令都是運作在背景,第一條指令先擷取到信号量,是以可以操作 LED 燈,将LED 燈打開,并且占有 25S。第二條指令因為擷取信号量失敗而進入休眠狀态,等待第一條指令運作完畢并釋放信号量以後才擁有 LED 燈使用權,将 LED 燈關閉,運作結果如圖 39.3.3.2所示:

i.MX6ULL終結者Linux并發與競争實驗信号量實驗

圖 3.2

可以看到有兩次運作結果。

解除安裝子產品指令:

rmmod gpioled_semaphore

i.MX6ULL終結者Linux并發與競争實驗信号量實驗

繼續閱讀