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