分类: LINUX
2012-06-09 12:15:26
首先我们需要想一个问题,为什么OS会把内核同步单独列为一章,为什么没有相关的用户态同步?
带着这个问题我们先来看下用户态和内核在同步方面的区别:首先我们知道在用户态下的进程都有自己的相互独立的地址空间,当然里面的数据也是进程独享的,这就意味着进程之间不会相会影响。也许你会问,当不同进程访问一个临界区时不久有可能造成数据的不一致,但是如果使用合适的调度算法就可能避免这种情况的发生。再次对内核中的共享数据来说,内核中的所有任务都可以对数据进行访问,这就会引发并发的执行,从而造成资源竞争。
并发分为真并发和伪并发,在SMP里面多CPU可以实现真正意义上的并发,从而提高系统的效率,但是并发的存在又引发了临界资源的访问混乱,从而要对内核实行同步策略。
接下来我们从非抢占式内核说起,在非抢占式内核里面,引起并发的操作主要有中断和优先级调度。而在抢占式里面,内核的主要任务:内核线程、中断、系统调用、下半部分都可引起并发。其实引起并发的主要原因是一个任务在执行时被另外一个任务打断。
在内核同步里面主要讲述了几种常用的同步机制以及相关的改进机制。其基本同步机制包括:原子操作、自旋锁、信号量等。而自旋锁里面又引出读写自旋锁、顺序锁和RCU(读-拷贝-更新)锁,而信号量有分为内核同步的信号量和system V IPC 信号量(用户态)。
对以上同步机制在内核里面如何表示和使用大家估计都知道,这里仅作相关说明。
对于原子操作主要对小数据进行同步执行。而自旋锁的引入是针对多处理器而言的。
自旋锁的标志是在等待可用锁的时候进行自旋忙等,极大的浪费了CPU的资源。在自旋锁中引入读/写自旋锁是为了提高系统的并发能力,这似乎看起来有点矛盾。因为我们这几种机制的提出就是针对并发来说的,现在又提高并发。呵呵。首先要说明的是 /写自旋锁拥有相同的优先级,也就是说如果读自旋锁获取了锁,写自旋锁就的等待读自旋锁的释放,反之亦然。对于读操作来说,我们可以认为其可以多个任务同时对临界资源进行访问,而写则必须单独访问临界资源。下面的顺序锁可以说是对读/写自旋锁的改进,它打破了读写的优先级顺序,使得写操作具有更高的优先级。这样一来,在修改临界资源时写操作就不会等待很长时间,这样对读操作来说就造成了不便,当正在读取共享资源时有写操作执行,势必会对读操作产生读数据不一致的情况。我们可以在读数据前后进行读顺序计数器(seqlock_t数据结构的一个sequence字段的定义),并检查两次的值是否相同,如果不同,说明有新的写着对数据进行了修改并增加了顺序计数器。
RCU则是为了保护在多数情况下被多个CPU读的数据结构而设计的一种数据结构。可以允许多个读者和写着并发执行。它的实现不是使用我们之前提到的锁机制执行的,而是使用指针引用实现,即就是保护指针所指向的数据结构。当然它的使用是有相关限制的:只保护被动态分配的数据结构并且不能睡眠。
信号量的使用明确的说明了资源是否可用。信号量执行期间可以睡眠,这是自旋锁所不具有的优势。
接下来对原子操作、自旋锁、信号量针对不同的情况做以区别:
情况 单处理器 多处理器
异常 信号量 无
中断 本地禁止中断 自旋锁
可延迟的函数 无 无或自旋锁
异常与中断 本地禁止中断 自旋锁
异常与可延迟函数 本地禁止软中断 自旋锁
中断与可延迟函数 本地中断禁止 自旋锁
异常、中断和可延迟函数 本地中断禁止 自旋锁
PS:通过上面可以看出只要获取自旋锁,就要在本地对中断禁止,并禁止内核抢占。