跳转至

并发问题

现代CPU大都支持对称多处理(symmetric multiprocessing,SMP),在开发内核的时候不得不考虑并发因素。

信号量

信号量(Semaphore)是计算机科学一个通用的概念,它是一个整数值,并和一对函数PV一起使用。希望进入临界值的进程调用P,如果值大于1,则减小1并执行进程;如果值为0或更小,则必须等待他人释放。释放调用V,他会增加信号量的值,并必要时唤醒等待的进程。

如果信号量的值一开始为1,也叫做互斥锁(Mutex)。

Linux中的相关代码在<asm/semaphore.h>

自旋锁

自旋锁和Mutex的区别在于,前者如果获取不到锁会忙等待,后者则会让出CPU。无论Mutex还是自旋锁。其​​最底层的锁状态竞争​​都是通过原子指令实现的。以atomic_test_and_set这个原子指令为例,给出示例代码如下:

void spin_lock(spinlock_t *lock) {
    while (atomic_test_and_set(&lock->flag) == LOCKED) { // CAS循环
        cpu_relax(); // 提示CPU降低功耗(如x86的PAUSE指令)
    }
}
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/wait.h>

// 互斥锁结构体
struct kernel_mutex {
    atomic_t lock;          // 锁状态:0=空闲, 1=锁定
    wait_queue_head_t wait; // 等待队列
    struct task_struct *owner; // 当前持有者(用于调试/死锁检测)
};

// 初始化互斥锁
void mutex_init(struct kernel_mutex *m)
{
    atomic_set(&m->lock, 0); // 初始状态为空闲
    init_waitqueue_head(&m->wait); // 初始化等待队列
    m->owner = NULL;
}

// 获取锁
void mutex_lock(struct kernel_mutex *m)
{
    // 尝试快速获取锁
    if (atomic_test_and_set(&m->lock, 0, 1) == 0) {
        m->owner = current; // 记录当前持有者
        return;
    }

    // 慢路径:锁已被占用
    DEFINE_WAIT(wait); // 定义等待项

    while (true) {
        prepare_to_wait(&m->wait, &wait, TASK_UNINTERRUPTIBLE);

        // 再次尝试获取锁(避免唤醒后竞争)
        if (atomic_test_and_set(&m->lock, 0, 1) == 0) {
            break;
        }

        // 让出CPU - 核心差异点!
        schedule();
    }

    finish_wait(&m->wait, &wait);
    m->owner = current; // 设置新持有者
}

// 释放锁
void mutex_unlock(struct kernel_mutex *m)
{
    // 清除锁状态
    atomic_set(&m->lock, 0);
    m->owner = NULL;

    // 唤醒一个等待者
    wake_up(&m->wait);
}

读写锁

Completion

免锁

原子变量

无锁数据结构