目錄
- 原子操作實(shí)驗(yàn)
- 實(shí)驗(yàn)程序編寫
- 運(yùn)行測(cè)試(運(yùn)行多個(gè)APP搶占資源)
- 自旋鎖實(shí)驗(yàn)
- 實(shí)驗(yàn)程序編寫
- 運(yùn)行測(cè)試
- 信號(hào)量實(shí)驗(yàn)
- 實(shí)驗(yàn)程序編寫
- 運(yùn)行測(cè)試(第二條命令因?yàn)楂@取信號(hào)量失敗而進(jìn)入休眠狀態(tài))
- 互斥體實(shí)驗(yàn)(類似二值信號(hào)量,會(huì)休眠)
- 實(shí)驗(yàn)程序編寫
- 運(yùn)行測(cè)試
在上一章中我們學(xué)習(xí)了Linux 下的并發(fā)與競(jìng)爭(zhēng),并且學(xué)習(xí)了四種常用的處理并發(fā)和競(jìng)爭(zhēng)的機(jī)制:原子操作、自旋鎖、信號(hào)量和互斥體。本章我們就通過四個(gè)實(shí)驗(yàn)來學(xué)習(xí)如何在驅(qū)動(dòng)中使用這四種機(jī)制。
原子操作實(shí)驗(yàn)
本實(shí)驗(yàn)對(duì)應(yīng)的例程路徑為:開發(fā)板光盤-> 2、Linux 驅(qū)動(dòng)例程-> 7_atomic。
本例程我們?cè)诘谒氖逭碌膅pioled.c 文件基礎(chǔ)上完成。在本節(jié)使用中我們使用原子操作來實(shí)現(xiàn)對(duì)LED 這個(gè)設(shè)備的互斥訪問,也就是一次只允許一個(gè)應(yīng)用程序可以使用LED 燈。
實(shí)驗(yàn)程序編寫
1、修改設(shè)備樹文件
因?yàn)楸菊聦?shí)驗(yàn)是在第四十五章實(shí)驗(yàn)的基礎(chǔ)上完成的,因此不需要對(duì)設(shè)備樹做任何的修改。
2、LED 驅(qū)動(dòng)修改
本節(jié)實(shí)驗(yàn)在第四十五章實(shí)驗(yàn)驅(qū)動(dòng)文件gpioled.c 的基礎(chǔ)上修改而來。新建名為“7_atomic”的文件夾,然后在7_atomic 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“atomic”。將5_gpioled實(shí)驗(yàn)中的gpioled.c 復(fù)制到7_atomic 文件夾中,并且重命名為atomic.c。本節(jié)實(shí)驗(yàn)重點(diǎn)就是使用atomic 來實(shí)現(xiàn)一次只能允許一個(gè)應(yīng)用訪問LED,所以我們只需要在atomic.c 文件源碼的基礎(chǔ)上加上添加atomic 相關(guān)代碼即可,完成以后的atomic.c 文件內(nèi)容如下所示:
#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>
#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LEDOFF 0
#define LEDON 1
struct gpioled_dev{dev_t devid
; struct cdev cdev
; struct class *class
; struct device *device
; int major
; int minor
; struct device_node *nd
; int led_gpio
; atomic_t lock
;
};struct gpioled_dev gpioled
;
static int led_open(struct inode *inode
, struct file *filp
)
{if (!atomic_dec_and_test(&gpioled
.lock
)) {atomic_inc(&gpioled
.lock
); return -EBUSY
; }filp
->private_data
= &gpioled
; return 0;
}
static ssize_t led_read(struct file *filp
, char __user
*buf
, size_t cnt
, loff_t *offt
)
{return 0;
}
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); } else if(ledstat
== LEDOFF
) {gpio_set_value(dev
->led_gpio
, 1); }return 0;
}
static int led_release(struct inode *inode
, struct file *filp
)
{struct gpioled_dev *dev
= filp
->private_data
;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
,
};
static int __init
led_init(void)
{int ret
= 0;atomic_set(&gpioled
.lock
, 1); 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");}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
);ret
= gpio_direction_output(gpioled
.led_gpio
, 1);if(ret
< 0) {printk("can't set gpio!\r\n");}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
); gpioled
.cdev
.owner
= THIS_MODULE
;cdev_init(&gpioled
.cdev
, &gpioled_fops
);cdev_add(&gpioled
.cdev
, gpioled
.devid
, GPIOLED_CNT
);gpioled
.class
= class_create(THIS_MODULE
, GPIOLED_NAME
);if (IS_ERR(gpioled
.class
)) {return PTR_ERR(gpioled
.class
);}gpioled
.device
= device_create(gpioled
.class
, NULL, gpioled
.devid
, NULL, GPIOLED_NAME
);if (IS_ERR(gpioled
.device
)) {return PTR_ERR(gpioled
.device
);}return 0;
}
static void __exit
led_exit(void)
{cdev_del(&gpioled
.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,用來實(shí)現(xiàn)一次只能允許一個(gè)應(yīng)用訪問LED 燈,led_init 驅(qū)動(dòng)入口函數(shù)會(huì)將lock 的值設(shè)置為1。
第57~60 行,每次調(diào)用open 函數(shù)打開驅(qū)動(dòng)設(shè)備的時(shí)候先申請(qǐng)lock,如果申請(qǐng)成功的話就表示LED 燈還沒有被其他的應(yīng)用使用,如果申請(qǐng)失敗就表示LED 燈正在被其他的應(yīng)用程序使用。每次打開驅(qū)動(dòng)設(shè)備的時(shí)候先使用atomic_dec_and_test 函數(shù)將lock減1,如果atomic_dec_and_test
函數(shù)返回值為真就表示lock 當(dāng)前值為0,說明設(shè)備可以使用。如果atomic_dec_and_test 函數(shù)返回值為假,就表示lock 當(dāng)前值為負(fù)數(shù)(lock 值默認(rèn)是1),lock 值為負(fù)數(shù)的可能性只有一個(gè),那就是其他設(shè)備正在使用LED。其他設(shè)備正在使用LED 燈,那么就只能退出了,在退出之前調(diào)用函數(shù)atomic_inc 將lock 加1,因?yàn)榇藭r(shí)lock 的值被減成了負(fù)數(shù),必須要對(duì)其加1,將lock 的值變?yōu)?。
第120 行,LED 燈使用完畢,應(yīng)用程序調(diào)用close 函數(shù)關(guān)閉的驅(qū)動(dòng)文件,led_release 函數(shù)執(zhí)行,調(diào)用atomic_inc 釋放lcok,也就是將lock加1。
第143 行,初始化原子變量lock,初始值設(shè)置為1,這樣每次就只允許一個(gè)應(yīng)用使用LED燈。
3、編寫測(cè)試APP
新建名為atomicApp.c 的測(cè)試APP,在里面輸入如下所示內(nèi)容:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#define LEDOFF 0
#define LEDON 1
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];fd
= open(filename
, O_RDWR
);if(fd
< 0){printf("file %s open failed!\r\n", argv
[1]);return -1;}databuf
[0] = atoi(argv
[2]); retvalue
= write(fd
, databuf
, sizeof(databuf
));if(retvalue
< 0){printf("LED Control Failed!\r\n");close(fd
);return -1;}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 中的內(nèi)容就是在第四十五章的ledAPP.c 的基礎(chǔ)上修改而來的,重點(diǎn)是加入了第63~68 行的模擬占用25 秒LED 的代碼。測(cè)試x!
運(yùn)行測(cè)試(運(yùn)行多個(gè)APP搶占資源)
1、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為atomic.o,Makefile 內(nèi)容如下所示:
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 行,設(shè)置obj-m 變量的值為atomic.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:
make
-j32
編譯成功以后就會(huì)生成一個(gè)名為“atomic.ko”的驅(qū)動(dòng)模塊文件。
2、編譯測(cè)試APP
輸入如下命令編譯測(cè)試atomicApp.c 這個(gè)測(cè)試程序:
arm
-linux
-gnueabihf
-gcc atomicApp
.c
-o atomicApp
編譯成功以后就會(huì)生成atomicApp 這個(gè)應(yīng)用程序。
3、運(yùn)行測(cè)試
將上一小節(jié)編譯出來的atomic.ko 和atomicApp 這兩個(gè)文件拷貝到rootfs/lib/modules/4.1.15目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令加載atomic.ko 驅(qū)動(dòng)模塊:
depmod
modprobe atomic
.ko
驅(qū)動(dòng)加載成功以后就可以使用atomicApp 軟件來測(cè)試驅(qū)動(dòng)是否工作正常,輸入如下命令以后臺(tái)運(yùn)行模式打開LED 燈,“&”表示在后臺(tái)運(yùn)行atomicApp 這個(gè)軟件:
./atomicApp
/dev
/gpioled
1&
輸入上述命令以后觀察開發(fā)板上的紅色LED 燈是否點(diǎn)亮,然后每隔5 秒都會(huì)輸出一行“App running times ”,如圖48.1.2.1 所示:
從圖48.1.2.1 可以看出,atomicApp 運(yùn)行正常,輸出了“App running times:1”和“App running times:2”,這就是模擬25S 占用,說明atomicApp 這個(gè)軟件正在使用LED 燈。此時(shí)再輸入如下命令關(guān)閉LED 燈:
./atomicApp
/dev
/gpioled
0
輸入上述命令以后會(huì)發(fā)現(xiàn)如圖48.1.2.2 所示輸入信息:
從圖48.1.2.2 可以看出,打開/dev/gpioled 失敗!原因是在圖48.1.2.1 中運(yùn)行的atomicAPP軟件正在占用/dev/gpioled,如果再次運(yùn)行atomicApp 軟件去操作/dev/gpioled 肯定會(huì)失敗。必須等待圖48.1.2.1中的atomicApp 運(yùn)行結(jié)束,也就是25S 結(jié)束以后其他軟件才能去操作/dev/gpioled。
這個(gè)就是采用原子變量實(shí)現(xiàn)一次只能有一個(gè)應(yīng)用程序訪問LED 燈。
如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:
rmmod atomic
.ko
自旋鎖實(shí)驗(yàn)
上一節(jié)我們使用原子變量實(shí)現(xiàn)了一次只能有一個(gè)應(yīng)用程序訪問LED 燈,本節(jié)我們使用自旋鎖來實(shí)現(xiàn)此功能。在使用自旋鎖之前,先回顧一下自旋鎖的使用注意事項(xiàng):
①、自旋鎖保護(hù)的臨界區(qū)要盡可能的短,因此在open 函數(shù)中申請(qǐng)自旋鎖,然后在release 函數(shù)中釋放自旋鎖的方法就不可取。我們可以使用一個(gè)變量來表示設(shè)備的使用情況,如果設(shè)備被使用了那么變量就加一,設(shè)備被釋放以后變量就減1,我們只需要使用自旋鎖保護(hù)這個(gè)變量即可。
②、考慮驅(qū)動(dòng)的兼容性,合理的選擇API 函數(shù)。
綜上所述,在本節(jié)例程中,我們通過定義一個(gè)變量dev_stats 表示設(shè)備的使用情況,dev_stats為0 的時(shí)候表示設(shè)備沒有被使用,dev_stats 大于0 的時(shí)候表示設(shè)備被使用。驅(qū)動(dòng)open 函數(shù)中先判斷dev_stats 是否為0,也就是判斷設(shè)備是否可用,如果為0 的話就使用設(shè)備,并且將dev_stats加1,表示設(shè)備被使用了。使用完以后在release 函數(shù)中將dev_stats 減1,表示設(shè)備沒有被使用了。因此真正實(shí)現(xiàn)設(shè)備互斥訪問的是變量dev_stats,但是我們要使用自旋鎖對(duì)dev_stats 來做保護(hù)。
實(shí)驗(yàn)程序編寫
1、修改設(shè)備樹文件
本章實(shí)驗(yàn)是在上一節(jié)實(shí)驗(yàn)的基礎(chǔ)上完成的,同樣不需要對(duì)設(shè)備樹做任何的修改。
2、LED 驅(qū)動(dòng)修改
本節(jié)實(shí)驗(yàn)在第上一節(jié)實(shí)驗(yàn)驅(qū)動(dòng)文件atomic.c 的基礎(chǔ)上修改而來。新建名為“8_spinlock”的文件夾,然后在8_spinlock 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“spinlock”。將7_atomic實(shí)驗(yàn)中的atomic.c 復(fù)制到8_spinlock 文件夾中,并且重命名為spinlock.c。將原來使用atomic 的地方換為spinlock 即可,其他代碼不需要修改,完成以后的spinlock.c 文件內(nèi)容如下所示(有省
略):
#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>
#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LEDOFF 0
#define LEDON 1
struct gpioled_dev{dev_t devid
; struct cdev cdev
; struct class *class
; struct device *device
; int major
; int minor
; struct device_node *nd
; int led_gpio
; int dev_stats
; spinlock_t lock
;
};struct gpioled_dev gpioled
;
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
) { spin_unlock_irqrestore(&gpioled
.lock
, flags
);return -EBUSY
;}gpioled
.dev_stats
++; spin_unlock_irqrestore(&gpioled
.lock
, flags
);return 0;
}
static ssize_t led_read(struct file *filp
, char __user
*buf
, size_t cnt
, loff_t *offt
)
{return 0;
}
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); } else if(ledstat
== LEDOFF
) {gpio_set_value(dev
->led_gpio
, 1); }return 0;
}
static int led_release(struct inode *inode
, struct file *filp
)
{unsigned long flags
;struct gpioled_dev *dev
= filp
->private_data
;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
,
};
static int __init
led_init(void)
{int ret
= 0;spin_lock_init(&gpioled
.lock
);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");}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
);ret
= gpio_direction_output(gpioled
.led_gpio
, 1);if(ret
< 0) {printk("can't set gpio!\r\n");}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
); gpioled
.cdev
.owner
= THIS_MODULE
;cdev_init(&gpioled
.cdev
, &gpioled_fops
);cdev_add(&gpioled
.cdev
, gpioled
.devid
, GPIOLED_CNT
);gpioled
.class
= class_create(THIS_MODULE
, GPIOLED_NAME
);if (IS_ERR(gpioled
.class
)) {return PTR_ERR(gpioled
.class
);}gpioled
.device
= device_create(gpioled
.class
, NULL, gpioled
.devid
, NULL, GPIOLED_NAME
);if (IS_ERR(gpioled
.device
)) {return PTR_ERR(gpioled
.device
);}return 0;
}
static void __exit
led_exit(void)
{cdev_del(&gpioled
.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 表示設(shè)備狀態(tài),如果為0 的話表示設(shè)備還沒有被使用,如果大于0 的話就表示設(shè)備已經(jīng)被使用了。
第44 行,定義自旋鎖變量lock。
第61~67 行,使用自旋鎖實(shí)現(xiàn)對(duì)設(shè)備的互斥訪問,第61 行調(diào)用spin_lock_irqsave 函數(shù)獲取鎖,為了考慮到驅(qū)動(dòng)兼容性,這里并沒有使用spin_lock 函數(shù)來獲取鎖。第62 行判斷dev_stats 是否大于0,如果是的話表示設(shè)備已經(jīng)被使用了,那么就調(diào)用spin_unlock_irqrestore函數(shù)釋放鎖,并且返回-EBUSY。如果設(shè)備沒有被使用的話就在第66 行將dev_stats 加1,表示設(shè)備要被使用了,然后調(diào)用spin_unlock_irqrestore 函數(shù)釋放鎖。
自旋鎖的工作就是保護(hù)dev_stats 變量,真正實(shí)現(xiàn)對(duì)設(shè)備互斥訪問的是變量dev_stats。
第126~131 行,在release 函數(shù)中將dev_stats 減1,表示設(shè)備被釋放了,可以被其他的應(yīng)用程序使用。將dev_stats 減1 的時(shí)候需要自旋鎖對(duì)其進(jìn)行保護(hù)。
第155 行,在驅(qū)動(dòng)入口函數(shù)led_init 中調(diào)用spin_lock_init 函數(shù)初始化自旋鎖。
3、編寫測(cè)試APP
測(cè)試APP 使用48.1.1 小節(jié)中的atomicApp.c 即可,將7_atomic 中的atomicApp.c 文件到本例程中,并將atomicApp.c 重命名為spinlockApp.c 即可。
運(yùn)行測(cè)試
1、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為spinlock.o,Makefile 內(nèi)容如下所示:
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 行,設(shè)置obj-m 變量的值為spinlock.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:
make
-j32
編譯成功以后就會(huì)生成一個(gè)名為“spinlock.ko”的驅(qū)動(dòng)模塊文件。
2、編譯測(cè)試APP
輸入如下命令編譯測(cè)試spinlockApp.c 這個(gè)測(cè)試程序:
arm
-linux
-gnueabihf
-gcc spinlockApp
.c
-o spinlockApp
編譯成功以后就會(huì)生成spinlockApp 這個(gè)應(yīng)用程序。
3、運(yùn)行測(cè)試
將上一小節(jié)編譯出來的spinlock.ko 和spinlockApp 這兩個(gè)文件拷貝到
rootfs/lib/modules/4.1.15 目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令加載spinlock.ko 驅(qū)動(dòng)模塊:
depmod
modprobe spinlock
.ko
驅(qū)動(dòng)加載成功以后就可以使用spinlockApp 軟件測(cè)試驅(qū)動(dòng)是否工作正常,測(cè)試方法和48.1.2小節(jié)中一樣,先輸入如下命令讓spinlockAPP 軟件模擬占用25S 的LED 燈:
./spinlockApp
/dev
/gpioled
1&
緊接著再輸入如下命令關(guān)閉LED 燈:
./spinlockApp
/dev
/gpioled
0
看一下能不能關(guān)閉LED 燈,驅(qū)動(dòng)正常工作的話并不會(huì)馬上關(guān)閉LED 燈,會(huì)提示你“file /dev/gpioled open failed!”,必須等待第一個(gè)spinlockApp 軟件運(yùn)行完成(25S 計(jì)時(shí)結(jié)束)才可以再次操作LED 燈。
如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:
rmmod spinlock
.ko
信號(hào)量實(shí)驗(yàn)
本節(jié)我們來使用信號(hào)量實(shí)現(xiàn)了一次只能有一個(gè)應(yīng)用程序訪問LED 燈,信號(hào)量可以導(dǎo)致休眠,因此信號(hào)量保護(hù)的臨界區(qū)沒有運(yùn)行時(shí)間限制,可以在驅(qū)動(dòng)的open 函數(shù)申請(qǐng)信號(hào)量,然后在release 函數(shù)中釋放信號(hào)量。但是信號(hào)量不能用在中斷中,本節(jié)實(shí)驗(yàn)我們不會(huì)在中斷中使用信號(hào)量。
實(shí)驗(yàn)程序編寫
1、修改設(shè)備樹文件
本章實(shí)驗(yàn)是在上一節(jié)實(shí)驗(yàn)的基礎(chǔ)上完成的,同樣不需要對(duì)設(shè)備樹做任何的修改。
2、LED 驅(qū)動(dòng)修改
本節(jié)實(shí)驗(yàn)在第上一節(jié)實(shí)驗(yàn)驅(qū)動(dòng)文件spinlock.c 的基礎(chǔ)上修改而來。新建名為“9_semaphore”的文件夾,然后在9_semaphore 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“semaphore”。將8_spinlock 實(shí)驗(yàn)中的spinlock.c 復(fù)制到9_semaphore 文件夾中,并且重命名為semaphore.c。將原來使用到自旋鎖的地方換為信號(hào)量即可,其他的內(nèi)容基本不變,完成以后的semaphore.c 文件內(nèi)容如下所示(有省略):
#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>
#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LEDOFF 0
#define LEDON 1
struct gpioled_dev{dev_t devid
; struct cdev cdev
; struct class *class
; struct device *device
; int major
; int minor
; struct device_node *nd
; int led_gpio
; struct semaphore sem
;
};struct gpioled_dev gpioled
;
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;
}
static ssize_t led_read(struct file *filp
, char __user
*buf
, size_t cnt
, loff_t *offt
)
{return 0;
}
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); } else if(ledstat
== LEDOFF
) {gpio_set_value(dev
->led_gpio
, 1); }return 0;
}
static int led_release(struct inode *inode
, struct file *filp
)
{struct gpioled_dev *dev
= filp
->private_data
;up(&dev
->sem
); return 0;
}
static struct file_operations gpioled_fops
= {.owner
= THIS_MODULE
,.open
= led_open
,.read
= led_read
,.write
= led_write
,.release
= led_release
,
};
static int __init
led_init(void)
{int ret
= 0;sema_init(&gpioled
.sem
, 1);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");}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
);ret
= gpio_direction_output(gpioled
.led_gpio
, 1);if(ret
< 0) {printk("can't set gpio!\r\n");}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
); gpioled
.cdev
.owner
= THIS_MODULE
;cdev_init(&gpioled
.cdev
, &gpioled_fops
);cdev_add(&gpioled
.cdev
, gpioled
.devid
, GPIOLED_CNT
);gpioled
.class
= class_create(THIS_MODULE
, GPIOLED_NAME
);if (IS_ERR(gpioled
.class
)) {return PTR_ERR(gpioled
.class
);}gpioled
.device
= device_create(gpioled
.class
, NULL, gpioled
.devid
, NULL, GPIOLED_NAME
);if (IS_ERR(gpioled
.device
)) {return PTR_ERR(gpioled
.device
);}return 0;
}
static void __exit
led_exit(void)
{cdev_del(&gpioled
.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 行,要使用信號(hào)量必須添加<linux/semaphore.h>頭文件。
第43 行,在設(shè)備結(jié)構(gòu)體中添加一個(gè)信號(hào)量成員變量sem。
第60~65 行,在open 函數(shù)中申請(qǐng)信號(hào)量,可以使用down 函數(shù),也可以使用down_interruptible函數(shù)。如果信號(hào)量值大于等于1 就表示可用,那么應(yīng)用程序就會(huì)開始使用LED 燈。如果信號(hào)量值為0 就表示應(yīng)用程序不能使用LED 燈,此時(shí)應(yīng)用程序就會(huì)進(jìn)入到休眠狀態(tài)。等到信號(hào)量值大于1 的時(shí)候應(yīng)用程序就會(huì)喚醒,申請(qǐng)信號(hào)量,獲取LED 燈使用權(quán)。
第123 行,在release 函數(shù)中調(diào)用up 函數(shù)釋放信號(hào)量,這樣其他因?yàn)闆]有得到信號(hào)量而進(jìn)入休眠狀態(tài)的應(yīng)用程序就會(huì)喚醒,獲取信號(hào)量。
第147 行,在驅(qū)動(dòng)入口函數(shù)中調(diào)用sema_init 函數(shù)初始化信號(hào)量sem 的值為1,相當(dāng)于sem是個(gè)二值信號(hào)量。
總結(jié)一下,當(dāng)信號(hào)量sem 為1 的時(shí)候表示LED 燈還沒有被使用,如果應(yīng)用程序A 要使用LED 燈,先調(diào)用open 函數(shù)打開/dev/gpioled,這個(gè)時(shí)候會(huì)獲取信號(hào)量sem,獲取成功以后sem 的值減1 變?yōu)?。如果此時(shí)應(yīng)用程序B 也要使用LED 燈,調(diào)用open 函數(shù)打開/dev/gpioled 就會(huì)因?yàn)樾盘?hào)量無效(值為0)而進(jìn)入休眠狀態(tài)。當(dāng)應(yīng)用程序A 運(yùn)行完畢,調(diào)用close 函數(shù)關(guān)閉/dev/gpioled
的時(shí)候就會(huì)釋放信號(hào)量sem,此時(shí)信號(hào)量sem 的值就會(huì)加1,變?yōu)?。信號(hào)量sem 再次有效,表示其他應(yīng)用程序可以使用LED 燈了,此時(shí)在休眠狀態(tài)的應(yīng)用程序B 就會(huì)獲取到信號(hào)量sem,獲取成功以后就開始使用LED 燈。
3、編寫測(cè)試APP
測(cè)試APP 使用48.1.1 小節(jié)中的atomicApp.c 即可,將7_atomic 中的atomicApp.c 文件到本例程中,并將atomicApp.c 重命名為semaApp.c 即可。
運(yùn)行測(cè)試(第二條命令因?yàn)楂@取信號(hào)量失敗而進(jìn)入休眠狀態(tài))
1、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為semaphore.o,Makefile 內(nèi)容如下所示:
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 行,設(shè)置obj-m 變量的值為semaphore.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:
make
-j32
編譯成功以后就會(huì)生成一個(gè)名為“semaphore.ko”的驅(qū)動(dòng)模塊文件。
2、編譯測(cè)試APP
輸入如下命令編譯測(cè)試semaApp.c 這個(gè)測(cè)試程序:
arm
-linux
-gnueabihf
-gcc semaApp
.c
-o semaApp
編譯成功以后就會(huì)生成semaApp 這個(gè)應(yīng)用程序。
3、運(yùn)行測(cè)試
將上一小節(jié)編譯出來的semaphore.ko 和semaApp 這兩個(gè)文件拷貝到
rootfs/lib/modules/4.1.15 目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令
加載semaphore.ko 驅(qū)動(dòng)模塊:
depmod
modprobe semaphore
.ko
驅(qū)動(dòng)加載成功以后就可以使用semaApp 軟件測(cè)試驅(qū)動(dòng)是否工作正常,測(cè)試方法和48.1.2 小
節(jié)中一樣,先輸入如下命令讓semaApp 軟件模擬占用25S 的LED 燈:
./ semaApp
/dev
/gpioled
1&
緊接著再輸入如下命令關(guān)閉LED 燈:
./ semaApp
/dev
/gpioled
0&
注意兩個(gè)命令都是運(yùn)行在后臺(tái),第一條命令先獲取到信號(hào)量,因此可以操作LED 燈,將LED 燈打開,并且占有25S。
第二條命令因?yàn)楂@取信號(hào)量失敗而進(jìn)入休眠狀態(tài),等待第一條命令運(yùn)行完畢并釋放信號(hào)量以后才擁有LED 燈使用權(quán),將LED 燈關(guān)閉,運(yùn)行結(jié)果如圖48.3.2.1 所示:
如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:
rmmod semaphore
.ko
互斥體實(shí)驗(yàn)(類似二值信號(hào)量,會(huì)休眠)
前面我們使用原子操作、自旋鎖和信號(hào)量實(shí)現(xiàn)了對(duì)LED 燈的互斥訪問,但是最適合互斥的就是互斥體mutex 了。本節(jié)我們來學(xué)習(xí)一下如何使用mutex 實(shí)現(xiàn)對(duì)LED 燈的互斥訪問。
實(shí)驗(yàn)程序編寫
1、修改設(shè)備樹文件
本章實(shí)驗(yàn)是在上一節(jié)實(shí)驗(yàn)的基礎(chǔ)上完成的,同樣不需要對(duì)設(shè)備樹做任何的修改。
2、LED 驅(qū)動(dòng)修改
本節(jié)實(shí)驗(yàn)在第上一節(jié)實(shí)驗(yàn)驅(qū)動(dòng)文件semaphore.c 的基礎(chǔ)上修改而來。新建名為“10_mutex”的文件夾,然后在10_mutex 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“mutex”。將9_semaphore實(shí)驗(yàn)中的semaphore.c 復(fù)制到10_mutex 文件夾中,并且重命名為mutex.c。將原來使用到信號(hào)量的地方換為mutex 即可,其他的內(nèi)容基本不變,完成以后的mutex.c 文件內(nèi)容如下所示(有省略):
#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>
#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LEDOFF 0
#define LEDON 1
struct gpioled_dev{dev_t devid
; struct cdev cdev
; struct class *class
; struct device *device
; int major
; int minor
; struct device_node *nd
; int led_gpio
; struct mutex lock
;
};struct gpioled_dev gpioled
;
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;
}
static ssize_t led_read(struct file *filp
, char __user
*buf
, size_t cnt
, loff_t *offt
)
{return 0;
}
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); } else if(ledstat
== LEDOFF
) {gpio_set_value(dev
->led_gpio
, 1); }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
,
};
static int __init
led_init(void)
{int ret
= 0;mutex_init(&gpioled
.lock
);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");}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
);ret
= gpio_direction_output(gpioled
.led_gpio
, 1);if(ret
< 0) {printk("can't set gpio!\r\n");}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
); gpioled
.cdev
.owner
= THIS_MODULE
;cdev_init(&gpioled
.cdev
, &gpioled_fops
);cdev_add(&gpioled
.cdev
, gpioled
.devid
, GPIOLED_CNT
);gpioled
.class
= class_create(THIS_MODULE
, GPIOLED_NAME
);if (IS_ERR(gpioled
.class
)) {return PTR_ERR(gpioled
.class
);}gpioled
.device
= device_create(gpioled
.class
, NULL, gpioled
.devid
, NULL, GPIOLED_NAME
);if (IS_ERR(gpioled
.device
)) {return PTR_ERR(gpioled
.device
);}return 0;
}
static void __exit
led_exit(void)
{cdev_del(&gpioled
.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 函數(shù)中調(diào)用mutex_lock_interruptible 或者mutex_lock 獲取mutex,成功的話就表示可以使用LED 燈,失敗的話就會(huì)進(jìn)入休眠狀態(tài),和信號(hào)量一樣。
第124 行,在release 函數(shù)中調(diào)用mutex_unlock 函數(shù)釋放mutex,這樣其他應(yīng)用程序就可以獲取mutex 了。
第148 行,在驅(qū)動(dòng)入口函數(shù)中調(diào)用mutex_init 初始化mutex。
互斥體和二值信號(hào)量類似,只不過互斥體是專門用于互斥訪問的。
3、編寫測(cè)試APP
測(cè)試APP 使用48.1.1 小節(jié)中的atomicApp.c 即可,將7_atomic 中的atomicApp.c 文件到本例程中,并將atomicApp.c 重命名為mutexApp.c 即可。
運(yùn)行測(cè)試
1、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為mutex.o,Makefile 內(nèi)容如下所示:
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 行,設(shè)置obj-m 變量的值為mutex.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:
make
-j32
編譯成功以后就會(huì)生成一個(gè)名為“mutex.ko”的驅(qū)動(dòng)模塊文件。
2、編譯測(cè)試APP
輸入如下命令編譯測(cè)試mutexApp.c 這個(gè)測(cè)試程序:
arm
-linux
-gnueabihf
-gcc mutexApp
.c
-o mutexApp
編譯成功以后就會(huì)生成mutexApp 這個(gè)應(yīng)用程序。
3、運(yùn)行測(cè)試
將上一小節(jié)編譯出來的mutex.ko 和mutexApp 這兩個(gè)文件拷貝到rootfs/lib/modules/4.1.15目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令加載mutex.ko 驅(qū)動(dòng)模塊:
depmod
modprobe mutex
.ko
驅(qū)動(dòng)加載成功以后就可以使用mutexApp 軟件測(cè)試驅(qū)動(dòng)是否工作正常,測(cè)試方法和48.3.2中測(cè)試信號(hào)量的方法一樣。
如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:
rmmod mutex
.ko
總結(jié)
以上是生活随笔為你收集整理的Linux并发与竞争实验(一次只允许一个应用程序操作LED灯)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。