Linux自旋锁实例

本实例中使用自旋锁来实现对 LED 设备的互斥访问,即一次只允许一个应用程序使用 LED 灯,代码是在设备树下的字符设备驱动框架一文的基础上完成的
Linux驱动开发|Linux自旋锁实例_spinlock

在本例程中,通过定义一个变量 dev_stats 表示设备的使用情况,该变量为 0 时表示设备没有被使用, 大于 0 时表示设备被使用。驱动 open 函数中先判断 dev_stats 是否为 0,即判断设备是否可用,如果为 0 的话就使用设备,并且将 dev_stats 加 1,表示设备被使用了。使用完以后在 release 函数中将 dev_stats 减 1,表示设备没有被使用了。因此真正实现设备互斥访问的是变量 dev_stats,我们使用自旋锁对 dev_stats 来做保护

1. 程序编写

1.1 修改设备树文件

设备树文件修改与设备树下的字符设备驱动框架文中的修改方法一样,不需要做任何修改

1.2 LED驱动修改

修改驱动文件 gpioled.c 为 spinlock.c 并进行如下修改:

  • 在设备结构体中,添加自旋锁以及设备状态变量 dev_stats
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;			//所使用的gpio编号
	int dev_stats;			//设备状态,为0表示设备为使用
	spinlock_t lock;		//自旋锁
};

struct gpioled_dev gpioled;	//定义led设备
  • 打开设备时,判断 dev_stats 的值来检查LED有没有被占用
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;				//LED被使用,返回忙
	}
	gpioled.dev_stats++;		//标记设备以打开
	spin_unlock_irqrestore(&gpioled.lock, flags);	//解锁
	
	return 0;
}
  • 关闭设备时,将 dev_stats 减 1,表示设备没有被使用了
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 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");
	......
	......
}
1.3 编写测试APP

修改测试文件 ledApp.c 为 spinlockApp.c 并添加模拟占用LED的代码,使测试APP在获取LED驱动使用权后会持续一段时间,此时若有其他应用也去获取LED使用权的就会失败

  • 在 spinlockApp.c 中添加如下代码
while(1){
	sleep(5);
	cnt++;
	printf("App running times: %d\r\n",cnt);
	if(cnt >= 5)
		break;
}

2. 运行测试

2.1 编译驱动程序
  • 修改Makefile编译目标变量
obj-m := spinlock.o
  • 使用“make -j32”编译出驱动模块文件
make -j32
2.2 编译测试APP
  • 使用“arm-linux-gnueabihf-gcc”命令编译测试APP
arm-linux-gnueabihf-gcc spinlockApp.c -o spinlockApp
2.3 运行测试
  • 将驱动文件和APP可执行文件拷贝至“rootfs/lib/modules/4.1.15”中
  • 第一次加载驱动时,需使用“depmod”命令
depmod
  • 使用“modprobe”命令加载驱动
modprobe spinlock.ko
  • 使用“./spinlockApp /dev/gpioled 1"命令打开LED后,每隔5秒会输出一行App running times“”
./spinlockApp /dev/gpioled 1&   # & 表示在后台运行APP
  • 如果在LED被占用25秒期间,使用“./spinlockApp /dev/gpioled 0"命令关闭LED,会输出打开驱动失败
./spinlockApp /dev/gpioled 0
  • 使用”rmmod"命令卸载驱动
rmmod spinlock.ko

Linux驱动开发|Linux自旋锁实例_linux_02