【精通内核】Linux内核自旋写锁

273 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情

前言

📫作者简介小明java问道之路,专注于研究 Java/ Liunx内核/ C++及汇编/计算机底层原理/源码,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。

📫热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长

🏆InfoQ签约作者、CSDN专家博主/后端领域优质创作者/内容合伙人、阿里云专家/签约博主、51CTO专家🏆

🔥如果此文还不错的话,还请👍关注 、点赞 、收藏三连支持👍一下博主~


本文导读

对于一些数据结构,例如阻塞队列来说本身队列就是非线程安全,所以我们就需要实现同步的需求(信号量),这时就需要自旋锁这种机制,同时自旋锁也是与 CPU 核数相关的,下面我们了解其内核源码实现。

一、Linux内核自旋锁写锁

获取写锁 void_raw_write_lock,lock_ptr中原子性对lock变量执行宏定义RW_LOCK_BIAS=0x01000000相减运算,如果不为0,则跳到标号2处,执行__write_lock_failed 代码。

获取写锁失败,回调_write_lock_failed,原子性加 locker 的值,通过cmpl 和jne指令看看locker的值是否为RWLOCKBIAS,如果不相等,则一直重试。

如果相等,则尝试原子性减操作,如果依然不为0,则继续重试。

宏定义释放读锁,直接原子性自增即可,宏定义释放写锁,直接原子性加RWLOCK_BIAS即可。

二、Linux内核自旋锁写锁源码分析

// 获取写锁
static inline void_raw_write_lock(rwlock_t*rw) {
    __build_write_lock(rw, "__write_lock_failed");
}

#define__build_write_lock(rw, helper)
do {
    __build_write_lock_ptr(rw, helper);
} while (0)

#define_build_write lock_ptr(rwhelper)
    //原子性对lock变量执行宏定义RW_LOCK_BIAS=0x01000000相减运算 
    asm volatile(LOCK"subl $"RWLOCKBIAS_STR",(%O)"
        //如果不为0,则跳到标号2处,执行__write_lock_failed 代码
        "jnz 2r"
        "1:"
        LOCK SECTION_START()
        "2: call " helper ""
        "jmp 1b"
        LOCK SECTION_END
        ::"a"(rw) : "memory")

// 获取写锁失败,回调_write_lock_failed 
asm(
    "write_lock_failed:"  // 原子性加locker 的值
    LOCK "addIS" $"RW_LOCK_BIAS_STR",(%eax)"
    "1: rep; nop"
    // 通过cmpl 和jne指令看看locker的值是否为RWLOCKBIAS,如果不相等,则一直重试
    "cmpl $" RW_LOCK_BIAS_STR",(%eax)"
    "jne 1b"
    // 如果相等,则尝试原子性减操作
    LOCK "subl$RWLOCK_BIAS_STR",(%eax)
    // 如果依然不为0,则继续重试
    "jnz__write_lock_failed"
    "ret"

// 宏定义释放读锁,直接原子性自增即可
#define _raw_read_unlock(rw) asm volatile("lock; incl %0":"=m" ((rw)->lock):: "memory")

// 宏定义释放写锁,直接原子性加RWLOCK_BIAS即可
#define_raw_write_unlock(rw) asm volatile("lock;addl $"R V_LOCK_BIAS_STR ",%O":"=m" ((rw)->lock)::"memory")

总结

不管是普通自旋锁还是读写自旋锁都是通过原子性命令来加减操作的;而对于读写锁而言,可通过将 lock 变量变为 0x01000000 值,然后后将第六位作为写锁标志位来使用。