Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9472860
  • 博文数量: 1751
  • 博客积分: 12961
  • 博客等级: 上将
  • 技术积分: 20101
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-09 11:25
个人简介

偷得浮生半桶水(半日闲), 好记性不如抄下来(烂笔头). 信息爆炸的时代, 学习是一项持续的工作.

文章分类

全部博文(1751)

文章存档

2024年(27)

2023年(26)

2022年(112)

2021年(217)

2020年(157)

2019年(192)

2018年(81)

2017年(78)

2016年(70)

2015年(52)

2014年(40)

2013年(51)

2012年(85)

2011年(45)

2010年(231)

2009年(287)

分类: LINUX

2009-02-06 11:56:11

自旋锁是使用忙等待锁来确保互斥锁的一种特殊方法。如果锁可用,则获取锁,执行互斥锁动作,然后释放锁。如果锁不可用,线程将忙等待该锁,直到其可用为止。忙等待看起来效率低下,但它实际上比将线程休眠然后当锁可用时将其唤醒要快得多。

自旋锁只在 SMP 系统中才有用,但是因为您的代码最终将会在 SMP 系统上运行,将它们添加到 UP 系统是个明智的做法。

自旋锁有两种可用的形式:完全锁(full lock)和读写锁。 首先看一下完全锁。

首先通过一个简单的声明创建一个新的自旋锁。这可以通过调用 spin_lock_init 进行初始化。清单 5 中显示的每个变量都会实现相同的结果。



                
spinlock_t my_spinlock = SPIN_LOCK_UNLOCKED;

... or ...

DEFINE_SPINLOCK( my_spinlock );

... or ...

spin_lock_init( &my_spinlock );

定义了自旋锁之后,就可以使用大量的锁定变量了。每个变量用于不同的上下文。

清单 6 中显示了 spin_lock 和 spin_unlock 变量。这是一个最简单的变量,它不会执行中断禁用,但是包含全部的内存壁垒(memory barrier)。这个变量假定中断处理程序和该锁之间没有交互。



                
spin_lock( &my_spinlock );

// critical section

spin_unlock( &my_spinlock );

接下来是 irqsave 和 irqrestore 对,如清单 7 所示。spin_lock_irqsave 函数需要自旋锁,并且在本地处理器(在 SMP 情形中)上禁用中断。spin_unlock_irqrestore 函数释放自旋锁,并且(通过 flags参数)恢复中断。



                
spin_lock_irqsave( &my_spinlock, flags );

// critical section

spin_unlock_irqrestore( &my_spinlock, flags );

spin_lock_irqsave/spin_unlock_irqrestore 的一个不太安全的变体是 spin_lock_irq/spin_unlock_irq。 我建议不要使用此变体,因为它会假设中断状态。

最后,如果内核线程通过 bottom half 方式共享数据,那么可以使用自旋锁的另一个变体。bottom half 方法可以将设备驱动程序中的工作延迟到中断处理后执行。这种自旋锁禁用了本地 CPU 上的软中断。这可以阻止 softirq、tasklet 和 bottom half 在本地 CPU 上运行。这个变体如清单 8 所示。



                
spin_lock_bh( &my_spinlock );

// critical section

spin_unlock_bh( &my_spinlock );





回页首


在 许多情形下,对数据的访问是由大量的读和少量的写操作来完成的(读取数据比写入数据更常见)。读/写锁的创建就是为了支持这种模型。这个模型有趣的地方在 于允许多个线程同时访问相同数据,但同一时刻只允许一个线程写入数据。如果执行写操作的线程持有此锁,则临界段不能由其他线程读取。如果一个执行读操作的 线程持有此锁,那么多个读线程都可以进入临界段。清单 9 演示了这个模型。



                
rwlock_t my_rwlock;

rwlock_init( &my_rwlock );

write_lock( &my_rwlock );

// critical section -- can read and write

write_unlock( &my_rwlock );


read_lock( &my_rwlock );

// critical section -- can read only

read_unlock( &my_rwlock );

根据对锁的需求,还针对 bottom half 和中断请求(IRQ)对读/写自旋锁进行了修改。显然,如果您使用的是原版的读/写锁,那么按照标准自旋锁的用法使用这个自旋锁,而不区分读线程和写线程。





回页首


在 内核中可以使用互斥锁来实现信号量行为。内核互斥锁是在原子 API 之上实现的,但这对于内核用户是不可见的。互斥锁很简单,但是有一些规则必须牢记。同一时间只能有一个任务持有互斥锁,而且只有这个任务可以对互斥锁进行 解锁。互斥锁不能进行递归锁定或解锁,并且互斥锁可能不能用于交互上下文。但是互斥锁比当前的内核信号量选项更快,并且更加紧凑,因此如果它们满足您的需 求,那么它们将是您明智的选择。

可以通过 DEFINE_MUTEX 宏使用一个操作创建和初始化互斥锁。这将创建一个新的互斥锁并初始化其结构。可以在 ./linux/include/linux/mutex.h 中查看该实现。

                DEFINE_MUTEX( my_mutex );

互斥锁 API 提供了 5 个函数:其中 3 个用于锁定,一个用于解锁,另一个用于测试互斥锁。首先看一下锁定函数。在需要立即锁定以及希望在互斥锁不可用时掌握控制的情形下,可以使用第一个函数mutex_trylock。该函数如清单 10 所示。



                
ret = mutex_trylock( &my_mutex );
if (ret != 0) {
// Got the lock!
} else {
// Did not get the lock
}

如果想等待这个锁,可以调用 mutex_lock。这个调用在互斥锁可用时返回,否则,在互斥锁锁可用之前它将休眠。无论在哪种情形中,当控制被返回时,调用者将持有互斥锁。最后,当调用者休眠时使用mutex_lock_interruptible。在这种情况下,该函数可能返回 -EINTR。清单 11 中显示了这两种调用。



                
mutex_lock( &my_mutex );

// Lock is now held by the caller.

if (mutex_lock_interruptible( &my_mutex ) != 0) {

// Interrupted by a signal, no mutex held

}

当一个互斥锁被锁定后,它必须被解锁。这是由 mutex_unlock 函数来完成的。这个函数不能从中断上下文调用。最后,可以通过调用 mutex_is_locked 检查互斥锁的状态。这个调用实际上编译成一个内联函数。如果互斥锁被持有(锁定),那么就会返回 1;否则,返回 0。清单 12 演示了这些函数。



                
mutex_unlock( &my_mutex );

if (mutex_is_locked( &my_mutex ) == 0) {

// Mutex is unlocked

}

互斥锁 API 存在着自身的局限性,因为它是基于原子 API 的。但是其效率比较高,如果能满足你的需要,还是可以使用的。





回页首


最后看一下大内核锁(BLK)。它在内核中的用途越来越小,但是仍然有一些保留下来的用法。BKL 使多处理器 Linux 成为可能,但是细粒度(finer-grained)锁正在慢慢取代 BKL。BKL 通过lock_kernel 和 unlock_kernel 函数提供。要获得更多信息,请查看 ./linux/lib/kernel_lock.c。





回页首



Linux 性能非凡,其锁定方法也一样。原子锁不仅提供了一种锁定机制,同时也提供了算术或 bitwise 操作。自旋锁提供了一种锁定机制(主要应用于 SMP),而且读/写自旋锁允许多个读线程且仅有一个写线程获得给定的锁。最后,互斥锁是一种新的锁定机制,提供了一种构建在原子之上的简单 API。不管你需要什么,Linux 都会提供一种锁定方案保护您的数据。



学习

获得产品和技术
  • 在  上获取最新的 Linux 源代码。Linux 源代码本身就是关于内核操作最直接的来源。文档子目录中也有大量文档(尽管其中部分内容比较陈旧)。

  • 订购 SEK for Linux,这套光盘(2张 DVD)包含了最新的 IBM Linux 试用软件,这些软件来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere®。 

  • 使用可从 developerWorks 直接下载的 IBM 试用软件,构建下一个 Linux 开发项目。 
阅读(1441) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~