互斥锁mutex
互斥体概述
信号量是在并行处理环境中对多个处理器访问某个公共资源进行保护的机制,mutex用于互斥操作。
mutex的语义相对于信号量要简单轻便一些,在锁争用激烈的测试场景下,mutex比信号量执行速度更快,可扩展性更好,另外mutex数据结构的定义比信号量小。
互斥锁使用注意事项:
1.同一时刻只有一个线程可以持有mutex。
2.只有锁持有者可以解锁,不能在一个进程中持有mutex,在另外一个进程中释放他。
3.不允许递归地加锁和解锁。
4.当进程持有mutex时,进程不可以退出。
5.mutex必须使用官方API来初始化。
6.mutex可以睡眠,所以不允许在中断处理程序或者中断下半部中使用,例如tasklet、定时器
定义在linux/mutex.h中
struct mutex {
atomic_t count;
spinlock_t wait_lock;
struct list_head wait_list;
...
};
// 定义互斥锁
mutex_init(struct mutex *lock);
DEFINE_MUTEX(name);
// 为指定的mutex加锁,如果不可用则睡眠
mutex_lock(struct mutex *lock);
// 为指定的mutex加锁,可中断
mutex_lock_interruptible(struct mutex *lock);
// 为指定的mutex加锁,可kill
mutex_lock_killable(struct mutex *lock);
// 为指定的mutex解锁
mutex_unlock(struct mutex *lock);
// 尝试获取指定的mutex,成功返回1,否则返回0并不会睡眠
mutex_trylock(struct mutex *lock);
// 判断锁是否被使用,如果被使用返回1,否则返回0
mutex_is_locked(struct mutex *lock);
示例代码:
驱动模块mmutex.c代码,为啥两个mm,因为一个m很多内容回合内核中的名字冲突,踩过坑了。
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define DEV_NAME "mutex_test"
#define DEV_NUM (1)
static struct my_st
{
int major;
int minor;
dev_t dev_num;
struct cdev cdev;
struct class *class;
struct device *device;
struct mutex lock;
}*my;
static int mmutex_open(struct inode *inode,struct file *filp)
{
mutex_lock(&my->lock);
printk("已上锁\r\n");
return 0;
}
static int mmutex_release(struct inode *inode,struct file *flip)
{
mutex_unlock(&my->lock);
printk("已解锁\r\n");
return 0;
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = mmutex_open,
.release = mmutex_release,
};
static int __init mmutex_init(void)
my = kzalloc(sizeof(struct my_st),GFP_KERNEL);
if(my == NULL)
return -ENOMEM;
int ret = alloc_chrdev_region(&my->dev_num,DEV_NUM,0,DEV_NAME);
if(ret < 0)
goto alloc_chrdev_failed;
my->major = MAJOR(my->dev_num);
my->minor = MINOR(my->dev_num);
cdev_init(&my->cdev,&fops);
my->cdev.owner = THIS_MODULE;
ret = cdev_add(&my->cdev,my->dev_num,DEV_NUM);
if(ret < 0)
goto cdev_add_failed;
my->class = class_create(THIS_MODULE,DEV_NAME);
if(IS_ERR(my->class))
{
ret = PTR_ERR(my->class);
goto class_create_failed;
}
my->device = device_create(my->class,NULL,my->dev_num,NULL,DEV_NAME);
if(IS_ERR(my->device))
{
ret = PTR_ERR(my->device);
goto device_create_failed;
}
printk("mutex device is ok \r\n");
printk("主设备号:%d\r\n",my->major);
mutex_init(&my->lock);
return 0;
device_create_failed:
class_destroy(my->class);
class_create_failed:
cdev_del(&my->cdev);
cdev_add_failed:
unregister_chrdev_region(my->dev_num,DEV_NUM);
alloc_chrdev_failed:
kfree(my);
return ret;
}
static void __exit mmutex_exit(void)
{
device_destroy(my->class,my->dev_num);
class_destroy(my->class);
cdev_del(&my->cdev);
unregister_chrdev_region(my->dev_num,DEV_NUM);
kfree(my);
printk("mutex device was removed\r\n");
}
module_init(mmutex_init);
module_exit(mmutex_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chen");
Makefile
ifneq ($(KERNELRELEASE),)
obj-m:=mmutex.o
else
CURRENT_DIR:=$(shell pwd)
KERNEL_DIR:=/lib/modules/$(shell uname -r)/build
ccflags-y:=-std=gnu99 -Wdeclaration-after-statement
all:
$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) modules
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) clean
endif
下面是测试的app,先让一个终端执行.app,再让另一个终端也执行这个app,会发现在第一个app进行close后第二个app才可以open才可以成功,因为当第一个app执行open后获得了mutex锁,并且未释放。第二个app进行open时会由于第一个app未释放锁而进入睡眠状态。当第一个进程释放锁后第二进程才可以获得锁。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("/dev/mutex_test",O_RDWR);
if(fd < 0 )
{
perror("");
exit(1);
}
printf("before sleep\r\n");
sleep(15);
printf("after sleep\r\n");
close(fd);
exit(0);
}