linux驱动程序中的并发控制(自旋锁)-44
Posted 杨斌并
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux驱动程序中的并发控制(自旋锁)-44相关的知识,希望对你有一定的参考价值。
自旋锁(spin lock)
简介
- 原子锁和自旋锁的使用范围
原子操作是一种很好的避免竞态的方式,使用非常简单。但在某些方面却显得过于简单。例如,有很多数据需要被格式化,被添加到某些数据结构中,然后被分析处理。而这些操作又都要求是原子的。这种情况使用控制原子操作的原则变量很难处理或根本无法处理。因此,处理更复杂的并发和竞态就要使用自旋锁。 - 自旋锁特点
自旋锁从本质上讲就是保证代码段(也称为临界区)的操作是原子的。也就是说,如果要保证某段代码在执行期间不会被打断(原子操作),就要在代码段执行之前申请自旋锁,然后开始执行代码段中的代码,在执行完原子操作的代码段后,再释放自旋锁。在自旋锁被占用期间,任何进程将无法申请到自旋锁。而这些正在申请自旋锁的进程会在-一个小循环里不断扫描自旋锁,直到自旋锁被释放(空闲状态)才会被申请到。这种机制之所以叫做自旋锁,也就是指未申请到自旋锁时会不断循环(自己在那循环)来等待自旋锁的释放。 - 自旋锁使用
其实自旋锁从使用方法上看和原子操作有些类似,也是通过一个自旋锁类型(spinlock_ t)的变量控制自旋锁的申请和释放。所以从理解的角度可以将自旋锁想象成一一个变量。 当申请到自旋锁时,会为这个变量设置一一个标记,意思是说“我已经申请到自旋锁了,你们都等一会”, 当释放自旋锁时,会为这个变量设置另外-一个空闲标记,意思是说“自旋锁我已经用完了,你们可以用了”。
使用
自旋锁的数据结构的定义
typedef struct spinlock {
union {
struct raw_spinlock rlock;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
struct {
u8 __padding[LOCK_PADSIZE];
struct lockdep_map dep_map;
};
#endif
};
} spinlock_t;
- 其本质上是一个整数值(对该数值的操作需要保证原子性),该数值表示spin lock是否可用。初始化的时候被设定为1。当thread想要持有锁的时候调用spin_lock函数,该函数将spin lock那个整数值减去1,然后进行判断,如果等于0,表示可以获取spin lock,如果是负数,则说明其他thread的持有该锁,本thread需要spin。
定义自旋锁的两种方式:
- 定义自旋锁相关的头文件
//里面封装了各种api
#include<linux/spinlock.h>
//结构体
#include<linux/spinlock_types.h>
- 动态的
spinlock_t lock;
spin_lock_init (&lock);
- 静态的
DEFINE_SPINLOCK(lock);
获取自旋锁
使用spin lock 函数可以获取自旋锁,代码如下:
spin_ lock(&lock) ;
- 如果使用spin_lock函数成功获取自旋锁,spin_lock函数会立即返回。如果未获取自旋锁,spinlock函数会被阻塞(在那自旋),直到可以获取自旋锁才返回。
如果想不管是否成功获取自旋锁都立刻返回,可以使用spin trylock 函数,代码如下:
if (spin_trylock(&lock)){
printk("spin trylock available \\n");
} else {
printk("spin trylock unavailable \\n");
}
- 如果获取到锁,则返回非0的值,如果没有则返回0
相关的api
函数 | 描述 |
---|---|
DEFINE_SPINLOCK(lock) | 定义和初始化自旋锁变量 |
void spin_lock_init(spinlock_t *lock) | 初始化自旋锁变量 |
void spin_lock(spinlock_t *lock) | 获取自旋锁。如果无法获取自旋锁,则不断自旋(循环) |
int spin_trylock(spinlock_t *lock) | 来检测自旋锁是否可用获取自旋锁。如果成功获取自旋锁,则立刻返回非0值,如果无法获取自旋锁,则立刻返回0 (并不进行自旋,也就是说该函数不会被阻塞) |
void spin_unlock(spinlock_t *lock) | 释放自旋锁 |
void spin_lock_irq(spinlock_t *lock) | 获取自旋锁,并禁止中断。相当于spin _lock + local _irq_disable |
int spin_trylock_irq(spinlock_t *lock) | 获取自旋锁,并禁止中断,果成功获取自旋锁,则立刻返回非0值,如果无法获取自旋锁,则立刻返回0,相当于spin_ trylock + local_irq_disable |
void spin_unlock_irq(spinlock_t *lock) | 释放自旋锁,并允许中断。相当于spin_unlock + local_irq_enable |
void spin_lock_bh(spinlock_t *lock) | 获取自旋锁,并关闭底半部。相当于spin_lock + local_bh_disable |
void spin_trylock_bh(spinlock_t *lock) | 获取自旋锁,并关闭底半部。如果成功获取自旋锁,立刻返回非0値,否則立刻返回0。相当于spin trylock + local_ bh_ disable |
void spin_unlock_bh(spinlock_t *lock) | 释放自旋锁,并打开底半部。相当于spin _unlock +local_bh_enable |
int spin_is_locked(spinlock_t *lock) | 如果自旋锁已被占用,返回非0值,否则返回0 |
自旋锁实际上是忙等锁,当锁被占用时,再尝试获取自旋锁时,CPU 会一直循环执行“测试自旋锁”动作,直到自旋锁被释放,并成功获取自旋锁为止。CPU在等待自旋锁时不做任何有用的工作,仅仅是等待。因此,只有在占用锁时间极短的情况下,使用自旋锁才是合理的。当临界区很大(要执行的代码很多)或有共享设备时,需要较长时间占用锁,使用自旋锁就会降低系统性能。
不恰当地使用自旋锁可能导致系统死锁。引发这个问题最常见的情况是递归使用一个自旋锁,即如果一个已经拥有某个自旋锁的CPU想第二次获取这个自旋锁(也就是说,调用spinlock函数获取自旋锁后,在未释放自旋锁之前又调用了spin_lock 函数再次获取自旋锁),则该CPU将死锁。此外,如果进程获取自旋锁之后被阻塞,也有可能导致死锁的发生。copy_from_user,copy_to_user和kmalloc等函数都有可能引起阻塞,因此在自旋锁的占用期间最好不要调用这些函数。
代码
- spin_lock_test.c
#include<linux/module.h>
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/uaccess.h>
#include<linux/fs.h>
#include<linux/spinlock.h>
#include<linux/spinlock_types.h>
#include<linux/miscdevice.h>
#include<linux/delay.h>
#define DEVICE_NAME "spin_lock"
static char *data = "read\\n";
static int flag = 1;
//定义一个自旋锁
static DEFINE_SPINLOCK(lock);
static int spin_lock_open(struct inode *node,struct file *file){
printk("spin_lock_open \\n");
return 0;
}
static int spin_lock_release(struct inode *node,struct file *file){
printk("spin_lock_release \\n");
return 0;
}
static int spin_lock_read(struct file * file, char __user * buf, size_t size, loff_t * ppos){
int data_size = strlen(data);
if (copy_to_user(buf,(void *)data, data_size))
{
printk("copy_to_user is error\\n");
return -EINVAL;
}
if (flag)
{
flag = 0;
if (spin_trylock(&lock))
{
mdelay(20000);
spin_unlock(&lock);
}else{
return -EBUSY;
}
return size;
}else{
flag = 1;
return 0;
}
}
static int spin_lock_write(struct file *file, const char __user *buf, size_t size, loff_t * ppos){
char write_data[10];
memset(write_data,0,10);
if (copy_from_user(write_data, buf, size))
{
printk("copy_from_user is error\\n");
return -EINVAL;
}
if (strcmp("lock\\n", write_data) == 0)
{
printk("write_data is lock\\n");
spin_lock(&lock);
printk("spin lock available \\n");
spin_unlock(&lock);
}else if (strcmp("trylock\\n", write_data) == 0)
{
printk("write_data is trylock\\n");
if (spin_trylock(&lock))
{
printk("spin trylock available \\n");
spin_unlock(&lock);
}else {
printk("spin trylock unavailable \\n");
return -EBUSY;
}
}
return size;
}
static struct file_operations dev_fops={
.owner = THIS_MODULE,
.open = spin_lock_open,
.release = spin_lock_release,
.read = spin_lock_read,
.write = spin_lock_write
};
static struct miscdevice misc={
.minor=MISC_DYNAMIC_MINOR,
.name=DEVICE_NAME,
.fops=&dev_fops
};
static int demo_init(void){
int ret=misc_register(&misc);
if(ret < 0 ){
printk("atomic_init is error\\n");
return -1;
}
printk("demo_init_success\\n");
return ret;
}
static void demo_exit(void){
printk("ademo_exit_success\\n");
misc_deregister(&misc);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("binbing.Yang");
下面的操作都在20秒内
- 使用一个终端 在设备下执行
cat /dev/spin_lock
- 使用另外的两个或者多个终端分别执行如下
echo lock > /dev/spin_lock
echo trylock > /dev/spin_lock
- 当cat /dev/spin_lock时 会收到如下日志
设备打开
- echo trylock > /dev/spin_lock 和 echo trylock > /dev/spin_lock 后 会收到一下日志
spin_trylock 不会阻塞下面的代码
- 20 秒后会收到也就是 当cat /dev/spin_lock 读取到这个节点输入read 时,收到一下日志
spin_lock 会阻塞下面的代码
以上是关于linux驱动程序中的并发控制(自旋锁)-44的主要内容,如果未能解决你的问题,请参考以下文章