内核互斥相关(1)----基础知识

内核互斥相关(1)----基础知识

自旋锁

【本文关键词:自旋锁 互斥锁 信号量 原子操作】

来源

自旋锁是专为防止多核处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分。

何谓自旋锁

  • 外文名
    Spin lock
  • 概 念
    保护共享资源
  • 初 衷
    在短期间内进行轻量级的锁定

它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。
两者调度机制上略有不同。
1.对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。
2.自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。

原理

跟互斥锁一样,一个执行单元要想访问被自旋锁保护的共享资源,必须先得到锁,在访问完共享资源后,必须释放锁。如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁。

自旋锁.PNG

线程T1,T2  变量n
锁流程:开始--》获得锁--》执行程序--》释放锁--》结束
lock    0          1            1            0          0            1          0
T2              上锁          n+1          n+1      释放锁
T1                            自旋          自旋       自旋        拥有锁        n-1

自旋锁缺点:

由此我们可以看出,自旋锁是一种比较低级的保护数据结构或代码片段的原始方式,这种锁可能存在两个问题:

  • 死锁。试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。在递归程序中使用自旋锁应遵守下列策略:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂“自旋”,也无法获得资源,从而进入死循环。
    死锁举例
    例如,如果进程A锁住了记录1并等待记录2,而进程B锁住了记录2并等待记录1,这样两个进程就发生了死锁现象
    死锁
  • 过多占用cpu资源。如果不加限制,由于申请者一直在循环等待,因此自旋锁在锁定的时候,如果不成功,不会睡眠,会持续的尝试,单cpu的时候自旋锁会让其它process动不了. 因此,一般自旋锁实现会有一个参数限定最多持续尝试次数. 超出后, 自旋锁放弃当前time slice. 等下一次机会。

需要注意
1.锁使用者需要保持锁时间很短时,优先选择自旋锁,自旋锁的效率远高于互斥锁。
2.而自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用;信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用。
3.如果被保护的共享资源需要在中断上下文访问,就必须使用自旋锁;如果被保护的[共享资源]只在进程上下文访问,使用信号量保护该共享资源非常合适

对比      保持锁时间    保持期间    
自旋锁    短            抢占失效的 做不了其他事情,因为在自旋
信号量    长            可以被抢占的,处于休眠状态
。。。。。区别      
自旋锁    循环
互斥锁    进入睡眠

适用场景

#多处理机互斥算法(自旋锁算法)
do{
    b=1;
    while(b){
            lock(bus);
            b = test_and_set(&lock);
            unlock(bus);
    }
    临界区
    lock = 0;
    其余部分
}while(1)

自旋锁的基本形式如下:
spin_lock(&mr_lock);
//临界区
spin_unlock(&mr_lock);

相关概念

1.自旋锁是专为防止多[处理器]并发而引入的一种锁,它在[内核]中大量应用于[中断处理]等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,即在[标志寄存器]中关闭/打开中断标志位,不需要自旋锁)。
2.互斥锁在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

3.顺序锁,对读写锁的一种优化,使用顺序锁时,读不会被写执行单元阻塞(在读写锁中,写操作必须要等所有读操作完成才能进行)。也就是说,当向一个临界资源中写入的同时,也可以从此临界资源中读取,即实现同时读写,但是不允许同时写数据。如果读执行单元在读操作期间,写执行单元已经发生了写操作,那么,读执行单元必须重新开始,这样保证了数据的完整性,当然这种可能是微乎其微。顺序锁的性能是非常好的,同时他允许读写同时进行,大大的提高了并发性。
4.信号量,(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号
5.并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行
6.位操作,位操作是程序设计中对位模式按位或二进制数的一元和二元操作. 在许多古老的微处理器上, 位运算比加减运算略快, 通常位运算比乘除法运算要快很多. 在现代架构中, 情况并非如此:位运算的运算速度通常与加法运算相同(仍然快于乘法运算)。
7.原子操作,"原子操作(atomic operation)是不需要synchronized",这是多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch [1] (切换到另一个线程)。eg:当一个处理器读取一个字节时,其他处理器不能访问这个字节的内存地址(即处理器保证从系统内存当中读取或者写入一个字节是原子的)

8.等待队列,是指linux系统中进程所组成的队列,就是需要其他事件的发生才会自己本身被唤醒的进程,也就是说这些进程本身是在等待其他某些进程为他 们提供进程发生的条件。他们是属于消费者的,但是他们要消耗的东西还没有产生,这些就是处于等待状态的进程,组成了等待队列。等待队列很容易使用, 尽管它的设计很是微妙, 但不需要知道它的内部细节。 [1]

你可能感兴趣的:(内核互斥相关(1)----基础知识)