1 概述
spin_lock 主要用于内核开发,用于在内核模块或内核代码中对共享资源进行互斥访问。它通常用于非抢占式内核环境,因为在抢占式内核中,内核线程可以在任何时间点被中断,这可能导致自旋锁在某些情况下陷入死锁。
1.1 spin_lock特点
- spin_lock 是一种忙等待锁,它会在获取锁之前一直自旋等待。这意味着线程不会被挂起,而是会持续占用 CPU 资源,直到锁可用。
- 自旋锁适用于临界区代码非常短暂且锁争用较少的情况,因为它不会在等待期间让出 CPU,可能会浪费大量的 CPU 时间。
- 原子性:spin_lock 操作是原子的,它使用底层处理器原语(例如 x86 架构上的 xchg 指令或者 ARM 架构上的原子比较和交换指令)来确保锁的原子性。
1.2 spin_lock_irq
spin_lock_irq 会禁用本地中断(IRQs),这意味着中断服务程序(ISRs)无法在 spin_lock_irq 保护的临界区内执行,从而避免了竞争条件。
1.3 中断服务程序与普通线程同步的方式
- 使用信号量:可以使用信号量作为中断服务程序和普通线程之间的同步机制。中断服务程序可以在访问共享资源之前获取信号量,而普通线程则在完成对共享资源的访问后释放信号量。
- 使用自旋锁:自旋锁是一种用于互斥访问的锁定机制,可以在中断服务程序和普通线程之间使用。中断服务程序和线程都可以尝试获取自旋锁,但如果自旋锁已被持有,它们会自旋等待直到锁可用。
2 内核中使用实例
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spinlock.h>
MODULE_LICENSE("GPL");
// 声明并初始化一个自旋锁
static DEFINE_SPINLOCK(my_spinlock);
// 共享资源
static int shared_data = 0;
static int my_init(void) {
int i;
printk(KERN_INFO "Initializing my module...\n");
// 模拟多个线程同时访问共享资源
for (i = 0; i < 5; i++) {
if (fork() == 0) {
// 子进程获取自旋锁
spin_lock(&my_spinlock);
// 访问共享资源
shared_data++;
printk(KERN_INFO "Child process: shared_data = %d\n", shared_data);
// 释放自旋锁
spin_unlock(&my_spinlock);
// 退出子进程
_exit(0);
}
}
// 等待所有子进程退出
for (i = 0; i < 5; i++) {
wait(NULL);
}
printk(KERN_INFO "All child processes have exited. Final shared_data = %d\n", shared_data);
return 0;
}
static void my_exit(void) {
printk(KERN_INFO "Exiting my module...\n");
}
module_init(my_init);
module_exit(my_exit);