一、什么是自旋锁
自旋锁是一种用来保护共享资源的锁机制。它的特点是:当线程要获取锁时,如果锁正被其他线程占用,该线程不会停下来等待,而是持续用 CPU 反复尝试获取锁。这种不停"转圈等待"的方式,就是"自旋"。
使用场景
在什么情况下用自旋锁呢?比如当共享资源的锁定时间非常短(比如几微秒),这时让线程暂停再恢复的开销反而更大。如果有多个 CPU 核心,等待的线程可以继续占用 CPU 自旋,同时其他核心还能处理其他任务,这样反而更高效。
举个例子:银行 ATM 防护舱同一时间只能一人使用。当有人进去后,防护舱锁上,其他人只能在外面等待。但自旋锁不同,等待的人不会真的排队,而是不停看门是否开了——如果舱内的人两秒内出来开门,等待的人立刻就能进去,省去了排队的时间。
在程序中使用自旋锁时,每个任务获取锁后,其他想要获取锁的任务只能不断循环等待。虽然这种方式会一直占用 CPU,但只要临界区(需要锁保护的代码段)足够短,总效率反而更高。
注意
如果自旋锁用得不对(比如锁住时间太长或多个线程互相等待),可能导致死锁——这种情况会在后面详细讲解。
内核中以 spinlock_t 结构体来表示自旋锁,定义在 内核源码/include/linux/spinlock_types.h 文件中,如下所示:
c
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;1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
二、基础 API 介绍
自旋锁相关 API 函数定义在 内核源码/include/linux/spinlock.h 文件中。
| 函数 | 描述 |
|---|---|
DEFINE_SPINLOCK(spinlock_t lock) | 定义并初始化自旋锁 |
int spin_lock_init(spinlock_t *lock) | 初始化自旋锁 |
void spin_lock(spinlock_t *lock) | 获取指定的自旋锁,也叫做加锁 |
void spin_unlock(spinlock_t *lock) | 释放指定的自旋锁 |
int spin_trylock(spinlock_t *lock) | 尝试获取指定的自旋锁,如果没有获取到就返回 0 |
int spin_is_locked(spinlock_t *lock) | 检查指定的自旋锁是否被获取,如果没有被获取就返回非 0,否则返回 0 |
三、加锁的其他 API
spin_lock—— 不安全的 API(会发生死锁,不建议使用)spin_lock_irq—— 全局中断的关闭(会发生死锁)(禁止硬中断)spin_lock_irqsave—— 记录中断的关闭状态(为线程安全的方案)推荐
四、内核使用案例
自旋锁的使用步骤
- 在访问临界资源的时候先申请自旋锁
- 获取到自旋锁之后就进入临界区,获取不到自旋锁就"原地等待"
- 退出临界区的时候要释放自旋锁


五、实现
忙等待(while(1))—— 不会让出 CPU 的资源。
c
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned long tmp;
u32 newval;
arch_spinlock_t lockval;
prefetchw(&lock->slock);
__asm__ __volatile__(
"1: ldrex %0, [%3]\n"
" add %1, %0, %4\n"
" strex %2, %1, [%3]\n"
" teq %2, #0\n"
" bne 1b"
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
: "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
: "cc");
while (lockval.tickets.next != lockval.tickets.owner) {
wfe();
lockval.tickets.owner = READ_ONCE(lock->tickets.owner);
}
smp_mb();
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
六、实验
源代码下载
bash
git clone git@gitee.com:yangxuesong314/linux-driver.git1
- 若之前已 git 拉取代码可以忽略
- 代码位于:
linux-driver/03.内核互斥锁技术/02.经典自旋锁的使用
驱动代码:
c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
static spinlock_t spinlock_test;
static int flag = 1; // flag = 1 表示设备没有被打开,等于 0 则证明设备已经被打开
static int open_test(struct inode *inode, struct file *file)
{
spin_lock(&spinlock_test); // 自旋锁加锁
if (flag != 1) {
spin_unlock(&spinlock_test);
return -EBUSY;
}
flag = 0;
spin_unlock(&spinlock_test); // 自旋锁解锁
return 0;
}
static ssize_t read_test(struct file *file, char __user *ubuf, size_t len, loff_t *off)
{
int ret;
char kbuf[10] = "linux";
printk("\nthis is read_test \n");
ret = copy_to_user(ubuf, kbuf, strlen(kbuf));
if (ret != 0) {
printk("copy_to_user is error \n");
}
printk("copy_to_user is ok \n");
return 0;
}
static char kbuf[10] = {0};
static ssize_t write_test(struct file *file, const char __user *ubuf, size_t len, loff_t *off)
{
int ret;
ret = copy_from_user(kbuf, ubuf, len);
if (ret != 0) {
printk("copy_from_user is error\n");
}
if (strcmp(kbuf, "linux") == 0) {
ssleep(4);
} else if (strcmp(kbuf, "kernel") == 0) {
ssleep(2);
}
printk("copy_from_user buf is %s \n",kbuf);
return 0;
}
static int release_test(struct inode *inode, struct file *file)
{
printk("\nthis is release_test \n");
spin_lock(&spinlock_test);
flag = 1;
spin_unlock(&spinlock_test);
return 0;
}
struct chrdev_test {
dev_t dev_num;
int major, minor;
struct cdev cdev_test;
struct class *class_test;
};
struct chrdev_test dev1;
struct file_operations fops_test = {
.owner = THIS_MODULE,
.open = open_test,
.read = read_test,
.write = write_test,
.release = release_test,
};
static int __init atomic_init(void)
{
spin_lock_init(&spinlock_test);
if (alloc_chrdev_region(&dev1.dev_num, 0, 1, "chrdev_name") < 0) {
printk("alloc_chrdev_region is error \n");
}
printk("alloc_chrdev_region is ok \n");
dev1.major = MAJOR(dev1.dev_num);
dev1.minor = MINOR(dev1.dev_num);
printk("major is %d,minor is %d\n",dev1.major,dev1.minor);
cdev_init(&dev1.cdev_test, &fops_test);
dev1.cdev_test.owner = THIS_MODULE;
cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
dev1.class_test = class_create(THIS_MODULE, "class_test");
device_create(dev1.class_test, 0, dev1.dev_num, 0, "device_test");
return 0;
}
static void __exit atomic_exit(void)
{
device_destroy(dev1.class_test, dev1.dev_num);
class_destroy(dev1.class_test);
cdev_del(&dev1.cdev_test);
unregister_chrdev_region(dev1.dev_num, 1);
printk("module exit \n");
}
module_init(atomic_init);
module_exit(atomic_exit);
MODULE_LICENSE("GPL");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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
驱动加载 insmod spinlock.ko:

应用测试代码:
c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char str1[10] = {0};
fd = open(argv[1], O_RDWR);
if (fd < 0) {
printf("file open failed \n");
return -1;
}
if (strcmp(argv[2], "Linux") == 0) {
write(fd, "Linux", 10);
} else if (strcmp(argv[2], "kernel") == 0) {
write(fd, "kernel", 10);
}
close(fd);
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24