自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的 并发可简单采用关闭中断的方式,不需要自旋锁)。
自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直 进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任 何时刻防止多于一个的内核任务 同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。
事实上,自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进 行自旋(特别浪费处理器时间), 所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。
自旋锁一般这样被使用,如下所示:
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.6版本的非抢占模式下,spin_lock被编译为一条空语句,什么都不干;
在单处理器2.6版本的抢占模式下,spin_lock可以理解为一个抢占disable的开关,并在spin_unlock中被enable;(注意不是关中断,是关任务抢占),并且不自旋;
在SMP的2.6的抢占模式下,spin_lock才真正发挥了他的全部功能:防止SMP中并发访问临界区,防止内核抢占造成的竞争;自旋;并在spin_unlock中退出临界区。
因此,因为不自旋,所以死锁只发生在SMP情况下,单核不会发生死锁。
读写锁
读写自旋锁(rwlock)可允许读的并发,只能最多有一个写进程,读和写也不能同时进行。
读写自旋锁一般这样被使用,如下所示:
rwlock_t lock; //定义rwlock
rwlock_init(&lock); //初始化rwlock
//读时获取锁
read_lock(&lock);
... //临界资源
read_unlock(&lock);
//写时获取锁
write_lock_irqsave(&lock, flags);
... //临界资源
write_unlock_irqrestore(&lock, flags);
顺序锁
一、什么是顺序锁
顺序锁对读写锁的一种优化,使用顺序锁时,读不会被写执行单元阻塞(在读写锁中,写操作必须要等所有读操作完成才能进行)。也就是说,当向一个临界资源中写入的同时,也可以从此临界资源中读取,即实现同时读写,但是不允许同时写数据。如果读执行单元在读操作期间,写执行单元已经发生了写操作,那么,读执行单元必须重新开始,这样保证了数据的完整性,当然这种可能是微乎其微。顺序锁的性能是非常好的,同时他允许读写同时进行,大大的提高了并发性。
二、顺序锁的缺陷
顺序锁的缺陷在于,互斥访问的资源不能是指针,因为写操作有可能导致指针失效,而读操作对失效的指针进行操作将会发生意外错误。
顺序锁在某些场合比读写锁更加高效,但读写锁可以适用于所有场合,而顺序锁不行,所以顺序锁不能完全替代读写锁。
因为自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点很 好地满足了对称多处理机器需要的锁定服务。在单处理器上,自旋锁仅仅当作一个设置内核抢占的开关。如果内 核抢占也不存在,那么自旋锁会在编译时被完全剔除出内核。
简单的说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。另外自旋锁不 允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁——因为睡眠有可能造成持有锁的内核任务被重新调度,而 再次申请自己已持有的锁),它能够在中断上下文中使用。
产生死锁的四个必要条件
(1) 互斥条件:一个资源每次只能被一个进程(线程)使用。
(2) 请求与保持条件:一个进程(线程)因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件 : 此进程(线程)已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件 : 多个进程(线程)之间形成一种头尾相接的循环等待资源关系。
死锁的解除与预防:
(1)对于互斥,因为互斥不能破坏,所以不能由互斥入手来避免死锁
(2)对于请求与保持,请求的时候先把自己的锁释放掉,然后再申请新的锁
(3)对于不剥夺,如果别人的锁的优先级比自己的锁的优先级高,则可以抢过来
(4)对于循环等待,是一样的,在申请新锁的时候,先把自己的锁释放,在申请新锁,就不会陷入循环等待中
总结:除了互斥外,修改使其他3个必要条件之一不成立即可。