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打开设备文件并获取互斥锁成功。所以互斥锁申请完之后记得要释放。