第8节 内核同步
推荐给好友
打印
加入收藏
更新于2009-06-14 08:04:27

4. 8.1信号量

进程间对共享资源的互斥访问是通过“信号量”机制来实现的。信号量机制是操作系统教材中比较重要的内容之一。Linux内核中提供了两个函数down()和up(),分别对应于操作系统教材中的P、V操作。

信号量在内核中定义为semaphore数据结构,其位于include/i386/semaphore.h:

struct semaphore {
atomic_t count;
int sleepers;
wait_queue_head_t wait;
#if WAITQUEUE_DEBUG
long __magic;
#endif
};

其中的count域就是“信号量”中的那个“量”,它代表着可用资源的数量。如果该值大于0,那么资源就是空闲的,也就是说,该资源可以使用。相反,如果count小于0,那么这个信号量就是繁忙的,也就是说,这个受保护的资源现在不能使用。在后一种情况下,count的绝对值表示了正在等待这个资源的进程数。该值为0表示有一个进程正在使用这个资源,但没有其他进程在等待这个资源。

Wait域存放等待链表的地址,该链表中包含正在等待这个资源的所有睡眠的进程。当然,如果count大于或等于0,则等待队列为空。为了明确表示等待队列中正在等待的进程数,引入了计数器sleepers。

down()和up()函数主要应用在文件系统和驱动程序中,把要保护的临界区放在这两个函数中间,用法如下:

down();

临界区

up();

这两个函数是用嵌入式汇编实现的,非常麻烦,在此不予详细介绍。

4.8.2原子操作

避免干扰的最简单方法就是保证操作的原子性,即操作必须在一条单独的指令内执行。有两种类型的原子操作,即位图操作和数学的加减操作。

1.位图操作

在内核的很多地方用到位图,例如内存管理中对空闲页的管理,位图还有一个广泛的用途就是简单的加锁,例如提供对打开设备的互斥访问。关于位图的操作函数如下:

以下函数的参数中,addr指向位图。

void set_bit(int nr, volatile void *addr):设置位图的第nr位。

void clear_bit(int nr, volatile void *addr): 清位图的第nr位

void change_bit(int nr, volatile void *addr): 改变位图的第nr位

int test_and_set_bit(int nr, volatile void *addr): 设置第nr位,并返回该位原来的值,且两个操作是原子操作,不可分割。

int test_and_clear_bit(int nr, volatile void *addr): 清第nr为,并返回该位原来的值,且两个操作是原子操作。

int test_and_change_bit(int nr, volatile void *addr):改变第nr位,并返回该位原来的值,且这两个操作是原子操作。

这些操作利用了LOCK_PREFIX宏,对于SMP内核,该宏是总线锁指令的前缀,对于单CPU这个宏不起任何作用。这就保证了在SMP环境下访问的原子性。

2.算术操作

有时候位操作是不方便的,取而代之的是需要执行算术操作,即加、减操作及加1、减1操作。典型的例子是很多数据结构中的引用计数域count(如inode结构)。这些操作的原子性是由atomic_t数据类型和表4.14中的函数保证的。atomic_t的类型在include/ i386/atomic.h定义如下:

typedef struct { volatile int counter; } atomic_t;

4.8.3 自旋锁、读写自旋锁和大读者自旋锁

在Linux开发的早期,开发者就面临这样的问题,即不同类型的上下文(用户进程对中断)如何访问共享的数据,以及如何访问来自多个CPU同一上下文的不同实例。

在Linux内核中,临界区的代码或者是由进程上下文来执行,或者是由中断上下文来执行。在单CPU上,可以用cli/sti指令来保护临界区的使用,例如:
unsigned long flags;

save_flags(flags);

cli();

/* critical code */

restore_flags(flags);

但是,在SMP上,这种方法明显是没有用的,因为同一段代码序列可能由另一个进程同时执行,而cli()仅仅能单独地为每个CPU上的中断上下文提供对竟争资源的保护,它无法对运行在不同CPU上的上下文提供对竟争资源的访问。因此,必须用到自旋锁。

所谓自旋锁,就是当一个进程发现锁被另一个进程锁着时,它就不停地“旋转”,不断执行一个指令的循环直到锁打开。自旋锁只对SMP有用,对单CPU没有意义。

有三种类型的自旋锁:基本的、读写以及大读者自旋锁。读写自旋锁适用于“多个读者少数写者”的场合,例如,有多个读者仅有一个写者,或者没有读者只有一个写者。大读者自旋锁是读写自旋锁的一种,但更照顾读者。大读者自旋锁现在主要用在Sparc64和网络系统中。

上一节  下一节

相关链接


 
关于我们 | 诚邀加盟 | 客户服务 | 相关法律 | 网站地图 | 友情链接 | 服务信箱:service@eefocus.com
© 2006 与非门科技信息咨询(北京)有限公司 All Rights Reserved.