没有合适的资源?快使用搜索试试~ 我知道了~
Unreliable Guide to Locking -by Rusty Russell-中文版 .pdf
5星 · 超过95%的资源 需积分: 10 14 下载量 199 浏览量
2010-04-20
16:01:40
上传
评论
收藏 816KB PDF 举报
温馨提示
试读
24页
Unreliable Guide to Locking -by Rusty Russell-中文版 .pdf
资源推荐
资源详情
资源评论
Kernel Locking 中文版
Unreliable Guide To Locking
Rusty Russell
<rusty@rustcorp.com.au>
翻译:
albcamus <albcamus@163.com>
Copyright 2003 Rusty Russell©
This documentation is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
For more details see the file COPYING in the source
distribution of Linux.
第 1 章. 介绍
欢迎进入 Rusty 优秀的《Unreliable Guide to Kernel Locking》细节。本文档描述
了 Linux 2.6 内核的锁系统。
随着 Linux 内核引入了超线程与 抢占 ,每一个 Hacking 内核的人都应该了解并发性
与为
SMP
加锁的基础知识。
第 2 章. 并发带来的问题
(如果你已知道并发是什么,请略过本章).
在一个普通的程序里,你可以这样为一个计数器增加计数:
very_important_count++;
下面是你期望的结果:
Table 2-1. 期望的结果
实例 1 实例 2
读 very_important_count (5)
加 1 (6)
写回 very_important_count (6)
读 very_important_count (6)
加 1 (7)
写回 very_important_count (7)
事情还可能这样发生:
Table 2-2. 可能的结果
实例 1 实例 2
读 very_important_count (5)
读 very_important_count (5)
加 1 (6)
加 1 (6)
写回 very_important_count (6)
写回 very_important_count (6)
2.1. 竞态条件与临界区
这种情况,结果依赖于多个任务的相对执行顺序,叫做竞态条件。包含并发问题的代
码,叫做临界区。尤其是 Linux 开始支持 SMP 机器以来,竞态条件和临界区就成为内核设计
与实现的主要问题。
抢占具有相同的影响,即使只有一个 CPU 也是这样的:抢占了正在临界区中执行的任
务,我们就重现了上面描述的情况。在这种情况下,抢占了其他代码的线程必须在它的临界
区独自运行(排斥其它线程)。
解决的方法是:认识到何时会发生同时访问,使用锁来保证在任意时刻只有一个实例
能够进入临界区。在 Linux 内核中有很多友好的原语(primitives)来帮助你做到这点──也
有不友好的原语,但我将当作它们并不存在。
第 3 章. 在 Linux 内核中加锁
我多么希望能给你一句忠告:不要与比你更不理性的人同眠;而如果是关于锁的忠告,
我会这样给出:保持简单。
不要试图引入新类型的锁。
真是够奇怪的,最后一条竟然恰恰与我的忠告相反──因为这时你已经与不理性的人
同眠了,你需要考虑弄只看门狗。(译者案:Watchdog 是一种以 NMI 方式检查 CPU 死锁的机
制)
3.1. 两种主要类型的锁:自旋锁与信号量
内核中主要有两种类型的锁。最基本的类型是自旋锁(include/asm/spinlock.h),它
只允许一个持有者;如果你获取自旋锁时不成功,你将一直在自旋中尝试获得它,直到成功。
自旋锁又小又快,可以在任何地方使用。
第二种类型是信号量(include/asm/spinlock.h),它可以在任何时刻被多个持有者同
时持有(允许的持有者的数量是在信号量初试化时规定的),但是在通常情况下信号量只允许
一个持有者(这时它也叫互斥锁,mutex)。如果你获取信号量时不成功,任务就会把自身放
到一个队列中睡眠,一直到信号量被释放时才被唤醒。这意味着在你等待的时候,CPU 将做
其他的事情。但是,很多情况是不允许睡眠的(参考 第 9 章 ),这时你应该用自旋锁而不是
信号量。
这两种锁的任何一种都是不允许递归的:参考 7.1 小节 .
3.2. 单 CPU 内核与锁
在单 CPU 机器上,对于编译时打开了 CONFIG_SMP、但没打开 CONFIG_PREEMPT 的内核
来说,自旋锁根本不存在。这是一个出色的设计策略:既然没有别人能在同时刻执行,就没
理由加锁。
如果编译时同时打开了 CONFIG_SMP 和 CONFIG_PREEMPT,自旋锁的作用就仅仅是禁止
抢占,这就足以防止任何竞态了。多数情况下,我们可以把抢占等同于 SMP 来考虑其可能带
来的竞态问题,不必单独对付它。
当测试加锁代码时,即使你没有 SMP 测试环境,总应该打开 CONFIG_SMP 和
CONFIG_PREEMPT 选项,因为这会重现关于锁的某些类型的 BUG。
信号量依然存在,因为它们之所以被引入,乃是为了同步用户上下文。我们将马上看
到这一点。
3.3. 只在用户上下文加锁
如果你的数据结构只可能被用户上下文访问,你可以用信号量
(linux/asm/semaphore.h)来保护它。这是最简单的情况了:把信号量初试化为可用资源的
数量(通常是 1),就可以调用 down_interruptible()来获取信号量,调用 up()来释放它。还
有一个 down()函数,但应该避免使用它,因为如果有信号到来它不会返回。
例如:linux/net/core/netfilter.c 允许用 nf_register_sockopt()注册新的
setsockopt()和 getsockopt()调用。注册和注销只有在模块加载与卸载时才会发生(是在引
导时执行的,没有并发问题),注册了的链表只有 setsockopt()或 getsockopt()系统调用才
会查阅。因此,nf_sockopt_mutex 就可以完美的保护住这些,这是因为 setsockopt 和
getsockopt 允许睡眠。
3.4. 在用户上下文与 Softirqs 之间加锁
如果一个
softirq
与用户上下文共享数据,就有两个问题:首先,当前的用户上下文
可能被 softirq 中断;其次,临界区可能会在别的 CPU 进入。这时 spin_lock_bh()
(include/linux/spinlock.h)就有了用武之地。它会在那个 CPU 上禁止 softirqs,然后获
取锁。spin_unlock_bh()做相反的工作。(由于历史原因,后缀‘bh’成为对各种下半部的
通称,后者是 software interrupts 的旧称。其实 spin_lock_bh 本应叫作
spin_lock_softirq 才贴切)
注意这里你也可以用 spin_lock_irq()或者 spin_lock_irqsave(),这样不单会禁止
softirqs,还会禁止硬件中断:参考第 4 章 。
这在
UP
上也能很好地工作:自旋锁消失了,该宏变成了只是 local_bh_disable()
(include/linux/interrupt.h),它会禁止 softirqs 运行。
3.5. 在用户上下文与 Tasklets 之间加锁
这种情况与上面的情况(译者案,上面“在用户上下文与 softirqs 之间加锁”的情况)
完全一致,因为 t
asklets
本来就是作为一种 softirq 运行的。
3.6. 在用户上下文与 Timers 之间加锁
这种情况也和上面情况完全一致,因为
timers
本来就是一种 softirq(译者案:Timer
是时钟中断的下半部)。从加锁观点来看,tasklets 和 timers 的地位是等同的。
3.7. 在 Tasklets/Timers 之间加锁
有时一个 tasklet 或 timer 会与另一个 tasklet 或 timer 共享数据。
3.7.1. 同一个 Tasklet/Timer
由于同一时刻,一个 tasklet 决不会在两个 CPU 上执行,即使在 SMP 机器上,你也不
必担心你的 tasklet 会发生重入问题(同时运行了两个实例)
3.7.2. 不同的 Tasklets/Timers
如果你的 tasklet 或 timer 想要同另一个 tasklet 或 timer 共享数据,你需要对它们
二者都使用 spin_lock()和 spin_unlock()。没必要使用 spin_lock_bh(),因为你已经处在
tasklet 中了,而同一个 CPU 不会同时再执行其他的 tasklet。
3.8. 在 Softirqs 之间加锁
一个 softirq 经常需要与自己或其它的 tasklet/timer 共享数据。
3.8.1. 同一个 Softirq
同一个 softirq 可能在别的 CPU 上执行:你可以使用 per-CPU 数据(参考 8.3 小节 )
以获得更好的性能。如果你打算这样使用 softirq,通常是为了获得可伸缩的高性能而带来
额外的复杂性。
你需要用 spin_lock()和 spin_unlock()保护共享数据。
3.8.2. 不同的 Softirqs
你需要使用 spin_lock()和 spin_unlock()保护共享数据,不管是 timer,tasklet,
还是不同的/同一个/另一个的 softirq:它们中的任何一个都可以在另一个 CPU 上运行。
第 4 章. 硬中断上下文
硬中断通常与一个 tasklet 或 softirq 通信。这通常涉及到把一个任务放到某个队列
中,再由 softirq 取出来。
4.1. 在硬中断与 Softirqs/Tasklets 之间加锁
如果一个硬件中断服务例程与一个 softirq 共享数据,就有两点需要考虑。第一,
softirq 的执行过程可能会被硬件中断打断;第二,临界区可能会被另一个 CPU 上的硬件中
断进入。这正是 spin_lock_irq()派上用场的地方。它在那个 CPU 上禁止中断,然后获取锁。
spin_unlock_irq()做相反的工作。
硬件中断服务例程中不需要使用 spin_lock_irq(),因为当它在执行的时候 softirq
是不可能执行的:它可以使用 spin_lock(),这个会更快一些。唯一的例外是另一个硬件中
断服务例程使用了相同的锁:spin_lock_irq()会禁止那个硬件中断。
这在 UP 机器上也能很好的工作:自旋锁消失了,spin_lock_irq()变成了仅仅是
local_irq_disable() (include/asm/smp.h),这将会禁止 sofirq/tasklet/BH 的运行。
spin_lock_irqsave()(include/linux/spinlock.h)是 spin_lock_irq()的变体,
它把当前的中断开启与否的状态保存在一个状态字中,用以将来传递给
spin_unlock_restore()。这意味着同样的代码既可以在硬件中断服务例程中(这时中断已
关闭)使用,也可以在 softirqs 中(这时需要主动禁止中断)使用。
注意,softirqs(包括 tasklets 和 timers)是在硬件中断返回时得到运行的,因此
spin_lock_irq()同样也会禁止掉它们。从这个意义上说,spin_lock_irqsave()是最通用和
最强大的加锁函数。
4.2. 在两个硬中断服务例程之间加锁
很少有两个硬件中断服务例程共享数据的情况。如果你真的需要这样做,可以使用
spin_lock_irqsave() :在进入中断服务时是否自动关闭中断,这件事是体系结构相关的。
第 5 章. 关于锁的使用的图表
Pete Zaitcev 提供了这样的总结:
• 如果你处在进程上下文(任何系统调用),想把其他进程排挤出临界区,使用信号量。
你可以获得信号量之后去睡眠(例如调用 copy_from_user 或 kmalloc(x,
GFP_KERNEL)之类的函数)。
• 否则(亦即:数据可能被中断访问),使用 spin_lock_irqsave()和
spin_lock_irqrestore()。
• 避免任何持有自旋锁超过 5 行代码的情况,避免任何跨越函数调用的只有自旋锁的情
况。(有种情况例外,比方象 readb 这样的原子访问)
剩余23页未读,继续阅读
资源评论
- majieyue2012-08-10非常经典,内核程序员必读
happy_flying
- 粉丝: 0
- 资源: 15
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功