疯狂内核之同步与互斥
文章平均质量分 83
yunsongice
这个作者很懒,什么都没留下…
展开
-
同步与互斥的基本原理
在本科学习过《操作系统》的同志们都知道,现代操作系统提供了一个并发控制环境,即系统中同时活动着的多个不同的进程,这些进程共享同一个CPU、内存或 I/O设备。特别是对于Linux这种世界上最先进的操作系统来说,其多任务、多用户、分时实时混合的性质决定了多个进程在某种程度上彼此依赖或相互制约的关系,这些关系我们叫“并发关系”,按其性质可以分为同步(synchronization)和互斥(mutual原创 2010-03-11 18:44:00 · 3432 阅读 · 0 评论 -
内核同步与互斥的总结
前面讲了那么多内核同步与互斥的技术,现在我们就来做一个总结。我们可以随意使用前面所述的同步技术保护共享数据结构避免竞争条件。当然,系统性能可能随所选择同步原语种类的不同而有很大变化。通常情况下,内核开发者采用下述由经验得到的法则:把系统中的并发度保持在尽可能高的程度。系统中的并发度又取决于两个主要因素:(1)同时运转的I/O设备数(2)进行有效工作的CPU数为了使I/O吞吐量最大化,应该使中断禁止保持在很短的时间。因为,当中断被禁止时,由I/O设备产生的IRQ被PIC暂时忽略,因此,就没原创 2010-05-31 15:34:00 · 2239 阅读 · 0 评论 -
一些避免竞争条件的实例
<br />人们总是期望内核开发者确定和解决由内核控制路径的交错执行所引起的同步问题。但是,避免竞争条件是一项艰巨的任务,因为这需要对内核的各个成分如何相互作用有一个清楚的理解。为了直观地认识内核内部到底是什么样子,需要提及前面博文中所定义同步技术的几种典型应用场景。<br />1 引用计数器<br /><br />引用计数器广泛地用在内核中以避免由于资源的并发分配和释放而产生的竞争条件。引用计数器(reference counter)只不过是一个atomic_t计数器,与特定的资源,如内存页、模块或文原创 2010-05-31 14:51:00 · 1691 阅读 · 0 评论 -
禁止本地中断
<br />禁止本地CPU中断是确保一组内核语句被当作一个临界区处理的主要机制。这个机制的意义是:即使当硬件设备产生了一个IRQ信号时,中断禁止也让内核控制路径继续执行,因此,这就提供了一种有效的方式,确保内核控制路径中的一些中断处理程序能访问的数据结构也受到保护。<br />1 禁止本地中断<br /><br />然而,禁止本地中断并不保护运行在另一个CPU上的中断处理程序对该数据结构的并发访问,因此,在多处理器系统上,禁止本地中断经常与自旋锁结合使用。<br /><br />宏local_irq原创 2010-05-31 14:32:00 · 4238 阅读 · 1 评论 -
RCU机制
读-拷贝-更新(RCU)是为了保护在多数情况下被多个CPU读的数据结构而设计的另一种同步技术。RCU允许多个读者和写者并发执行(相对于只允许一个写者执行的顺序锁有了改进)。而且,RCU是不使用锁的,就是说,它不使用被所有CPU共享的锁或计数器,在这一点上与读/写自旋锁和顺序锁(由于高速缓存行窃用和失效而有很高的开销)相比RCU具有更大的优势。RCU是如何不使用共享数据结构而令人惊讶地实现多原创 2010-05-19 14:46:00 · 4041 阅读 · 0 评论 -
顺序锁
当使用读/写自旋锁时,内核控制路径发出的执行read_lock或write_lock操作的请求具有相同的优先权:读者必须等待,直到写操作完成。同样地,写者也必须等待,直到读操作完成。Linux 2.6中引入了顺序锁(seqlock),它与读/写自旋锁非常相似,只是它为写者赋予了较高的优先级:事实上,即使在读者正在读的时候也允许写者继续运行。这种策略的好处是写者永远不会等待读(除非另外一个写原创 2010-05-19 14:14:00 · 2936 阅读 · 2 评论 -
自旋锁
加锁(locking)是一种广泛应用的同步技术。当内核控制路径必须访问共享数据结构或进入临界区时,就需要为自己获取一把“锁”。由锁机制保护的资源非常类似于限制于房间内的资源,当某人进入房间时,就把门锁上。如果内核控制路径希望访问资源,就试图获取钥匙“打开门”。当且仅当资源空闲时,它才能成功。然后,只要它还想使用这个资源,门就依然锁着。当内核控制路径释放了锁时,门就打开,另一个内核控制路径就可以进入原创 2010-05-18 19:59:00 · 3533 阅读 · 3 评论 -
读写自旋锁
读/写自旋锁同样是在保护SMP体系下的共享数据结构而引入的,它的引入是为了增加内核的并发能力。只要内核控制路径没有对数据结构进行修改,读/写自旋锁就允许多个内核控制路径同时读同一数据结构。如果一个内核控制路径想对这个结构进行写操作,那么它必须首先获取读/写锁的写锁,写锁授权独占访问这个资源。这样设计的目的,即允许对数据结构并发读可以提高系统性能。下图显示有两个受读/写锁保护的临界区(CI和原创 2010-05-18 20:24:00 · 2111 阅读 · 1 评论 -
优化屏障和内存壁垒
当使用指令优化的编译器时,你千万不要认为指令会严格按它们在源代码中出现的顺序执行。例如,编译器可能重新安排汇编语言指令以使寄存器以最优的方式使用。此外,现代CPU通常并行地执行若干条指令,且可能重新安排内存访问。这种重新排序可以极大地加速程序的执行。然而,当处理同步时,必须避免指令重新排序。因为如果放在同步及原语之后的一条指令在同步原语本身之前执行,事情很快就会变得失控。所以,所有的同步技原创 2010-05-18 19:56:00 · 2750 阅读 · 0 评论 -
原子操作
若干汇编语言指令都具有“读-修改-写”特点 —— 也就是说,它们访问存储器单元两次,第一次读原值,第二次写新值。假定运行在两个CPU上的两个内核控制路径试图通过执行非原子操作来同时“读-修改-写”同一存储器单元,如n++。首先,两个CPU都试图读同一单元,比如n=5。但是存储器仲裁器(对访问RAM芯片的操作进行串行化的硬件电路)插手,只允许其中的一个访问而让另一个延迟。然而,当第一个读操作原创 2010-05-18 19:51:00 · 2232 阅读 · 0 评论 -
每CPU变量
最好的同步技术是把设计不需要同步的临界资源放在首位,这是一种思维方法,因为每一种显式的同步原语都有不容忽视的性能开销。最简单也是最重要的同步技术包括把内核变量或数据结构声明为每CPU变量(per-cpu variable)。每CPU变量主要是数据结构的数组,系统的每个CPU对应数组的一个元素。一个CPU不应该访问与其他CPU对应的数组元素,另外,它可以随意读或修改它自己的元素而不用原创 2010-05-18 19:47:00 · 5811 阅读 · 0 评论 -
内核抢占
为了更好地理解内核代码是如何执行的,我们借用ULK-3中的思想,把内核看作必须满足两种请求的侍者:一种请求来自顾客,另一种请求来自数量有限的几个不同的老板。对不同的请求,侍者采用如下的策略:1. 老板提出请求时,如果侍者正空闲,则侍者开始为老板服务,这是空闲情况。2. 如果老板提出请求时侍者正在为顾客服务,那么侍者停止为顾客服务,开始为老板服务,这是高优先级抢占低优先级情况。原创 2010-05-18 18:52:00 · 2642 阅读 · 0 评论 -
信号量
信号量这个东西,从本质上说,它实现了一个加锁原语,即让等待者睡眠,直到等待的资源变为空闲。实际上,Linux提供两种信号量:- 内核信号量,由内核控制路径使用- System V IPC信号量,由用户态进程使用在本专题,我们集中讨论内核信号量,而IPC信号量将有专门的专题来讲。内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。然而,当内核控制路径试图获取内核信号量所保护的忙资源时,相应的进程被挂起,其task_struck结构被从rq上脱链。只有在资源被释放时,原创 2010-05-31 14:27:00 · 2332 阅读 · 3 评论