赞
踩
信号量可以导致休眠,因此信号量保护的临界区没有运行时间限制,可以在驱动的 open 函数申请信号量,然后在release 函数中释放信号量。但是信号量不能用在中断中,本节实验我们不会在中断中使用信号量。
本实验例程路径: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 灯。
应用测试程序使用38.1.2中的应用测试程序即可。
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所示:
驱动模块加载成功后,使用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所示:
可以看到有两次运行结果。
卸载模块命令:
rmmod gpioled_semaphore
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。