什么是并发操作
并发是指多个执行单元同时、并行被执行,而并发的执行单位对于共享资源(硬件资源和软件上的全局变量、静态变量等)的访问很容易导致竞态(race conditions)
竞态主要发生在如下几种情况:
1、对称多处理器(SMP)的多个CPU
2、单CPU内进程与抢占它的进程
3、中断(硬中断、软中断、Tasklet、底半部)与进程之间
4、中断也可能被更高优先级的中断打断,因此,多个中断之间也可能引起并发导致竞态
解决竞态问题的途径是保证对共享资源的互斥访问,所谓互斥访问是指一个执行单元在访问共享资源的时候,其他的执行单元被禁止访问。
访问共享资源的代码区成为临界区,临界区需要被以某种互斥机制加以保护,中断屏蔽、原子操作、自旋锁和信号量是linux设备驱动中可采用的互斥途径。
1、中断屏蔽:只能禁止和使能本CPU内的中断。
2、原子操作:是指在执行过程中不会被别的代码路径所中断的操作。内核代码可以安全的调用它们而不被打断。分为针对位和整形变量进行原子操作两类
3、自旋锁:理解它最简单的方法是把它当作一个变量看待,该变量把一个临界区标记为”我当前在运行,请稍等一会“或者标记为”我当前不在运行,可以被使用“
使用自旋锁可以保证临界区不受别的CPU和本CPU内的抢占进程打扰,但是得到锁的代码路径在执行临界区的时候,还可能受到中断和底半部的影响。
为防止这种影响,需要用到自旋锁的衍生。可以避免突如其来的中断对系统造成的伤害。
使用自旋锁需注意如下:
1、实际上是忙等锁,当锁不可用时,CPU一直循环执行”测试并设置“该锁直到可用而取得该锁,CPU在等待自旋锁时不做任何有用的工作,仅仅是等待。
因此,只有在占有锁的时间极短的情况下,使用自旋锁才是合理的。
2、自旋锁可能导致死锁。引发这个问题最常见的情况是递归使用一个自旋锁,即如果一个已经拥有某个自旋锁的CPU想第二次获得这个锁,则CPU将死锁
3、自旋锁锁定期间不能调用可能引起进程调度的函数。如果进程获取自旋锁之后阻塞,如调用copy_from_user()\copy_to_user()\kmalloc()和msleep
等,则可能导致内核崩溃。
4、信号量:只有得到信号量的进程才能执行临界区代码,与自旋锁不同的是,当获取不到信号量时,进程不会原地打转而是进入休眠等待状态,不能用在中断上下文中
如果信号量被初始化为0,则它可以用于同步,同步意味着一个执行单元的继续执行需等待另一执行单元完成某事。
信号量和自旋锁的区别:
1、信号量是进程级的,用于多个进程之间对资源的互斥,。鉴于进程上下文的切换开销较大,只有当进程占用资源时间较长时,用信号量才是较好的选择,
当所要保护的临界区访问时间较短时,用自旋锁是较好的
2、信号量所保护的临界区可包含可能引起阻塞的代码,而自旋锁要避免,因为阻塞意味着要进行进程的切换,如果进程切换出去后,另一个进程企图获得
本自旋锁,死锁将发生
3、信号量存在于进程上下文,因此,如果被保护的共享资源需要在中断或软中断情况下使用,则在信号量和自旋锁之间只能选择自旋锁。