linux spinlock之使用

linux spinlock的资料网上一大堆,但是每次查过之后,过段时间就忘记了,今天简单的总结下使用方法。

        自旋锁最多可以被一个可执行线程持有,如果一个执行线程试图获取一个已经被持有的自旋锁,那么该线程就会一直进行忙循环--旋转--等待锁重新可用。要是锁未被持有,请求锁的执行线程便能立刻得到它,继续执行。在任意时间,自旋锁可以防止多于一个的执行线程同时进入临界区,同一个锁可以用在多个位置。

        一个被争用的自旋锁使得请求它的线程在锁重新可用时自旋,这种行为是自旋锁的要点。所以自旋锁不应该被长时间持有。事实上,这点正是使用自旋锁的初衷:在短时间内进行轻量级加锁。

自旋锁方法:

        使用的接口定义在<linux/spinlock.h>,自旋锁的基本使用形式如下:

         DEFINE_SPINLOCK(mylock);

          spin_lock(&mylock);

           /* 临界区 */

         spin_unlock(&mylock);

        因为自旋锁在同一时刻至多被一个执行线程持有,所以一个时刻只能有一个线程位于临界区内,这就为多处理器提供了防止并发访问所需的机制。

        linux内核实现的自旋锁是不可以递归调用的,这点不同于自旋锁在其他操作系统中的实现。

        自旋锁可以使用在中断处理程序中(此处不能使用信号量,因为他们会导致睡眠)。在中断处理程序中使用自旋锁时,一定要在获取锁之前,禁止本地中断(在当前处理器上的中断请求),否则中断处理程序会打断正持有锁的内核代码,有可能会试图去争用这个已经被持有的自旋锁,这样一来,中断处理程序就会自旋,等待该锁重新可用,但是锁的持有者在这个中断处理程序执行完毕之前不可能运行,所以会导致死锁。注意:需要关闭的只是当前处理器上的中断,如果中断发生在不同的处理器上,即使中断处理程序在同一锁上自旋,也不会妨碍锁的持有者(在不同的处理器上)最终释放锁。

      内核提供的禁止中断同时请求锁的接口如下:

        DEFINE_SPINLOCK(mylock);

        unsigned long flags;

         spin_lock_irqsave(&mylock, flags);

        /* 临界区 */

         spin_lock_irqrestore(&mylock, flags);

       函数spin_lock_irqsave保存中断的当前状态,并禁止本地中断,然后去获取指定的锁。spin_lock_irqrestore对指定的锁解锁,然后让中断恢复到加锁前的状态,所以即使中断最初是被禁止的,代码也不会错误的去激活它们,相反,会继续让它们禁止。

        如果能确定中断在加锁前是激活的,那就不需要在解锁后恢复中断以前的状态了,可以无条件的在解锁时激活中断,这时使用spin_lock_irq和spin_unlock_irq会更好些。

使用自旋锁的方法:

1 定义自旋锁变量  spinlock_t testlock;

2 初始化锁   spin_lock_init(&testlock);

以上两步也可以通过DEFINE_SPINLOCK(testlock)来实现

3 获取锁      spin_lock/spin_lock_irq/spin_lock_irqsave

4 释放锁     spin_unlock/spin_unlock_irq/spin_unlock_restore

几个函数的区别,参考了下网络资料,以下资料是从别人的博客中拷贝来的,谢谢该博客主人的分享:

void spin_lock(spinlock_t *lock);

void spin_lock_irq(spinlock_t *lock);

void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);

 

1、spin_lock与spin_lock_irq区别

 

在Linux内核中何时使用spin_lock,何时使用spin_lock_irqsave很容易混淆。首先看一下代码是如何实现的。

spin_lock的调用关系

     spin_lock

            |

           + ----->  raw_spin_lock

|

+------>  _raw_spin_lock

                         |

                        +--------> __raw_spin_lock

[cpp] view plaincopy

  1. static inline void __raw_spin_lock(raw_spinlock_t *lock)  
  2. {  
  3.         preempt_disable();  
  4.         spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);  
  5.         LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);  
  6. }  

spin_lock_irq的调用关系

    spin_lock_irq

                |

               +-------> raw_spin_lock_irq

                                           |

                                          +---------> _raw_spin_lock_irq

                                                                      |

                                                                      +------------> __raw_spin_lock_irq

[cpp] view plaincopy

  1. static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)  
  2. {  
  3.         local_irq_disable();  
  4.         preempt_disable();  
  5.         spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);  
  6.         LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);  
  7. }  

可以看出来他们两者只有一个差别:是否调用local_irq_disable()函数, 即是否禁止本地中断。

在任何情况下使用spin_lock_irq都是安全的。因为它既禁止本地中断,又禁止内核抢占。

spin_lock比spin_lock_irq速度快,但是它并不是任何情况下都是安全的。

举个例子:进程A中调用了spin_lock(&lock)然后进入临界区,此时来了一个中断(interrupt),

该中断也运行在和进程A相同的CPU上,并且在该中断处理程序中恰巧也会spin_lock(&lock)

试图获取同一个锁。由于是在同一个CPU上被中断,进程A会被设置为TASK_INTERRUPT状态,

中断处理程序无法获得锁,会不停的忙等,由于进程A被设置为中断状态,schedule()进程调度就

无法再调度进程A运行,这样就导致了死锁!

但是如果该中断处理程序运行在不同的CPU上就不会触发死锁。 因为在不同的CPU上出现中断不会导致

进程A的状态被设为TASK_INTERRUPT,只是换出。当中断处理程序忙等被换出后,进程A还是有机会

获得CPU,执行并退出临界区。

所以在使用spin_lock时要明确知道该锁不会在中断处理程序中使用。

 

 

2、spin_lock_irq与spin_lock_irqsave区别

 

spin_lock_irqsave在进入临界区前,保存当前中断寄存器flag状态,关中断,进入临界区,在退出临界区时,把保存的中断状态写回到中断寄存器。

spin_lock_irq在进入临界区前不保存中断状态,关中断,进入临界区,在退出临界区时,开中断。

 

spin_lock_irqsave锁返回时,中断状态不会被改变,调用spin_lock_irqsave前是开中断返回就开中断。

spin_lock_irq锁返回时,永远都是开中断,即使spin_lock_irq前是关中断

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值