i.MX6ULL终结者Linux并发与竞争实验信号量实验

信号量可以导致休眠,因此信号量保护的临界区没有运行时间限制,可以在驱动的 open 函数申请信号量,然后在release 函数中释放信号量。但是信号量不能用在中断中,本节实验我们不会在中断中使用信号量。

1 编写驱动程序

本实验例程路径:i.MX6UL终结者光盘资料/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”这个目录,如果没有的话需要自行创建一下。开发板中使用的是光盘资料里面提供的busybox文件系统,光盘资料的“i.MX6UL终结者光盘资料\08_开发板系统镜像\03_文件系统镜像\01_Busybox文件系统”目录下)。输入下面命令加载模块:

depmod
modprobe gpioled_semaphore

驱动模块加载成功如图 3.1所示:
在这里插入图片描述

图 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所示:
在这里插入图片描述

图 3.2

可以看到有两次运行结果。
卸载模块命令:
rmmod gpioled_semaphore

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值