从零写OS(三十九):自旋锁 —— 多核下的第一道防线
ch38 把两颗 CPU 都启动了,但这带来了一个新问题:两颗核心同时访问同一份数据会怎样? 考虑这个场景:CPU0 和 CPU1 同时调用 pmm_alloc() 申请物理页,都扫描到了同一个空闲位,都标记为"已用",于是把同一个物理页分配给了两个不同的进程。这两个进程会互相覆盖对方的内存,然后崩溃。 这章实现自旋锁(spinlock),让同一时间只有一颗核心能进入临界区。 为什么普通变量不能做锁 直觉上,可以用一个整数变量做锁: int lock = 0; if (lock == 0) { // CPU0 读到 0 lock = 1; // CPU1 也读到 0,同时进入! // 临界区 } 问题在于"读-判断-写"不是原子操作。两颗核心在读和写之间有一个竞争窗口,都能同时通过检查。 xchg:原子交换 x86 提供了 xchg 指令,它原子地交换寄存器和内存的值——读和写在硬件层面是不可分割的: static inline void spin_lock(spinlock_t *lock) { uint32_t val = 1; __asm__ volatile ( "1: xchgl %0, %1\n" // 原子:把 1 写入 lock,把旧值读到 val " testl %0, %0\n" // 旧值为 0? " jz 2f\n" // 是 → 获锁成功 " pause\n" // 否 → 稍等,再试 " jmp 1b\n" "2:\n" : "+r"(val), "+m"(lock->locked) :: "memory" ); } xchg 隐含 lock 前缀,直接是总线级原子操作。如果拿到的旧值是 0,说明锁之前是空闲的,现在已经被我们锁上了。如果拿到的旧值是 1,说明别人持有锁,自旋等待。 ...