linux内核 自旋锁+中断 spin_lock_bh/spin_unlock_bh

spin_lock_bh通常用在进程中,用来禁止抢断和禁止软中断。

spin_lock_bh()中首先会调用local_bh_disable()禁止当前CPU的软件中断。而函数spin_unlock_bh()则调用local_bh_enable()来势能本地CPU的软件中断。在软件中断被禁止的时候,本地CPU的所有软中断都不会被执行。

如果一个softirq 与 用户上下文共享数据,就有两个问题:首先,当前的用户上下文可能被softirq中断;其次,临界区可能会在别的CPU进入。这时spin_lock_bh()(include/linux/spinlock.h)就有了用武之地。它会在那个CPU上禁止softirqs,然后获
取锁。spin_unlock_bh()做相反的工作。(由于历史原因,后缀‘bh’成为对各种下半部的通称,后者是software interrupts的旧称。其实spin_lock_bh本应叫作spin_lock_softirq才贴切),注意这里你也可以用spin_lock_irq()或者spin_lock_irqsave(),这样不单会禁止softirqs,还会禁止硬件中断。

tasklets 和timer其实是一种softirq,从加锁观点来看,tasklets和timers的地位是等同的。

硬中断通常与一个tasklet或softirq通信。这通常涉及到把一个任务放到某个队列中,再由softirq取出来。如果一个硬件中断服务例程与一个softirq共享数据,就有两点需要考虑。第一,softirq的执行过程可能会被硬件中断打断;第二,临界区可能会被另一个CPU上的硬件中断进入。这正是spin_lock_irq()派上用场的地方。它在那个CPU上禁止中断,然后获取锁。spin_unlock_irq()做相反的工作。
硬件中断服务例程中不需要使用spin_lock_irq(),因为当它在执行的时候softirq是不可能执行的:它可以使用spin_lock(),这个会更快一些。唯一的例外是另一个硬件中断服务例程使用了相同的锁:spin_lock_irq()会禁止那个硬件中断。

自旋锁使用注意事项:

  • 因为在等待自旋锁的时候处于 自旋 状态,因此锁的持有时间不能太长,一定要短,否则的话会降低系统性能。如果临界区较大,运行时间较长的话可以使用其它的并发处理方式,比如信号量和互斥体

  • 自旋锁保护的临界区不能调用任何可能导致线程休眠的 API 函数,否则的话可能导致死锁

  • 不能递归申请自旋锁,因为一旦通过递归的方式申请一个你正在持有的锁,那么你就必须自旋,等待锁被释放,然而你正处于自旋状态,根本没法释放锁。结果就是自己把自己锁死了!

  • 在编写驱动程序的时候我们必须考虑到驱动的可移植性,因此不管你用的是单核的还是多核的 SOC,都将其当做多核 SOC 来编写驱动程序。

猜你喜欢

转载自blog.csdn.net/wangquan1992/article/details/108960171