内核spinlock raw_spin_lock spin_lock_bh

文章详细介绍了自旋锁的使用场景,特别是在中断上下文不能被调度出去的情况下。自旋锁用于保护小的临界区,避免普通锁导致的进程切换开销。自旋锁的工作原理包括禁止内核抢占和关闭中断,确保持有锁的线程不会被调度出去。文章还提供了自旋锁的实现代码片段,并提到了使用自旋锁时需要注意临界区内不能睡眠。
摘要由CSDN通过智能技术生成

使用自旋锁的场景

临界区在中断上下文中不能被调度出去的场景。

先说明下中断上下文不能被调度出来的原因。

中断上下文中唯一能打断当前中断的是更高优先级的中断。进程是不能打断中断的。

在linux的中断模型中,中断的上半部是没有优先级的,所有来的中断将尽力处理。这时候对于local cpu来说一旦拥有cpu,就不能睡眠,否则就无法被唤醒。

这点对于softirq,tasklet也一样,因此这些bottom half也不能休眠),这样中断上下文中发生了调度,导致了休眠是无法再被唤醒的。

另个schedule()在切换进程时,保存当前的进程上下文(CPU寄存器的值、进程的状态以及堆栈中的内容),以便以后恢复此进程运行。中断发生后,内核会先保存当前被中断的进程上下文(在调用中断处理程序后恢复);但在中断处理程序里,CPU寄存器的值肯定已经变化了吧(最重要的程序计数器PC、堆栈SP等),如果此时因为睡眠或阻塞操作调用了schedule(),则保存的进程上下文就不是当前的进程context了.所以不可以在中断处理程序中调用schedule()。

如果中断处理时,禁止了内核抢占,如果休眠,会导致内核挂起。

导致中断上下文被调度出去的场景:schedule(),休眠,IO例程

临界区小,普通的锁会导致切换,性能损耗太大。

自旋锁的原理

所有自旋锁的变体都会禁止内核抢占。重点强调下:禁止了抢占并不意味着CPU就能由当前进程一直独占,中断仍可以获得CPU。所以变体的一个主要方向是添加参中断的禁止。

考虑简单的情形:内核线程A获取到了自旋锁,禁止抢占本地CPU,进入临界区执行时。这时对于单CPU,在禁止抢占的情况下,其他进程是无法通过调度获得CPU,只有通过中断的方式获得CPU。在单CPU的情况下,是不用关注进程调度的问题,主要考虑中断抢占CPU的情况。

内核线程B在执行spin_lock时,执行禁止抢占,不停地自旋,等待锁资源。

    • __raw_spin_lock

static inline void __raw_spin_lock(raw_spinlock_t *lock)  
{  
         preempt_disable();  // 禁止内核抢占
         spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);  
         LOCK_CONTENDED(lock, do_raw_spin_trylock, 
         do_raw_spin_lock);  
}
    • __raw_spin_lock_irq

static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
{
        local_irq_disable(); // 关闭中断
        preempt_disable(); // 禁止内核抢占 
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, 
        do_raw_spin_lock);
}
    • __raw_spin_lock_irqsave

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
    unsigned long flags;

    local_irq_save(flags); // 关闭中断,并保存中断的状态
    preempt_disable();    // 禁止内核抢占
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    /*
     * On lockdep we dont want the hand-coded irq-enable of
     * do_raw_spin_lock_flags() code, because lockdep assumes
     * that interrupts are not re-enabled during lock-acquire:
     */
#ifdef CONFIG_LOCKDEP
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
    do_raw_spin_lock_flags(lock, &flags);
#endif
    return flags;
}
    • arch_local_irq_save

static __always_inline unsigned long arch_local_irq_save(void)
{
    unsigned long flags = arch_local_save_flags(); // 保存中断状态
    arch_local_irq_disable(); // 关闭中断
    return flags;
}

自旋锁的使用方法及注意点

使用自旋锁,有两种方式定义一个锁:

动态的:

spinlock_t lock;
spin_lock_init (&lock);

静态的:

DEFINE_SPINLOCK(lock);

使用时的注意点

临界区内能不能睡眠

相关引用

https://www.cnblogs.com/ggzhangxiaochao/p/15874982.html

https://www.codenong.com/2559602/

https://zhuanlan.zhihu.com/p/373893804

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值