跟着简叔学的,可以B站搜索 简说linux
与之前学的信号量相比,自旋锁是一种死等的机制。而信号量不会。只有一个执行单元获取锁并进入到临界区,其他的都给我死等。可以在中断上下文执行,因为是不睡眠的。中断上下文代码不允许睡眠,也不允许调用那些可能会引起睡眠的函数。这种死等的实现是不同的架构有不一样的方法。
自旋锁的实现
查看结构体定义struct spinlock (/include/linux/spinlock_types.h)
然后再去看看,raw_spinlock
然后再去看看arch_spinlock_t!!!!然后发现这东西的定义适合cpu架构相关的,多层封装是为了增加灵活性。。以arm为例:
路径(/arch/arm/include/asm/spinlock_types.h)
其中owner表示持有这个数字的thread可以获取自旋锁,next表示如果后续有thread请求获取这个自旋锁,给他分配这个数字。
自旋锁的初始化 按照上述所说就是把那两个东西置为0,对spinlock的初始化一层层再找到arm里的初始化实现,就长这样
基本所有实现都是spin—>raw_spin->arch_spin. 由不同的架构去实现,实现的逻辑就是。
- 刚开始owner=next=0;
- 第一个thread获取spinlock,可获取成功,此时owner==0,next=0;
- 第二个thread获取spinlock,如果第一个thread还没有释放spinlock,则next++, next变为1;
- 第三个thread获取spinlock,如果第一个thread还没有释放spinlock,则next++, next变为2;
此时第一个thread释放spinlock,则执行ownerowner=1; - 虽然此时第二个thread和第三个thread都在等待spinlock,但是因为第二个thread的next=owner,所以第二个thread可以获取到spinlock,第三个thread则继续等待。这样保证了spinlock的唤醒机制是先到先唤醒,后到后唤醒,保证了公平性。
另外,还有一种自旋锁叫做读写自旋锁。读写自旋锁可以让多个读一起读,但是经常让写死等。
自旋锁的使用
继续修改我们的hello驱动。
首先定义自旋锁与临界资源。
1
2spinlock_t count_lock;
int open_count=0;然后在hello_init中进行初始化
1
2//自旋锁初始化
spin_lock_init(&count_lock);open与close操作中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28int hello_open(struct inode *p, struct file *f)
{
spin_lock(&count_lock);
if(!open_count>=1)
{
spin_enlock(&count_lock);
printk(KERN_EMERG"device is busy,helloopen fall!!\r\n");
return -EBUSY;
}
open_count++;
spin_lock(&count_lock);
printk(KERN_EMERG"hello_open ok~~~\r\n");
return 0;
}
int hello_close(struct inode *inode ,struct file *filp)
{
if(!open_count!=1)
{
printk(KERN_EMERG"device is busy,helloopen fall!!\r\n");
return -EBUSY;
}
spin_enlock(&count_lock);
printk(KERN_INFO"hello_close ok");
return 0;
}之前也说过了自旋锁的特性,尽量要让临界区执行的快一点。不然cpu会经常死等。
结果和信号量结果相同。因为从应用程序来看确实本质区别也不大。