日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux并发与竞争实验(一次只允许一个应用程序操作LED灯)

發布時間:2023/12/10 linux 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux并发与竞争实验(一次只允许一个应用程序操作LED灯) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 原子操作實驗
    • 實驗程序編寫
    • 運行測試(運行多個APP搶占資源)
  • 自旋鎖實驗
    • 實驗程序編寫
    • 運行測試
  • 信號量實驗
    • 實驗程序編寫
    • 運行測試(第二條命令因為獲取信號量失敗而進入休眠狀態)
  • 互斥體實驗(類似二值信號量,會休眠)
    • 實驗程序編寫
    • 運行測試

在上一章中我們學習了Linux 下的并發與競爭,并且學習了四種常用的處理并發和競爭的機制:原子操作、自旋鎖、信號量和互斥體。本章我們就通過四個實驗來學習如何在驅動中使用這四種機制。

原子操作實驗

本實驗對應的例程路徑為:開發板光盤-> 2、Linux 驅動例程-> 7_atomic。
本例程我們在第四十五章的gpioled.c 文件基礎上完成。在本節使用中我們使用原子操作來實現對LED 這個設備的互斥訪問,也就是一次只允許一個應用程序可以使用LED 燈。

實驗程序編寫

1、修改設備樹文件
因為本章實驗是在第四十五章實驗的基礎上完成的,因此不需要對設備樹做任何的修改。

2、LED 驅動修改
本節實驗在第四十五章實驗驅動文件gpioled.c 的基礎上修改而來。新建名為“7_atomic”的文件夾,然后在7_atomic 文件夾里面創建vscode 工程,工作區命名為“atomic”。將5_gpioled實驗中的gpioled.c 復制到7_atomic 文件夾中,并且重命名為atomic.c。本節實驗重點就是使用atomic 來實現一次只能允許一個應用訪問LED,所以我們只需要在atomic.c 文件源碼的基礎上加上添加atomic 相關代碼即可,完成以后的atomic.c 文件內容如下所示:

#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : atomic.c 作者 : 左忠凱 版本 : V1.0 描述 : 原子操作實驗,使用原子變量來實現對實現設備的互斥訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/18 左忠凱創建 ***************************************************************/ #define GPIOLED_CNT 1 /* 設備號個數 */ #define GPIOLED_NAME "gpioled" /* 名字 */ #define LEDOFF 0 /* 關燈 */ #define LEDON 1 /* 開燈 *//* gpioled設備結構體 */ struct gpioled_dev{dev_t devid; /* 設備號 */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設備 */int major; /* 主設備號 */int minor; /* 次設備號 */struct device_node *nd; /* 設備節點 */int led_gpio; /* led所使用的GPIO編號 */atomic_t lock; /* 原子變量 */ };struct gpioled_dev gpioled; /* led設備 *//** @description : 打開設備* @param - inode : 傳遞給驅動的inode* @param - filp : 設備文件,file結構體有個叫做private_data的成員變量* 一般在open的時候將private_data指向設備結構體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {/* 通過判斷原子變量的值來檢查LED有沒有被別的應用使用 */if (!atomic_dec_and_test(&gpioled.lock)) {//V減1 不能使用驅動atomic_inc(&gpioled.lock); /* 小于0的話就加1,使其原子變量等于0 */return -EBUSY; /* LED被使用,返回忙 */}filp->private_data = &gpioled; /* 設置私有數據 */return 0; }/** @description : 從設備讀取數據 * @param - filp : 要打開的設備文件(文件描述符)* @param - buf : 返回給用戶空間的數據緩沖區* @param - cnt : 要讀取的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 讀取的字節數,如果為負值,表示讀取失敗*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 向設備寫數據 * @param - filp : 設備文件,表示打開的文件描述符* @param - buf : 要寫給設備寫入的數據* @param - cnt : 要寫入的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 寫入的字節數,如果為負值,表示寫入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 獲取狀態值 */if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打開LED燈 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1); /* 關閉LED燈 */}return 0; }/** @description : 關閉/釋放設備* @param - filp : 要關閉的設備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int led_release(struct inode *inode, struct file *filp) {struct gpioled_dev *dev = filp->private_data;/* 關閉驅動文件的時候釋放原子變量 就是加1 */atomic_inc(&dev->lock);return 0; }/* 設備操作函數 */ static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release, };/** @description : 驅動出口函數* @param : 無* @return : 無*/ static int __init led_init(void) {int ret = 0;/* 初始化原子變量 */atomic_set(&gpioled.lock, 1); /* 原子變量初始值為1 *//* 設置LED所使用的GPIO *//* 1、獲取設備節點:gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;} else {printk("gpioled node find!\r\n");}/* 2、 獲取設備樹中的gpio屬性,得到LED所使用的LED編號 */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、設置GPIO1_IO03為輸出,并且輸出高電平,默認關閉LED燈 */ret = gpio_direction_output(gpioled.led_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注冊字符設備驅動 *//* 1、創建設備號 */if (gpioled.major) { /* 定義了設備號 */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);} else { /* 沒有定義設備號 */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申請設備號 */gpioled.major = MAJOR(gpioled.devid); /* 獲取分配號的主設備號 */gpioled.minor = MINOR(gpioled.devid); /* 獲取分配號的次設備號 */}printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一個cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、創建類 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {return PTR_ERR(gpioled.class);}/* 5、創建設備 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {return PTR_ERR(gpioled.device);}return 0; }/** @description : 驅動出口函數* @param : 無* @return : 無*/ static void __exit led_exit(void) {/* 注銷字符設備驅動 */cdev_del(&gpioled.cdev);/* 刪除cdev */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注銷設備號 */device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第42 行,原子變量lock,用來實現一次只能允許一個應用訪問LED 燈,led_init 驅動入口函數會將lock 的值設置為1。

第57~60 行,每次調用open 函數打開驅動設備的時候先申請lock,如果申請成功的話就表示LED 燈還沒有被其他的應用使用,如果申請失敗就表示LED 燈正在被其他的應用程序使用。每次打開驅動設備的時候先使用atomic_dec_and_test 函數將lock減1,如果atomic_dec_and_test
函數返回值為真就表示lock 當前值為0,說明設備可以使用。如果atomic_dec_and_test 函數返回值為假,就表示lock 當前值為負數(lock 值默認是1),lock 值為負數的可能性只有一個,那就是其他設備正在使用LED。其他設備正在使用LED 燈,那么就只能退出了,在退出之前調用函數atomic_inc 將lock 加1,因為此時lock 的值被減成了負數,必須要對其加1,將lock 的值變為0。

第120 行,LED 燈使用完畢,應用程序調用close 函數關閉的驅動文件,led_release 函數執行,調用atomic_inc 釋放lcok,也就是將lock加1

第143 行,初始化原子變量lock,初始值設置為1,這樣每次就只允許一個應用使用LED燈。

3、編寫測試APP
新建名為atomicApp.c 的測試APP,在里面輸入如下所示內容:

#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : atomicApp.c 作者 : 左忠凱 版本 : V1.0 描述 : 原子變量測試APP,測試原子變量能不能實現一次只允許一個應用程序使用LED。 其他 : 無 使用方法 :./atomicApp /dev/gpioled 0 關閉LED燈./atomicApp /dev/gpioled 1 打開LED燈 論壇 : www.openedv.com 日志 : 初版V1.0 2019/1/30 左忠凱創建 ***************************************************************/#define LEDOFF 0 #define LEDON 1/** @description : main主程序* @param - argc : argv數組元素個數* @param - argv : 具體參數* @return : 0 成功;其他 失敗*/ int main(int argc, char *argv[]) {int fd, retvalue;char *filename;unsigned char cnt = 0;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打開beep驅動 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要執行的操作:打開或關閉 *//* 向/dev/gpioled文件寫入數據 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}/* 模擬占用25S LED */while(1) {sleep(5);cnt++;printf("App running times:%d\r\n", cnt);if(cnt >= 5) break;}printf("App running finished!");retvalue = close(fd); /* 關閉文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0; }

atomicApp.c 中的內容就是在第四十五章的ledAPP.c 的基礎上修改而來的,重點是加入了第63~68 行的模擬占用25 秒LED 的代碼。測試x

運行測試(運行多個APP搶占資源)

1、編譯驅動程序
編寫Makefile 文件,本章實驗的Makefile 文件和第四十章實驗基本一樣,只是將obj-m 變量的值改為atomic.o,Makefile 內容如下所示:

1 KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek ...... 4 obj-m := atomic.o ...... 11 clean: 12 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設置obj-m 變量的值為atomic.o。
輸入如下命令編譯出驅動模塊文件:

make -j32

編譯成功以后就會生成一個名為“atomic.ko”的驅動模塊文件。
2、編譯測試APP
輸入如下命令編譯測試atomicApp.c 這個測試程序:

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

編譯成功以后就會生成atomicApp 這個應用程序。
3、運行測試
將上一小節編譯出來的atomic.ko 和atomicApp 這兩個文件拷貝到rootfs/lib/modules/4.1.15目錄中,重啟開發板,進入到目錄lib/modules/4.1.15 中,輸入如下命令加載atomic.ko 驅動模塊:

depmod //第一次加載驅動的時候需要運行此命令 modprobe atomic.ko //加載驅動

驅動加載成功以后就可以使用atomicApp 軟件來測試驅動是否工作正常,輸入如下命令以后臺運行模式打開LED 燈,“&”表示在后臺運行atomicApp 這個軟件:

./atomicApp /dev/gpioled 1& //打開LED 燈

輸入上述命令以后觀察開發板上的紅色LED 燈是否點亮,然后每隔5 秒都會輸出一行“App running times ”,如圖48.1.2.1 所示:

從圖48.1.2.1 可以看出,atomicApp 運行正常,輸出了“App running times:1”和“App running times:2”,這就是模擬25S 占用,說明atomicApp 這個軟件正在使用LED 燈。此時再輸入如下命令關閉LED 燈:

./atomicApp /dev/gpioled 0 //關閉LED 燈

輸入上述命令以后會發現如圖48.1.2.2 所示輸入信息:

從圖48.1.2.2 可以看出,打開/dev/gpioled 失敗!原因是在圖48.1.2.1 中運行的atomicAPP軟件正在占用/dev/gpioled,如果再次運行atomicApp 軟件去操作/dev/gpioled 肯定會失敗。必須等待圖48.1.2.1中的atomicApp 運行結束,也就是25S 結束以后其他軟件才能去操作/dev/gpioled。

這個就是采用原子變量實現一次只能有一個應用程序訪問LED 燈。
如果要卸載驅動的話輸入如下命令即可:

rmmod atomic.ko

自旋鎖實驗

上一節我們使用原子變量實現了一次只能有一個應用程序訪問LED 燈,本節我們使用自旋鎖來實現此功能。在使用自旋鎖之前,先回顧一下自旋鎖的使用注意事項:

①、自旋鎖保護的臨界區要盡可能的短,因此在open 函數中申請自旋鎖,然后在release 函數中釋放自旋鎖的方法就不可取。我們可以使用一個變量來表示設備的使用情況,如果設備被使用了那么變量就加一,設備被釋放以后變量就減1,我們只需要使用自旋鎖保護這個變量即可。
②、考慮驅動的兼容性,合理的選擇API 函數。

綜上所述,在本節例程中,我們通過定義一個變量dev_stats 表示設備的使用情況,dev_stats為0 的時候表示設備沒有被使用,dev_stats 大于0 的時候表示設備被使用。驅動open 函數中先判斷dev_stats 是否為0,也就是判斷設備是否可用,如果為0 的話就使用設備,并且將dev_stats加1,表示設備被使用了。使用完以后在release 函數中將dev_stats 減1,表示設備沒有被使用了。因此真正實現設備互斥訪問的是變量dev_stats,但是我們要使用自旋鎖對dev_stats 來做保護。

實驗程序編寫

1、修改設備樹文件
本章實驗是在上一節實驗的基礎上完成的,同樣不需要對設備樹做任何的修改。

2、LED 驅動修改
本節實驗在第上一節實驗驅動文件atomic.c 的基礎上修改而來。新建名為“8_spinlock”的文件夾,然后在8_spinlock 文件夾里面創建vscode 工程,工作區命名為“spinlock”。將7_atomic實驗中的atomic.c 復制到8_spinlock 文件夾中,并且重命名為spinlock.c。將原來使用atomic 的地方換為spinlock 即可,其他代碼不需要修改,完成以后的spinlock.c 文件內容如下所示(有省
略):

#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : spinlock.c 作者 : 左忠凱 版本 : V1.0 描述 : 自旋鎖實驗,使用自旋鎖來實現對實現設備的互斥訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/18 左忠凱創建 ***************************************************************/ #define GPIOLED_CNT 1 /* 設備號個數 */ #define GPIOLED_NAME "gpioled" /* 名字 */ #define LEDOFF 0 /* 關燈 */ #define LEDON 1 /* 開燈 *//* gpioled設備結構體 */ struct gpioled_dev{dev_t devid; /* 設備號 */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設備 */int major; /* 主設備號 */int minor; /* 次設備號 */struct device_node *nd; /* 設備節點 */int led_gpio; /* led所使用的GPIO編號 */int dev_stats; /* 設備使用狀態,0,設備未使用;>0,設備已經被使用 */spinlock_t lock; /* 自旋鎖 */ };struct gpioled_dev gpioled; /* led設備 *//** @description : 打開設備* @param - inode : 傳遞給驅動的inode* @param - filp : 設備文件,file結構體有個叫做private_data的成員變量* 一般在open的時候將private_data指向設備結構體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {unsigned long flags;filp->private_data = &gpioled; /* 設置私有數據 */spin_lock_irqsave(&gpioled.lock, flags); /* 上鎖 */if (gpioled.dev_stats) { /* 如果設備被使用了 大于1 */spin_unlock_irqrestore(&gpioled.lock, flags);/* 解鎖 */return -EBUSY;}gpioled.dev_stats++; /* 如果設備沒有打開,那么就標記已經被使用了 */spin_unlock_irqrestore(&gpioled.lock, flags);/* 解鎖 */return 0; }/** @description : 從設備讀取數據 * @param - filp : 要打開的設備文件(文件描述符)* @param - buf : 返回給用戶空間的數據緩沖區* @param - cnt : 要讀取的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 讀取的字節數,如果為負值,表示讀取失敗*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 向設備寫數據 * @param - filp : 設備文件,表示打開的文件描述符* @param - buf : 要寫給設備寫入的數據* @param - cnt : 要寫入的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 寫入的字節數,如果為負值,表示寫入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 獲取狀態值 */if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打開LED燈 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1); /* 關閉LED燈 */}return 0; }/** @description : 關閉/釋放設備* @param - filp : 要關閉的設備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int led_release(struct inode *inode, struct file *filp) {unsigned long flags;struct gpioled_dev *dev = filp->private_data;/* 關閉驅動文件的時候將dev_stats減1 */spin_lock_irqsave(&dev->lock, flags); /* 上鎖 */if (dev->dev_stats) {dev->dev_stats--;}spin_unlock_irqrestore(&dev->lock, flags);/* 解鎖 */return 0; }/* 設備操作函數 */ static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release, };/** @description : 驅動入口函數* @param : 無* @return : 無*/ static int __init led_init(void) {int ret = 0;/* 初始化自旋鎖 */spin_lock_init(&gpioled.lock);/* 設置LED所使用的GPIO *//* 1、獲取設備節點:gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;} else {printk("gpioled node find!\r\n");}/* 2、 獲取設備樹中的gpio屬性,得到LED所使用的LED編號 */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、設置GPIO1_IO03為輸出,并且輸出高電平,默認關閉LED燈 */ret = gpio_direction_output(gpioled.led_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注冊字符設備驅動 *//* 1、創建設備號 */if (gpioled.major) { /* 定義了設備號 */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);} else { /* 沒有定義設備號 */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申請設備號 */gpioled.major = MAJOR(gpioled.devid); /* 獲取分配號的主設備號 */gpioled.minor = MINOR(gpioled.devid); /* 獲取分配號的次設備號 */}printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一個cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、創建類 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {return PTR_ERR(gpioled.class);}/* 5、創建設備 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {return PTR_ERR(gpioled.device);}return 0; }/** @description : 驅動出口函數* @param : 無* @return : 無*/ static void __exit led_exit(void) {/* 注銷字符設備驅動 */cdev_del(&gpioled.cdev);/* 刪除cdev */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注銷設備號 */device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第43 行,dev_stats 表示設備狀態,如果為0 的話表示設備還沒有被使用,如果大于0 的話就表示設備已經被使用了。

第44 行,定義自旋鎖變量lock

第61~67 行,使用自旋鎖實現對設備的互斥訪問,第61 行調用spin_lock_irqsave 函數獲取鎖,為了考慮到驅動兼容性,這里并沒有使用spin_lock 函數來獲取鎖。第62 行判斷dev_stats 是否大于0,如果是的話表示設備已經被使用了,那么就調用spin_unlock_irqrestore函數釋放鎖,并且返回-EBUSY。如果設備沒有被使用的話就在第66 行將dev_stats 加1,表示設備要被使用了,然后調用spin_unlock_irqrestore 函數釋放鎖。
自旋鎖的工作就是保護dev_stats 變量,真正實現對設備互斥訪問的是變量dev_stats

第126~131 行,在release 函數中將dev_stats 減1,表示設備被釋放了,可以被其他的應用程序使用。將dev_stats 減1 的時候需要自旋鎖對其進行保護。

第155 行,在驅動入口函數led_init 中調用spin_lock_init 函數初始化自旋鎖。

3、編寫測試APP

測試APP 使用48.1.1 小節中的atomicApp.c 即可,將7_atomic 中的atomicApp.c 文件到本例程中,并將atomicApp.c 重命名為spinlockApp.c 即可。

運行測試

1、編譯驅動程序
編寫Makefile 文件,本章實驗的Makefile 文件和第四十章實驗基本一樣,只是將obj-m 變量的值改為spinlock.o,Makefile 內容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := spinlock.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設置obj-m 變量的值為spinlock.o。
輸入如下命令編譯出驅動模塊文件:

make -j32

編譯成功以后就會生成一個名為“spinlock.ko”的驅動模塊文件。
2、編譯測試APP
輸入如下命令編譯測試spinlockApp.c 這個測試程序:

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

編譯成功以后就會生成spinlockApp 這個應用程序。
3、運行測試
將上一小節編譯出來的spinlock.ko 和spinlockApp 這兩個文件拷貝到
rootfs/lib/modules/4.1.15 目錄中,重啟開發板,進入到目錄lib/modules/4.1.15 中,輸入如下命令加載spinlock.ko 驅動模塊:

depmod //第一次加載驅動的時候需要運行此命令 modprobe spinlock.ko //加載驅動

驅動加載成功以后就可以使用spinlockApp 軟件測試驅動是否工作正常,測試方法和48.1.2小節中一樣,先輸入如下命令讓spinlockAPP 軟件模擬占用25S 的LED 燈:

./spinlockApp /dev/gpioled 1& //打開LED 燈

緊接著再輸入如下命令關閉LED 燈:

./spinlockApp /dev/gpioled 0 //關閉LED 燈

看一下能不能關閉LED 燈,驅動正常工作的話并不會馬上關閉LED 燈,會提示你“file /dev/gpioled open failed!”,必須等待第一個spinlockApp 軟件運行完成(25S 計時結束)才可以再次操作LED 燈。

如果要卸載驅動的話輸入如下命令即可:

rmmod spinlock.ko

信號量實驗

本節我們來使用信號量實現了一次只能有一個應用程序訪問LED 燈,信號量可以導致休眠,因此信號量保護的臨界區沒有運行時間限制,可以在驅動的open 函數申請信號量,然后在release 函數中釋放信號量。但是信號量不能用在中斷中,本節實驗我們不會在中斷中使用信號量。

實驗程序編寫

1、修改設備樹文件
本章實驗是在上一節實驗的基礎上完成的,同樣不需要對設備樹做任何的修改。

2、LED 驅動修改
本節實驗在第上一節實驗驅動文件spinlock.c 的基礎上修改而來。新建名為“9_semaphore”的文件夾,然后在9_semaphore 文件夾里面創建vscode 工程,工作區命名為“semaphore”。將8_spinlock 實驗中的spinlock.c 復制到9_semaphore 文件夾中,并且重命名為semaphore.c。將原來使用到自旋鎖的地方換為信號量即可,其他的內容基本不變,完成以后的semaphore.c 文件內容如下所示(有省略):

#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <linux/semaphore.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : semaphore.c 作者 : 左忠凱 版本 : V1.0 描述 : 信號量實驗,使用信號量來實現對實現設備的互斥訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/18 左忠凱創建 ***************************************************************/ #define GPIOLED_CNT 1 /* 設備號個數 */ #define GPIOLED_NAME "gpioled" /* 名字 */ #define LEDOFF 0 /* 關燈 */ #define LEDON 1 /* 開燈 *//* gpioled設備結構體 */ struct gpioled_dev{dev_t devid; /* 設備號 */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設備 */int major; /* 主設備號 */int minor; /* 次設備號 */struct device_node *nd; /* 設備節點 */int led_gpio; /* led所使用的GPIO編號 */struct semaphore sem; /* 信號量 */ };struct gpioled_dev gpioled; /* led設備 *//** @description : 打開設備* @param - inode : 傳遞給驅動的inode* @param - filp : 設備文件,file結構體有個叫做private_data的成員變量* 一般在open的時候將private_data指向設備結構體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {filp->private_data = &gpioled; /* 設置私有數據 *//* 獲取信號量 */if (down_interruptible(&gpioled.sem)) { /* 獲取信號量,進入休眠狀態的進程可以被信號打斷 */return -ERESTARTSYS;} #if 0down(&gpioled.sem); /* 不能被信號打斷 */ #endifreturn 0; }/** @description : 從設備讀取數據 * @param - filp : 要打開的設備文件(文件描述符)* @param - buf : 返回給用戶空間的數據緩沖區* @param - cnt : 要讀取的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 讀取的字節數,如果為負值,表示讀取失敗*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 向設備寫數據 * @param - filp : 設備文件,表示打開的文件描述符* @param - buf : 要寫給設備寫入的數據* @param - cnt : 要寫入的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 寫入的字節數,如果為負值,表示寫入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 獲取狀態值 */if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打開LED燈 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1); /* 關閉LED燈 */}return 0; }/** @description : 關閉/釋放設備* @param - filp : 要關閉的設備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int led_release(struct inode *inode, struct file *filp) {struct gpioled_dev *dev = filp->private_data;up(&dev->sem); /* 釋放信號量,信號量值加1 */return 0; }/* 設備操作函數 */ static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release, };/** @description : 驅動入口函數* @param : 無* @return : 無*/ static int __init led_init(void) {int ret = 0;/* 初始化信號量 */sema_init(&gpioled.sem, 1);/* 設置LED所使用的GPIO *//* 1、獲取設備節點:gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;} else {printk("gpioled node find!\r\n");}/* 2、 獲取設備樹中的gpio屬性,得到LED所使用的LED編號 */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、設置GPIO1_IO03為輸出,并且輸出高電平,默認關閉LED燈 */ret = gpio_direction_output(gpioled.led_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注冊字符設備驅動 *//* 1、創建設備號 */if (gpioled.major) { /* 定義了設備號 */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);} else { /* 沒有定義設備號 */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申請設備號 */gpioled.major = MAJOR(gpioled.devid); /* 獲取分配號的主設備號 */gpioled.minor = MINOR(gpioled.devid); /* 獲取分配號的次設備號 */}printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一個cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、創建類 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {return PTR_ERR(gpioled.class);}/* 5、創建設備 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {return PTR_ERR(gpioled.device);}return 0; }/** @description : 驅動出口函數* @param : 無* @return : 無*/ static void __exit led_exit(void) {/* 注銷字符設備驅動 */cdev_del(&gpioled.cdev);/* 刪除cdev */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注銷設備號 */device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第14 行,要使用信號量必須添加<linux/semaphore.h>頭文件。

第43 行,在設備結構體中添加一個信號量成員變量sem

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

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

第147 行,在驅動入口函數中調用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 燈了,此時在休眠狀態的應用程序B 就會獲取到信號量sem,獲取成功以后就開始使用LED 燈。

3、編寫測試APP
測試APP 使用48.1.1 小節中的atomicApp.c 即可,將7_atomic 中的atomicApp.c 文件到本例程中,并將atomicApp.c 重命名為semaApp.c 即可。

運行測試(第二條命令因為獲取信號量失敗而進入休眠狀態)

1、編譯驅動程序
編寫Makefile 文件,本章實驗的Makefile 文件和第四十章實驗基本一樣,只是將obj-m 變量的值改為semaphore.o,Makefile 內容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := semaphore.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設置obj-m 變量的值為semaphore.o。
輸入如下命令編譯出驅動模塊文件:

make -j32

編譯成功以后就會生成一個名為“semaphore.ko”的驅動模塊文件。
2、編譯測試APP

輸入如下命令編譯測試semaApp.c 這個測試程序:

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

編譯成功以后就會生成semaApp 這個應用程序。
3、運行測試
將上一小節編譯出來的semaphore.ko 和semaApp 這兩個文件拷貝到
rootfs/lib/modules/4.1.15 目錄中,重啟開發板,進入到目錄lib/modules/4.1.15 中,輸入如下命令
加載semaphore.ko 驅動模塊:

depmod //第一次加載驅動的時候需要運行此命令 modprobe semaphore.ko //加載驅動

驅動加載成功以后就可以使用semaApp 軟件測試驅動是否工作正常,測試方法和48.1.2 小
節中一樣,先輸入如下命令讓semaApp 軟件模擬占用25S 的LED 燈:

./ semaApp /dev/gpioled 1& //打開LED 燈

緊接著再輸入如下命令關閉LED 燈:

./ semaApp /dev/gpioled 0& //關閉LED 燈

注意兩個命令都是運行在后臺,第一條命令先獲取到信號量,因此可以操作LED 燈,將LED 燈打開,并且占有25S。
第二條命令因為獲取信號量失敗而進入休眠狀態,等待第一條命令運行完畢并釋放信號量以后才擁有LED 燈使用權,將LED 燈關閉,運行結果如圖48.3.2.1 所示:

如果要卸載驅動的話輸入如下命令即可:

rmmod semaphore.ko

互斥體實驗(類似二值信號量,會休眠)

前面我們使用原子操作、自旋鎖和信號量實現了對LED 燈的互斥訪問,但是最適合互斥的就是互斥體mutex 了。本節我們來學習一下如何使用mutex 實現對LED 燈的互斥訪問。

實驗程序編寫

1、修改設備樹文件
本章實驗是在上一節實驗的基礎上完成的,同樣不需要對設備樹做任何的修改。
2、LED 驅動修改
本節實驗在第上一節實驗驅動文件semaphore.c 的基礎上修改而來。新建名為“10_mutex”的文件夾,然后在10_mutex 文件夾里面創建vscode 工程,工作區命名為“mutex”。將9_semaphore實驗中的semaphore.c 復制到10_mutex 文件夾中,并且重命名為mutex.c。將原來使用到信號量的地方換為mutex 即可,其他的內容基本不變,完成以后的mutex.c 文件內容如下所示(有省略):

#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <linux/semaphore.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : mutex.c 作者 : 左忠凱 版本 : V1.0 描述 : 互斥體實驗,使用互斥體來實現對實現設備的互斥訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/18 左忠凱創建 ***************************************************************/ #define GPIOLED_CNT 1 /* 設備號個數 */ #define GPIOLED_NAME "gpioled" /* 名字 */ #define LEDOFF 0 /* 關燈 */ #define LEDON 1 /* 開燈 *//* gpioled設備結構體 */ struct gpioled_dev{dev_t devid; /* 設備號 */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設備 */int major; /* 主設備號 */int minor; /* 次設備號 */struct device_node *nd; /* 設備節點 */int led_gpio; /* led所使用的GPIO編號 */struct mutex lock; /* 互斥體 */ };struct gpioled_dev gpioled; /* led設備 *//** @description : 打開設備* @param - inode : 傳遞給驅動的inode* @param - filp : 設備文件,file結構體有個叫做private_data的成員變量* 一般在open的時候將private_data指向設備結構體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {filp->private_data = &gpioled; /* 設置私有數據 *//* 獲取互斥體,可以被信號打斷 */if (mutex_lock_interruptible(&gpioled.lock)) {return -ERESTARTSYS;} #if 0mutex_lock(&gpioled.lock); /* 不能被信號打斷 */ #endifreturn 0; }/** @description : 從設備讀取數據 * @param - filp : 要打開的設備文件(文件描述符)* @param - buf : 返回給用戶空間的數據緩沖區* @param - cnt : 要讀取的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 讀取的字節數,如果為負值,表示讀取失敗*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 向設備寫數據 * @param - filp : 設備文件,表示打開的文件描述符* @param - buf : 要寫給設備寫入的數據* @param - cnt : 要寫入的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 寫入的字節數,如果為負值,表示寫入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 獲取狀態值 */if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打開LED燈 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1); /* 關閉LED燈 */}return 0; }/** @description : 關閉/釋放設備* @param - filp : 要關閉的設備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int led_release(struct inode *inode, struct file *filp) {struct gpioled_dev *dev = filp->private_data;/* 釋放互斥鎖 */mutex_unlock(&dev->lock);return 0; }/* 設備操作函數 */ static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release, };/** @description : 驅動入口函數* @param : 無* @return : 無*/ static int __init led_init(void) {int ret = 0;/* 初始化互斥體 */mutex_init(&gpioled.lock);/* 設置LED所使用的GPIO *//* 1、獲取設備節點:gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;} else {printk("gpioled node find!\r\n");}/* 2、 獲取設備樹中的gpio屬性,得到LED所使用的LED編號 */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、設置GPIO1_IO03為輸出,并且輸出高電平,默認關閉LED燈 */ret = gpio_direction_output(gpioled.led_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注冊字符設備驅動 *//* 1、創建設備號 */if (gpioled.major) { /* 定義了設備號 */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);} else { /* 沒有定義設備號 */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申請設備號 */gpioled.major = MAJOR(gpioled.devid); /* 獲取分配號的主設備號 */gpioled.minor = MINOR(gpioled.devid); /* 獲取分配號的次設備號 */}printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一個cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、創建類 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {return PTR_ERR(gpioled.class);}/* 5、創建設備 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {return PTR_ERR(gpioled.device);}return 0; }/** @description : 驅動出口函數* @param : 無* @return : 無*/ static void __exit led_exit(void) {/* 注銷字符設備驅動 */cdev_del(&gpioled.cdev);/* 刪除cdev */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注銷設備號 */device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第43 行,定義互斥體lock。
第60~65 行,在open 函數中調用mutex_lock_interruptible 或者mutex_lock 獲取mutex,成功的話就表示可以使用LED 燈,失敗的話就會進入休眠狀態,和信號量一樣。
第124 行,在release 函數中調用mutex_unlock 函數釋放mutex,這樣其他應用程序就可以獲取mutex 了。
第148 行,在驅動入口函數中調用mutex_init 初始化mutex。

互斥體和二值信號量類似,只不過互斥體是專門用于互斥訪問的。

3、編寫測試APP
測試APP 使用48.1.1 小節中的atomicApp.c 即可,將7_atomic 中的atomicApp.c 文件到本例程中,并將atomicApp.c 重命名為mutexApp.c 即可。

運行測試

1、編譯驅動程序
編寫Makefile 文件,本章實驗的Makefile 文件和第四十章實驗基本一樣,只是將obj-m 變量的值改為mutex.o,Makefile 內容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := mutex.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設置obj-m 變量的值為mutex.o。
輸入如下命令編譯出驅動模塊文件:

make -j32

編譯成功以后就會生成一個名為“mutex.ko”的驅動模塊文件。

2、編譯測試APP
輸入如下命令編譯測試mutexApp.c 這個測試程序:

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

編譯成功以后就會生成mutexApp 這個應用程序。
3、運行測試
將上一小節編譯出來的mutex.ko 和mutexApp 這兩個文件拷貝到rootfs/lib/modules/4.1.15目錄中,重啟開發板,進入到目錄lib/modules/4.1.15 中,輸入如下命令加載mutex.ko 驅動模塊:

depmod //第一次加載驅動的時候需要運行此命令 modprobe mutex.ko //加載驅動

驅動加載成功以后就可以使用mutexApp 軟件測試驅動是否工作正常,測試方法和48.3.2中測試信號量的方法一樣。

如果要卸載驅動的話輸入如下命令即可:

rmmod mutex.ko

總結

以上是生活随笔為你收集整理的Linux并发与竞争实验(一次只允许一个应用程序操作LED灯)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。