前面我们使用原子操作、自旋锁和信号量实现了对 LED 灯的互斥访问,但是最适合互斥的就是互斥体 mutex 了。本节我们来学习一下如何使用 mutex 实现对 LED 灯的互斥访问。
1 编写驱动程序
本实验例程路径:i.MX6UL终结者光盘资料/06_Linux驱动例程/07_gpioled_mutex
创建gpioled_mutex.c,内容如下(有省略) :
1 #include <linux/types.h>
2 #include <linux/kernel.h>
......
23 /* gpioled 设备结构体 */
24 struct gpioled_dev{
25 dev_t devid; /* 设备号 */
26 struct cdev cdev; /* cdev */
27 struct class *class; /* 类 */
28 struct device *device; /* 设备 */
29 int major; /* 主设备号 */
30 int minor; /* 次设备号 */
31 struct device_node *nd; /* 设备节点 */
32 int led_gpio; /* led 所使用的 GPIO 编号*/
33 struct mutex lock; /* 互斥体 */
34 };
35
36 struct gpioled_dev gpioled; /* led 设备 */
37
38 /*
39 * @description : 打开设备
40 * @param – inode : 传递给驱动的 inode
41 * @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
42 * 一般在 open 的时候将 private_data 指向设备结构体。
43 * @return : 0 成功;其他 失败
44 */
45 static int led_open(struct inode *inode, struct file *filp)
46 {
47 filp->private_data = &gpioled; /* 设置私有数据 */
48
49 /* 获取互斥体,可以被信号打断 */
50 if (mutex_lock_interruptible(&gpioled.lock)) {
51 return -ERESTARTSYS;
52 }
53 #if 0
54 mutex_lock(&gpioled.lock); /* 不能被信号打断 */
55 #endif
56
57 return 0;
58 }
......
104 /*
105 * @description : 关闭/释放设备
106 * @param – filp : 要关闭的设备文件(文件描述符)
107 * @return : 0 成功;其他 失败
108 */
109 static int led_release(struct inode *inode, struct file *filp)
110 {
111 struct gpioled_dev *dev = filp->private_data;
112
113 /* 释放互斥锁 */
114 mutex_unlock(&dev->lock);
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 mutex_init(&gpioled.lock);
......
198 return 0;
199 }
......
214
215 module_init(led_init);
216 module_exit(led_exit);
217 MODULE_LICENSE("GPL");
218 MODULE_AUTHOR("topeet");
第 33 行,定义互斥体 lock。
第 45~58 行,在 open 函数中调用 mutex_lock_interruptible 或者 mutex_lock 获取 mutex,成功的话就表示可以使用 LED 灯,失败的话就会进入休眠状态,和信号量一样。
第 114 行,在 release 函数中调用 mutex_unlock 函数释放 mutex,这样其他应用程序就可以获取 mutex 了。
第 138 行,在驱动入口函数中调用 mutex_init 初始化 mutex。
互斥体和二值信号量类似,只不过互斥体是专门用于互斥访问的。
2 编写应用测试程序
应用测试程序使用38.1.2中的应用测试程序即可。
3 运行测试
1、编译驱动程序
添加Makefile文件,修改obj-m的值为 gpioled_mutex.o,内容如下:
KERNELDIR := /home/topeet/kernel
CURRENT_PATH := $(shell pwd)
obj-m := gpioled_mutex.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_mutex.ko驱动模块文件。
2、运行测试
将编译好的gpioled_mutex.ko驱动模块文件拷贝到/lib/modules/4.1.15目录下(检查开发板根文件系统中有没有“/lib/modules/4.1.15”这个目录,如果没有的话需要自行创建一下。开发板中使用的是光盘资料里面提供的busybox文件系统,光盘资料的“i.MX6UL终结者光盘资料\08_开发板系统镜像\03_文件系统镜像\01_Busybox文件系统”目录下)。输入下面命令加载模块:
depmod
modprobe gpioled_mutex
驱动模块加载成功如图 3.1所示:
驱动模块加载成功后,使用gpioled_atomic_test应用测试程序进行测试,测试流程和原子变量测试一样。使用下面的命令打开LED灯:
./gpioled_atomic_test /dev/gpioled 1 &
然后紧接着输入LED灯关闭命令:
./gpioled_atomic_test /dev/gpioled 0 &
运行结果如下:
可以看出和信号量的测试结果一样。
卸载模块命令:
rmmod gpioled_mutex