Linux驱动之互斥量

Linux内核中的互斥锁跟信号量一样,是内核中实现进程的同步与互斥的机制。不同的是信号量可以实现多个进程同时访问共享资源,但是互斥锁只允许一个进程访问共享资源。
互斥锁的相关函数

// 互斥锁结构体,省略宏定义相关的参数
struct mutex {
	/* 1: unlocked, 0: locked, negative: locked, possible waiters */
	atomic_t		count;  // 互斥锁计数
	spinlock_t		wait_lock; // 自旋锁
	struct list_head	wait_list;  // 互斥量等待队列
	......................
};

// 互斥锁初始化宏定义
# define mutex_init(mutex) \
do {							\
	static struct lock_class_key __key;		\
							\
	__mutex_init((mutex), #mutex, &__key);		\
} while (0)

void mutex_lock(struct mutex *lock);  // 获取互斥锁,如果获取不到就睡眠,不可被打断
int __must_check mutex_lock_interruptible(struct mutex *lock);// 获取互斥锁,如果获取不到就睡眠,可被打断
int mutex_trylock(struct mutex *lock); // 尝试获取互斥锁,获取成功返回1,否则返回0,不进入睡眠
void mutex_unlock(struct mutex *lock);  // 释放互斥锁

接下来编写驱动程序和应用程序来进行互斥锁的测试、
驱动程序

#define CHRDEV_MAJOR 240  // 主设备号
#define CHRDEV_MAION 0    // 次设备号
#define CHRDEV_COUNT 1    // 次设备号个数
#define CHRDEV_NAME  "testchrdev"

struct led_cdev
{
	struct cdev chrdevcdev;
	int major;
	dev_t dev;
	struct class *led_dev_class;
	struct mutex led_mutex;  // 定义互斥锁
};
static struct led_cdev leddev;



ssize_t chrdev_read (struct file *file, char __user *usr, size_t size, loff_t *loff)
{
	
	return 0;
}
int chrdev_open (struct inode *inode, struct file *file)
{
	if (!mutex_trylock(&leddev.led_mutex)) {  // 当应用程序打开文件时会尝试获取互斥锁
		return -EBUSY;                         // 当互斥锁已经获取完时,就返回错误码
	}
	file->private_data = &leddev;
	return 0;
}
int chrdev_release (struct inode *inode, struct file *file)
{
	struct led_cdev  *led_private_data = (struct led_cdev  *)file->private_data;
	mutex_unlock(&led_private_data->led_mutex);  // 释放互斥锁
	return 0;
}
struct file_operations fops = 
{
	.open    = chrdev_open,
	.read    = chrdev_read,
	.release = chrdev_release,
};


static int __init chrdev_init(void)
{
	int ret = 0,error = 0;
	struct device *devices;
	//DEBUG_SFLR("%s\r\n",__func__);
	error = alloc_chrdev_region(&leddev.dev,CHRDEV_MAION,CHRDEV_COUNT,CHRDEV_NAME); // 注册设备号
	printk("MAJOR = %d MINOR = %d\r\n",MAJOR(leddev.dev),MINOR(leddev.dev));
	if(error < 0){
		printk("alloc_chrdev_region error\r\n");
		ret =  -EBUSY;
		goto fail;
	}
	leddev.major = MAJOR(leddev.dev);
	cdev_init(&leddev.chrdevcdev, &fops); // 绑定字符设备操作函数集
	error = cdev_add(&leddev.chrdevcdev,leddev.dev,CHRDEV_COUNT);   // 添加字符设备
	if(error < 0){
		printk("cdev_add error\r\n");
		ret =  -EBUSY;
		goto fail1;
	}
	
	// 创建类,类名为testledclass
	leddev.led_dev_class = class_create(THIS_MODULE, "testledclass");
	if (IS_ERR(leddev.led_dev_class)){
		printk("class_create error\r\n");
		ret =  -EBUSY;
		goto fail2;
	}
		
	// 创建设备
	devices = device_create(leddev.led_dev_class, NULL, MKDEV(leddev.major,0), NULL, "testled");
	if(NULL == devices){
		printk("device_create error\r\n");
		ret =  -EBUSY;
		goto fail3;
	}
	
	
	mutex_init(&leddev.led_mutex);   // 初始化互斥锁
	
	return 0;
fail3:	
	class_destroy(leddev.led_dev_class);/*  删除类 */
	
fail2:	
	cdev_del(&leddev.chrdevcdev);/*  删除cdev */
fail1:
	unregister_chrdev_region(leddev.dev,CHRDEV_COUNT);
fail:
	return ret;
}


static void __exit chrdev_exit(void)
{
	//DEBUG_SFLR("%s\r\n",__func__);
	device_destroy(leddev.led_dev_class,MKDEV(leddev.major,0));/*  卸载设备 */
	class_destroy(leddev.led_dev_class);/*  删除类 */
	cdev_del(&leddev.chrdevcdev);/*  删除cdev */
	unregister_chrdev_region(leddev.dev,CHRDEV_COUNT);
}

module_init(chrdev_init);
module_exit(chrdev_exit);

MODULE_DESCRIPTION("xxxxxx");
MODULE_AUTHOR("xxxxxx");
MODULE_LICENSE("GPL");

在驱动程序中,第83行调用mutex_init进入互斥锁的初始化。在chrdev_open 函数中调用mutex_trylock尝试获取互斥锁,如果获取失败就返回错误代码给应用程序。在chrdev_release调用mutex_unlock释放互斥锁。

应用程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>

#define FILE_NAME "/dev/testled"

void *pthread_func1 (void *arg)
{
	
	int fd = -1;
	fd = open(FILE_NAME,O_RDONLY);
	if(fd < 0){
		printf("%s open %s error\r\n",__func__,FILE_NAME);
		pthread_exit(0);
	}
	printf("%s open %s success\r\n",__func__,FILE_NAME);
	sleep(5);
	
	close(fd);
	pthread_exit(0);
}

void *pthread_func2(void *arg)
{
	
	int fd = -1;
	
	while(1)
	{
		fd = open(FILE_NAME,O_RDONLY);
		if(fd < 0){
		printf("%s open %s error\r\n",__func__,FILE_NAME);
			
		}
		if(fd > 0)
		{
			break;
		}
		sleep(1);
	}
	printf("%s open %s success\r\n",__func__,FILE_NAME);
	close(fd);
	pthread_exit(0);
}
int main(void)
{
	pthread_t pth1,pth2;
	int err = 0;
	
	pthread_create(&pth1,NULL,pthread_func1,NULL);
	sleep(1);
	pthread_create(&pth2,NULL,pthread_func2,NULL);
	while(1);
	return 0;
}

在应用程序的main函数中先创建2条线程。pthread_func1会先运行并打开设备文件,然后等5秒后再关闭设备文件。pthread_func2后运行,pthread_func2会进入死循环并每隔一秒就尝试打开设备文件,当文件打开成功就退出循环并关闭文件。将驱动程序和应用程序编译完之后放到开发板中运行。
Linux驱动之互斥量
通过结果可以看到,首先pthread_func1先运行并打开文件成功,pthread_func2后运行打开了4次文件都失败,第5次打开成功。这时因为当pthread_func1打开设备文件时,互斥锁已经被申请了,此时互斥锁的计数值为0,当pthread_func2想打开设备文件时,已经没可用的互斥锁了,所以就导致设备文件打开失败。当pthread_func1关闭文件时,释放了互斥锁,此时pthread_func2打开设备文件并获取互斥锁成功。所以互斥锁申请完之后记得要释放。

上一篇:【CV Transformer 论文笔记】Intriguing Properties of Vision Transformers


下一篇:MySQL的安装、配置与升级(版本5.x至8.x)