smp操作系统 内核跑在哪个核

您所在的位置: &
从2.x到4.x,Linux内核这十年经历了哪些重要变革
从2.x到4.x,Linux内核这十年经历了哪些重要变革
Linux内核的2.6时代跨度非常大,从2.6.1到2.6.39跨越了39个大版本。3.0到3.19经历了20个版本。4.0到4.2,又有3个版本。本文汇总分析从2.6.12到4.2这中间51个大版本,时间跨度10年的主要大模块的一些重要变革。
【综合消息】Linux内核现在已经进入4.x时代了,但是据说从版本2.6升到3.0,以及3.19升到4.0这之间都没什么太大的变革。事实如此吗?内核版本间的区别有多大?
说实话,这个问题挺大的。Linux内核的2.6 时代跨度非常大,从2.6.1 (2003年12月发布) 到
2.6.39(2011年5月发布),跨越了39 个大版本。3.0(原计划的2.6.40,2011年7月发布) 到
3.19(2015年2月发布),经历了20个版本。4.0(2015年4月发布)到4.2(2015年8月底发布),又有3个版本。
总的来说,从进入2.6之后,每个大版本跨度开发时间大概是 2 - 3 个月。2.6.x , 3.x,
4.x,数字的递进并没有非常根本性,引人注目的大变化,但每个大版本中都有一些或大或小的功能改变。主版本号只是一个数字而已。不过要直接从 2.6.x 升级 到
3.x, 乃至 4.x,随着时间间隔增大,出问题的机率当然大很多。
个人觉得 Linux 真正走入严肃级别的高稳定性,高可用性,高可伸缩性的工业级别内核大概是在 2003
年之后吧!一是随着互联网的迅速普及,更多的人使用、参与开发。二是社区经过11年发展,已经慢慢摸索出一套很稳定的协同开发模式,一个重要的特点是社区开始使用版本管理工具进行管理,脱离了之前纯粹手工(或一些辅助的简陋工具)处理代码邮件的方式,大大加快了开发的速度和力度。
因此,本文汇总分析一下从 2.6.12 (2005年6月发布,也就是社区开始使用 git 进行管理后的第一个大版本),到 4.2
(2015年8月发布)这中间共 51个大版本,时间跨度10年的主要大模块的一些重要的变革。
1.抢占支持(preemption): 2.6 时代开始支持(具体时间难考,是在 2.5 这个奇数版本中引入,可看此文章[1],关于 Linux
版本规则,可看我文章[2])。
可抢占性,对一个系统的调度延时具有重要意义。2.6
之前,一个进程进入内核态后,别的进程无法抢占,只能等其完成或退出内核态时才能抢占,这带来严重的延时问题,2.6 开始支持内核态抢占。
2.普通进程调度器(SCHED_OTHER)之纠结进化史
Linux一开始,普通进程和实时进程都是基于优先级的一个调度器,实时进程支持 100
个优先级,普通进程是优先级小于实时进程的一个静态优先级,所有普通进程创建时都是默认此优先级,但可通过 nice()
接口调整动态优先级(共40个)。实时进程的调度器比较简单,而普通进程的调度器,则历经变迁[3]:
(1) O(1) 调度器:2.6 时代开始支持(2002年引入)。顾名思义,此调度器为O(1)时间复杂度。该调度器以修正之间的O(n)
时间复杂度调度器,以解决扩展性问题。为每一个动态优先级维护队列,从而能在常数时间内选举下一个进程来执行。
(2) 夭折的 RSDL(The Rotating Staircase Deadline Scheduler)调度器,2007 年4
月提出,预期进入2.6.22,后夭折。
调度器存在一个比较严重的问题:复杂的交互进程识别启发式算法-为了识别交互性的和批处理型的两大类进程,该启发式算法融入了睡眠时间作为考量的标准,但对于一些特殊的情况,经常判断不准,而且是改完一种情况又发现一种情况。
Con Kolivas (八卦:这家伙白天是个麻醉医生)为解决这个问题提出RSDL(The Rotating Staircase Deadline
Scheduler)算法。该算法的亮点是对公平概念的重新思考:交互式(A)和批量式(B)进程应该是被完全公平对待的,对于两个动态优先级完全一样的 A,B
进程,它们应该被同等地对待,至于它们是交互式与否(交互式的应该被更快调度), 应该从他们对分配给他们的时间片的使用自然地表现出来,而不是应该由调度器自作高明地根据他们的睡眠时间去猜测。这个算法的核心是Rotating
Staircase,它是一种衰减式的优先级调整,不同进程的时间片使用方式不同,会让它们以不同的速率衰减(在优先级队列数组中一级一级下降,这是下楼梯这名字的由来),从而自然地区分开进程是交互式的(间歇性的少量使用时间片)和批量式的(密集的使用时间片)。具体算法细节可看这篇文章: [LWN.net]
(3) 完全公平的调度器(CFS), 2.6.23(2007年10月发布)
Con Kolivas 的完全公平的想法启发了原O(1)调度器作者Ingo
Molnar,他重新实现了一个新的调度器,叫CFS。新调度器的核心同样是完全公平性,即平等地看待所有普通进程,让它们自身行为彼此区分开来,从而指导调度器进行下一个执行进程的选举。
具体说来,此算法基于一个理想模型。想像你有一台无限个相同计算力的 CPU,那么完全公平很容易,每个 CPU 上跑一个进程即可。但是,现实的机器 CPU
个数是有限的,超过 CPU
个数的进程数不可能完全同时运行。因此,算法为每个进程维护一个理想的运行时间,及实际的运行时间,这两个时间差值大的,说明受到了不公平待遇,更应得到执行。
至于这种算法如何区分交互式进程和批量式进程,很简单。交互式的进程大部分时间在睡眠,因此它的实际运行时间很小,而理想运行时间是随着时间的前进而增加的,所以这两个时间的差值会变大。与之相反,批量式进程大部分时间在运行,它的实际运行时间和理想运行时间的差距就较小。因此,这两种进程被区分开来。
CFS 的测试性能比 RSDS 好,并得到更多的开发者支持,所以它最终替代了 RSDL 在 2.6.23
进入内核,一直使用到现在。可以八卦的是,Con Kolivas 因此离开了社区,不过他本人否认是因为此事,心生龃龉。后来,2009 年,他对越来越庞杂的 CFS
不满意,认为 CFS 过分注重对大规模机器,而大部分人都是使用少 CPU 的小机器,开发了 BFS 调度器[4],这个在 Android 中有使用,没进入
Linux 内核。
3.有空时再跑 SCHED_IDLE, 2.6.23(2007年10月发布)
此调度策略和 CFS 调度器在同一版本引入。系统在空闲时,每个 CPU 都有一个 idle 线程在跑,它什么也不做,就是把 CPU
放入硬件睡眠状态以节能(需要特定CPU的driver支持),并等待新的任务到来,以把 CPU 从睡眠状态中唤醒。如果你有任务想在 CPU 完全 idle
时才执行,就可以用sched_setscheduler() API 设置此策略。
4.吭哧吭哧跑计算 SCHED_BATCH, 2.6.16(2006年3月发布)
概述中讲到 SCHED_BATCH 并非 POSIX 标准要求的调度策略,而是 Linux 自己额外支持的。
它是从 SCHED_OTHER 中分化出来的,和 SCHED_OTHER 一样,不过该调度策略会让采用策略的进程比 SCHED_OTHER
更少受到调度器的重视。因此,它适合非交互性的,CPU 密集运算型的任务。如果你事先知道你的任务属于该类型,可以用 sched_setscheduler()
API 设置此策略。
在引入该策略后,原来的 SCHED_OTHER 被改名为 SCHED_NORMAL,不过它的值不变,因此保持API 兼容,之前的
SCHED_OTHER 自动成为 SCHED_NORMAL,除非你设置 SCHED_BATCH。
5.十万火急,限期完成 SCHED_DEADLINE, 3.14(2014年3月发布)
此策略支持的是一种实时任务。对于某些实时任务,具有阵发性(sporadic),它们阵发性地醒来执行任务,且任务有deadline
要求,因此要保证在deadline 时间到来前完成。为了完成此目标,采用该 SCHED_DEADLINE
的任务是系统中最高优先级的,它们醒来时可以抢占任何进程。
如果你有任务属于该类型,可以用 sched_setscheduler() 或 sched_setattr() API 设置此策略。
更多可参看此文章: [LWN.net]
6.普通进程的组调度支持(Fair Group Scheduling), 2.6.24(2008年1月发布)
2.6.23 引入的 CFS 调度器对所有进程完全公平对待。但这有个问题,设想当前机器有2个用户,有一个用户跑着9个进程,还都是CPU
密集型进程;另一个用户只跑着一个 X 进程,这是交互性进程。从 CFS 的角度看,它将平等对待这 10 个进程,结果导致的是跑 X
进程的用户受到不公平对待,他只能得到约 10% 的 CPU 时间,让他的体验相当差。
基于此,组调度的概念被引入[6]。CFS 处理的不再是一个进程的概念,而是调度实体(sched
entity),一个调度实体可以只包含一个进程,也可以包含多个进程。因此,上述例子的困境可以这么解决:分别为每个用户建立一个组,组里放该用户所有进程,从而保证用户间的公平性。
该功能是基于控制组(control group, cgroup)的概念,需要内核开启 CGROUP 的支持才可使用。关于 CGROUP
,以后可能会写。
7.实时进程的组调度支持(RT Group Scheduling), 2.6.25(2008年4月发布)
该功能同普通进程的组调度功能一样,只不过是针对实时进程的。
8.组调度带宽控制((CFS bandwidth control),3.2(2012年1月发布)
组调度的支持,对实现多租户系统的管理是十分方便的,在一台机器上,可以方便对多用户进行 CPU
均分。然后,这还不足够,组调度只能保证用户间的公平,但若管理员想控制一个用户使用的最大CPU 资源,则需要带宽控制。3.2 针对
CFS组调度,引入了此功能[6],该功能可以让管理员控制在一段时间内一个组可以使用 CPU 的最长时间。
9.极大提高体验的自动组调度(Auto Group Scheduling),2.6.38(2011年3月发布)
试想,你在终端里熟练地敲击命令,编译一个大型项目的代码,如Linux内核,然后在编译的同时悠闲地看着电影等待,结果电脑却非常卡,体验一定很不爽。
引入了一个针对桌面用户体验的改进,叫做自动组调度.短短400多行代码[7],就很大地提高了上述情形中桌面使用者体验,引起不小轰动。
其实原理不复杂,它是基于之前支持的组调度的一个延伸。Unix 世界里,有一个会话(session)
的概念,即跟某一项任务相关的所有进程,可以放在一个会话里,统一管理。比如你登录一个系统,在终端里敲入用户名,密码,然后执行各种操作,这所有进程,就被规划在一个会话。
因此,在上述例子里,编译代码和终端进程在一个会话里,你的浏览器则在另一个会话里。自动组调度的工作就是,把这些不同会话自动分成不同的调度组,从而利用组调度的优势,使浏览器会话不会过多地受到终端会话的影响,从而提高体验。
该功能可以手动关闭。
10.基于调度域的负载均衡,2.6.7(2004年6月发布)
计算机依靠并行度来突破性能瓶颈,CPU个数也是与日俱增。最早的是 SMP(对称多处理),所以 CPU共享内存,并访问速度一致。随着 CPU
个数的增加,这种做法不适应了,因为 CPU 个数的增多,增加了总线访问冲突,这样 CPU 增加的并行度被访问内存总线的瓶颈给抵消了,于是引入了
NUMA(非一致性内存访问)的概念。机器分为若干个node,每个node(其实一般就是一个socket)有本地可访问的内存,也可以通过 interconnect
中介机构访问别的 node 的内存,但是访问速度降低了,所以叫非一致性内存访问。Linux 2.5版本时就开始了对NUMA 的支持[5]。
而在调度器领域,调度器有一个重要任务就是做负载均衡。当某个 CPU 出现空闲,就要从别的 CPU
上调整任务过来执行;当创建新进程时,调度器也会根据当前负载状况分配一个最适合的 CPU 来执行。然后,这些概念是大大简化了实际情形。
在一个 NUMA 机器上,存在下列层级:
◆每一个NUMA node 是一个 CPU socket(你看主板上CPU位置上那一块东西就是一个socket)。
◆每一个socket上,可能存在两个核,甚至四个核。
◆每一个核上,可以打开硬件多纯程(HyperThread)。
如果一个机器上同时存在这三个层级,则对调度器来说,它所见的一个逻辑 CPU其实是一个HyperThread。处理同一个core
中的CPU,可以共享L1,乃至 L2 缓存,不同的 core 间,可以共享 L3 缓存(如果存在的话)。
基于此,负载均衡不能简单看不同 CPU 上的任务个数,还要考虑缓存,内存访问速度。所以,2.6.7 引入了调度域(sched domain)
的概念,把 CPU 按上述层级划分为不同的层级,构建成一棵树,叶子节点是每个逻辑 CPU,往上一层,是属于 core 这个域,再往上是属于 socket
这个域,再往上是 NUMA 这个域,包含所有 CPU。
当进行负载均衡时,将从最低一级域往上看,如果能在 core 这个层级进行均衡,那最好;否则往上一级,能在socket 一级进行均衡也还凑合;最后是在
NUMA node 之间进行均衡,这是代价非常大的,因为跨 node 的内存访问速度会降低,也许会得不偿失,很少在这一层进行均衡。
这种分层的做法不仅保证了均衡与性能的平衡,还提高了负载均衡的效率。
关于这方面,可以看这篇文章: [LWN.net]
11.更精确的调度时钟(HRTICK), 2.6.25(2008年4月发布)
CPU的周期性调度,和基于时间片的调度,是要基于时钟中断来触发的。一个典型的 1000 HZ 机器,每秒钟产生 1000
次时间中断,每次中断到来后,调度器会看看是否需要调度。
然而,对于调度时间粒度为微秒(10^-6)级别的精度来说,这每秒 1000 次的粒度就显得太粗糙了。
2.6.25引入了所谓的高清嘀哒(High Resolution Tick),以提供更精确的调度时钟中断。这个功能是基于高清时钟(High
Resolution Timer)框架,这个框架让内核支持可以提供纳秒级别的精度的硬件时钟(将会在时钟子系统里讲)。
12.自动 NUMA 均衡(Automatic NUMA balancing),3.8(2013年2月发布)
NUMA 机器一个重要特性就是不同 node 之间的内存访问速度有差异,访问本地 node 很快,访问别的 node
则很慢。所以,进程分配内存时,总是优先分配所在 node 上的内存。然而,前面说过,调度器的负载均衡是可能把一个进程从一个 node 迁移到另一个 node
上的,这样就造成了跨 node 的内存访问;Linux 支持 CPU 热插拔,当一个 CPU 下线时,它上面的进程会被迁移到别的 CPU
上,也可能出现这种情况。
调度者和内存领域的开发者一直致力于解决这个问题.由于两大系统都非常复杂,找一个通用的可靠的解决方案不容易,开发者中提出两套解决方案,各有优劣,一直未能达成一致意见。3.8内核中,内存领域的知名黑客
Mel Gorman 基于此情况,引入一个叫自动 NUMA
均衡的框架,以期存在的两套解决方案可以在此框架上进行整合;同时,他在此框架上实现了简单的策略:每当发现有跨 node
访问内存的情况时,就马上把该内存页面迁移到当前 node 上。
不过到 4.2 ,似乎也没发现之前的两套方案有任意一个迁移到这个框架上,倒是,在前述的简单策略上进行更多改进。
如果需要研究此功能的话,可参考以下几篇文章:
◆介绍 3.8 前两套竞争方案的文章: [LWN.net]
◆介绍 3.8 自动 NUMA 均衡 框架的文章: [LWN.net]
◆介绍 3.8 后进展的两篇文章,细节较多,建议对调度/内存代码有研究后才研读:
13.CPU 调度与节能
从节能角度讲,如果能维持更多的 CPU 处于深睡眠状态,仅保持必要数目的 CPU
执行任务,就能更好地节约电量,这对笔记本电脑来说,尤其重要。然而,这不是一个简单的工作,这涉及到负载均衡,调度器,节能模块的并互,Linux
调度器中曾经有相关的代码,但后来发现问题,在3.5, 3.6 版本中,已经把相关代码删除.整个问题需要重新思考。
在前不久,一个新的 patch 被提交到 Linux 内核开发邮件列表,这个问题也许有了新的眉目,到时再来更新此小节.可阅读此文章:
[3] IBM developworks 上有一篇综述文章,值得一读:
[4] [LWN.net]
[6] [LWN.net]
【编辑推荐】
【责任编辑: TEL:(010)】
关于&&&&的更多文章
Linux强大的适用性和创新性、开放性在桌面环境方面得到了很好的
讲师: 10人学习过讲师: 18人学习过讲师: 16人学习过
CentOS是Linux发行版之一,它是来自于Red Hat Enterpr
Windows Threshold是微软下一代Windows,计划于2015年
日前,红帽正式发布了企业版 Linux 7 ,满足企业当前
本书提供了.NET框架下C#编程的详尽指南。书中详细介绍了.NET框架中的核心概念、使用GDI+编写高级用户界面、多线程程序设计、使
51CTO旗下网站基本概述/内核
内核内核并不是计算机系统中必要的组成部分。程序可以直接地被调入计算机中执行,这样的设计说明了设计者不希望提供任何硬件抽象和操作系统的支持,它常见于早期计算机的设计中。最终,一些辅助性程序,例如和,被设计到机器核心当中,或者固化在只读里。这些变化发生时,操作系统内核的概念就渐渐明晰起来。直接对硬件操作是非常复杂的,所以内核通常提供一种硬件抽象的方法来完成这些操作。硬件抽象隐藏了复杂性,为应用软件和硬件提供了一套简洁,统一的接口,使程序设计更为简单。
单内核单内核结构的操作系统单内核为潜在的硬件提供了大量完善的硬件抽象操作。单内核结构在硬件之上定义了一个高阶的界面,应用一组原语(或者叫系统调用)来实现操作系统的功能,例如进程管理,文件系统,和存储管理等等,这些功能由多个运行在核心态的模块来完成。尽管每一个模块都是单独地服务这些操作,内核代码是高度集成的,而且难以编写正确。因为所有的都在同一个内核空间上运行,一个很小的bug都会使整个系统崩溃。然而,如果开发顺利,单内核结构就可以从运行效率上得到好处。 很多现代的结构内核,如Linux和FreeBSD内核,能够在运行时将模块调入执行,这就可以使扩充内核的功能变得更简单,也可以使内核的核心部分变得更简洁。单内核结构的例子:1、传统的UNIX内核,例如伯克利大学发行的版本;2、Linux内核。微内核具有安全权限限制的微内核只提供了很小一部分的硬件抽象,大部分功能由一种特殊的用户态程序:服务器来完成。微内核结构由一个非常简单的硬件抽象层和一组比较关键的原语或系统调用组成,这些原语仅仅包括了建立一个系统必需的几个部分,如:线程管理,地址空间和进程间通信等。微核的目标是将系统服务的实现和系统的基本操作规则分离开来。例如,进程的输入/输出锁定服务可以由运行在微核之外的一个服务组件来提供。这些非常模块化的用户态服务器用于完成操作系统中比较高级的操作,这样的设计使内核中最核心的部分的设计更简单。一个服务组件的失效并不会导致整个系统的崩溃,内核需要做的,仅仅是重新启动这个组件,而不必影响其它的部分。微内核将许多放入分离的进程,如文件系统,设备驱动程序,而进程通过消息传递调用OS服务。微内核结构必然是多线程的,第一代微内核,在核心提供了较多的服务,因此被称为“胖微内核”。它的典型代表是,它既是GNU HURD也是APPLE SERVER OS 的核心,可以说,。第二代为内核只提供最基本的OS服务,典型的OS是QNX,QNX在理论界很有名,被认为是一种先进的OS。微内核的例子: 第二代设计理论设计1、AIX 2、BeOS 3、L4微内核系列 4、Mach, 用于GNU Hurd和Mac OS X 5、Minix 6、7、QNX 8、RadiOS 9、VSTa
单内核与微内核的比较单内核结构是非常有吸引力的一种设计,由于在同一个地址空间上实现所有低级操作的系统控制的复杂性的效率会比在不同地址空间上实现更高些。20世纪90年代初,单内核结构被认为是过时的。把Linux设计成为单内核结构而不是微内核引起了无数的争议。现在,单核结构正趋向于容易被正确设计,所以它的发展会比微内核结构更迅速些。两个阵营中都有成功的案例。微核经常被用于机器人和医疗器械的嵌入式设计中,因为它的系统的关键部分都处在相互分开的,被保护的存储空间中。这对于单核设计来说是不可能的,就算它采用了运行时加载模块的方式。尽管Mach是众所周知的多用途的微内核,人们还是开发了除此之外的几个微内核。L3是一个演示性的内核,只是为了证明微内核设计并不总是低运行速度。它的后续版本L4甚至可以将Linux内核在单独的地址空间作为它的一个进程来运行。内核是一个从20世纪80年代就开始设计的微内核系统。它比Mach更接近微内核的理念。它被用于一些特殊的领域,在这些情况下由于软件错误导致系统失效是不允许的。例如航天飞机上的机械手,还有研磨镜片的机器,一点点失误就会导致上千的损失。很多人相信,由于Mach不能够解决一些提出微内核理论时针对的问题,所以毫无用处。Mach的爱好者表明这是非常狭隘的观点,遗憾的是似乎所有人都开始接受这种观点。混合内核内核混合内核很像微内核结构,只不过它的的组件更多的在核心态中运行,以获得更快的执行速度。实质上是微内核,只不过它让一些微核结构运行在用户空间的代码运行在内核空间,这样让内核的运行效率更高些。这是一种妥协做法,设计者参考了微内核结构的系统运行速度不佳的理论。然而后来的实验证明,纯微内核的系统实际上也可以是高效率的。大多数现代操作系统遵循这种设计范畴,微软视窗就是一个很好的例子。另外还有,运行在苹果Mac OS X上的内核,也是一个混合内核。 混合内核的例子: 1、BeOS 内核 2、DragonFly BSD 3、Windows NT、Windows 2000、Windows XP、以及等基于的操作系统。XNU一些人认为可以在运行时加载模块的单核系统和混合内核系统没有区别。这是不正确的。混合意味着它从单核和微核系统中都吸取了一定的设计模式,例如一些非关键的代码在用户空间运行,另一些在内核空间运行,单纯是为了效率的原因。外内核内核外内核这种内核不提供任何硬件抽象操作,但是允许为内核增加额外的运行库,通过这些运行库应用程序可以直接地或者接近直接地对硬件进行操作。外内核系统,也被称为纵向结构操作系统,使一种比较极端的设计方法。它的设计理念是让用户程序的设计者来决定硬件接口的设计。外内核本身非常的小,它通常只负责保护和系统资源复用相关的服务。传统的内核设计(包括单核和微核)都对硬件作了抽象,把硬件资源或设备驱动程序都隐藏在硬件抽象层下。比方说,在这些系统中,如果分配一段物理存储,应用并不知道它的实际位置。而外核的目标就是让应用程序直接请求一块特定的物理空间,一块特定的磁盘块等等。系统本身只保证被请求的资源当前是空闲的,应用程序就允许直接存取它。既然外核系统只提供了比较低级的硬件操作,而没有像其他系统一样提供高级的硬件抽象,那么就需要增加额外的运行库支持。这些运行库运行在外核之上,给用户程序提供了完整的功能。理论上,这种设计可以让各种操作系统运行在一个外核之上,如Windows和Unix。并且设计人员可以根据运行效率调整系统的各部分功能。现在,外核设计还停留在研究阶段,没有任何一个商业系统采用了这种设计。几种概念上的操作系统正在被开发,如的Nemesis,的Citrix系统和的一套系统。麻省理工学院也在进行着这类研究。无核TUNES Project和UnununiumOS都进行无内核的尝试。无内核的系统is not limited to a single centralizing entry point。&
Linux 排队自旋锁/内核
内核排队自旋锁(FIFO Ticket )是 Linux 内核 2.6.25 版本中引入的一种新型自旋锁,它解决了传统自旋锁由于无序竞争导致的“公平性”问题。本文详细介绍了排队自旋锁的设计原理和具体实现,并与 Windows 操作系统采用的类似技术进行比较。
自旋锁(Spinlock)是一种 Linux 内核中广泛运用的底层同步机制。自旋锁是一种工作于多处理器环境的特殊的锁,在单处理环境中自旋锁的操作被替换为空操作。当某个上的内核执行线程申请自旋锁时,如果锁可用,则获得锁,然后执行临界区操作,最后释放锁;如果锁已被占用,线程并不会转入睡眠状态,而是忙等待该锁,一旦锁被释放,则第一个感知此信息的线程将获得锁。长期以来,人们总是关注于自旋锁的安全和高效,而忽视了自旋锁的“公平”性。传统的自旋锁本质上用一个整数来表示,值为1代表锁未被占用。这种无序竞争的本质特点导致执行线程无法保证何时能取到锁,某些线程可能需要等待很长时间。随着计算机处理器个数的不断增长,这种“不公平”问题将会日益严重。排队自旋锁(FIFO Ticket Spinlock)是 Linux 内核 2.6.25 版本引入的一种新型自旋锁,它通过保存执行线程申请锁的顺序信息解决了传统自旋锁的“不公平”问题。排队自旋锁的代码由 Linux 内核开发者 Nick Piggin 实现,目前只针对 x86 体系结构(包括 IA32 和 x86_64),相信很快就会被移植到其它平台。Linux 内核自旋锁的底层数据结构 raw_spinlock_t 定义如下:slock&虽然被定义为无符号整数,但是实际上被当作有符号整数使用。slock&值为&1&代表锁未被占用,值为&0&或负数代表锁被占用。初始化时&slock&被置为&1。线程通过宏&spin_lock&申请自旋锁。如果不考虑内核抢占,则&spin_lock&调用&__raw_spin_lock&函数,代码如下所示:
清单&2.&__raw_spin_lock&函数清单&3.&LOCK_PREFIX宏在多处理器环境中&LOCK_PREFIX&实际被定义为&“lock”前缀。&typedef struct {&&&&&&&&&&&&&&&&&&&&&&& &} raw_spinlock_t;static inline void __raw_spin_lock(raw_spinlock_t *lock){asm volatile("\n1:\t"&&&& LOCK_PREFIX " ; decb %0\n\t"&&&& " 3f\n"&&&& "2:\t"&&&& "nop\n\t"&&&& "cmpb $0,%0\n\t"&&&& " 2b\n\t"&&&& "jmp 1b\n"&&&& "3:\n\t"&&&& : "+m" (lock-&slock) : : "memory");}&#ifdef CONFIG_SMP#define LOCK_PREFIX \".section .smp_locks,\"a\"\n" \_ASM_ALIGN "\n" \_ASM_PTR "661f\n" /* address */ \".previous\n" \"661:\n\ "#else /* ! CONFIG_SMP */#define LOCK_PREFIX ""#endif&
linux内核文件的区别/内核
内核vmlinux编译出来的最原始的内核文件,未压缩。
zImage是经过gzip压缩后的文件。bzImage bz表示“big zImage”,不是用bzip2压缩的。两者的不同之处在于,zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么采用zImage或bzImage都行,如果比较大应该用bzImage。uImageU-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的tag。是bzImage/zImage文件的或指向bzImage/zImage的链接。是“initial ramdisk”的简写。一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态。
发展改进/内核
源代码管理历史上,从来没有出现过用于Linux内核的正式的源代码管理或修正控制系统。实际上,很多开发者实现了他们自己的修正控制器,但是并没有官方的LinuxCVS档案库,内核让检查加入代码,并让其他人可以由此获得代码。修正控制器的缺乏,常常会使发行版本之间存在“”,没有人真正知道加入了哪些改变,这些改变是否能很好地融合,或者在即将发行的版本中哪些新内容是值得期待的。通常,如果更多的开发者可以像了解他们自己所做的改变一样了解到那些变化,某些问题就可以得到避免。由于缺乏正式的修正控制器和源代码管理工具,使得很多人提议使用一个名为BitKeeper的产品。BitKeeper是一个源代码管理系统,很多内核开发者已经成功地将其应用于他们自己的内核开发工作中。最初的2.5内核发布后不久,LinusTorvalds开始试用BitKeeper,以确定它是否能满足他的需要。现在,主要的2.4和2.5内核的Linux内核源代码都是用BitKeeper来管理的。对大部分可能很少或者根本不关心内核开发的用户来说,这一点看起来可能无关紧要。不过,在一些情况下,用户可以受益于那些由于使用BitKeeper而带来的开发Linux内核的方法的改变。使用BitKeeper的最大好处之一是补丁的融合。当多个补丁应用于同一的代码之上,并且其中一些补丁会对同一部分产生影响时,就可能会出现融合问题。一个好的源代码管理系统可以自动地完成其中一些更为复杂的部分工作,这样可以更快地融合补丁,并使更多的补丁加入到内核中。随着开发者社区的扩大,非常需要修正控制器来帮助保持对所有改变的追踪。由于每个人都可以将这些改变集成到主要的Linux内核中,为保证补丁不会被遗忘并可以方便地融合和管理,BitKeeper等工具是必不可少的。非常有必要使用一个实时的、集中的档案库来保存对Linux内核的最新更新。每一个被内核接受的改变或者补丁都被作为一个改变集被追踪。终端用户和开发者可以保存他们自己的源文件,并根据需要可以通过一个简单的命令用最新的改变集进行更新。对开发者来说,这意味着可以始终使用最新的代码拷贝。测试人员可以使用这些逻辑的改变集合来确定哪些变化导致了问题的产生,缩短调试所需要的时间。甚至那些希望使用最新内核的用户也可以直接利用实时的、集中的档案库,因为现在一旦他们所需要的部件或缺陷修复加入到内核中,他们就可以马上进行更新。当代码融合到内核时,任何用户都可以提供关于这些代码的即时反馈和缺陷报告。并行开发内核随着Linux内核的成长,变得更加复杂,而且吸引更多开发者将注意力集中到内核的特定方面的专门开发上来,出现了另一个开发Linux方法的有趣改变。在2.3内核版本的开发期间,除了由LinusTorvalds发行的主要的一个内核树之外,还有一些其他的内核树。在2.5的开发期间,内核树出现了爆炸式的增长。由于使用管理工具可以保持开发的同步并行进行,这样就可能实现开发的部分并行化。为了让其他人在他们所做的改变被接受之前可以进行测试,有一些开发需要并行化。那些保持自己的树的内核维护者致力于特定的组件和目标,比如内存管理、NUMA部件、改进扩展性和用于特定体系结构的代码,还有一些树收集并追踪对许多小缺陷的纠正。
Linux2.5开发树这种并行开发模型的优点是,它使得需要进行重大改变的开发者,或者针对一个特定的目标进行大量类似改变的那些开发者可以自由地在一个受控环境中开发,而并不影响其他人所用内核的稳定性。当开发者完成工作后,他们可以发布针对Linux内核当前版本的,以实现到此为止他们所完成的改变。这样,社区中的测试人员就可以方便地测试这些改变并提供反馈。当每一部分都被证明是稳定的之后,那些部分可以单独地,或者甚至同时全部地,融合到主要Linux内核中。在实际应用中测试过去,Linux内核测试方法围绕开放源代码开发模型进行。由于代码一经发布后就公开给其他开发者进行审查,因此从来没有出现过一个与其他形式的软件开发类似的正式的验证周期。这种方法背后的理论依据是“TheCathedralandtheBazaar”中所谓的“Linus法则”,这一法则的内容为“众人的眼光是雪亮的”。换句话说,高力度的审查可以找出大部分真正的大问题。然而实际上,内核有很多复杂的相互联系。即使进行了足够力度的审查,还是会漏过很多严重的缺陷。此外,最新的内核一经发布,终端用户可以下载并使用。2.4.0发布时,中很多人都提议进行更有组织的测试,以保证特定测试和代码审查的强度。有组织的测试包括运用测试计划、测试过程中的可重复性等等。使用所有的三种方法比最初只使用两种方法会带来更高的代码质量。
Linux测试项目/内核
内核最早对Linux开始进行有组织测试的贡献者是Linux测试项目(LinuxTestProject,LTP)。这个项目的目的是通过更有组织的测试方法提高Linux的质量。这个测试项目的一部分是自动测试套件的开发。开发的主要测试套件也叫做Linux测试项目。2.4.0内核发布时,LTP测试套件只有大约100个测试。随着2.4和2.5版本Linux的发展与成熟,LTP测试套件也正在发展和成熟。当前,Linux测试项目包括超过2000个测试,而且这个数字还在增长!代码覆盖分析现在所使用的新工具为内核提供了代码覆盖分析的功能。覆盖分析告诉我们,在一个给定的测试运行时,内核中哪些行代码被执行。更重要的是,覆盖分析提示了内核的哪些部分还根本没有被测试到。这个数据是重要的,因为它指出了需要再编写哪些新来测试内核的那些部分,以使内核可以得到更完备的测试。持续多日的内核回归测试在2.5的开发周期中,Linux测试项目所采用的另一个项目是,用LTP测试套件对Linux内核执行持续多日的回归测试。人们用BitKeeper创建了一个实时的、集中的档案库,以随时可以获得Linux内核的快照。在没有使用BitKeeper和快照时,测试人员不得不等到内核发布后才可以开始测试。现在,内核只要发生了改变,测试人员就可以进行测试。使用自动化工具来执行持续多日的回归测试的另一个优点是,和上一次测试相比变化较小。如果发现了一个新的回归缺陷,通常会容易地检测出这个缺陷可能是哪个改变导致的。同样,由于是最新的改变,因此它在开发者的脑海中印象还比较深——希望这能让他们更容易地记起并修订相应的代码。或许应该有这样一个结论,有一些缺陷比其他缺陷更容易被发现,因为那些正是持续多日的内核回归测试所发现并处理的那些。在开发周期中和实际发布之前能够每天进行这些测试,这就使那些只关注完整发行版本的测试者可以将精力集中于更严重和耗时的缺陷。可扩展测试平台内核另外一个名为开放源代码开发(OpenSourceDevelopmentLabs,OSDL)的团队也为Linux测试做出了重要的贡献。2.4内核发布后不久,OSDL创建了一个叫做(ScalableTestPlatform,STP)的系统。STP是一个自动化的测试平台,让开发者和测试者可以运行之上的系统所提供的测试。开发者甚至可以使用这个系统来测试他们自己的针对内核的补丁。可扩展测试平台简化了测试的步骤,因为STP可以构建内核、设置测试、运行测试,并收集结果。然后得到结果以进行深入地比较。很多人无法接触大型系统,比如具有8个处理器的SMP机器,而通过STP,任何人都可以在像这样的大型系统上运行测试,这个系统(STP)的另一个好处就在于此。追踪缺陷自从2.4发布以来,对Linux内核的有组织测试最大的改进之一是缺陷追踪。过去,在Linux内核中发现的缺陷会报告给Linux内核邮件列表,报告给特定组件或者特定体系的邮件列表,或者直接报告给维护发现缺陷的那部分代码的个人。随着开发和测试Linux的人数的增加,这个系统的不足之处很快就暴露了出来。在以前,除非人们对缺陷的报告可以惊人地维持下去,缺陷经常被遗漏、遗忘或者忽略。现在,安装了一个缺陷追踪系统(请参阅参考资料中的链接),来报告和追踪Linux内核的缺陷。系统经过了配置,这样当某个组件的缺陷被报告时,那个组件的维护者就会得到通知。维护者既可以接受并修复那个缺陷,或重新指定缺陷(如果最终确定实际上那是内核另外一部分的缺陷),也可以排除它(如果最终确定并不是真正的缺陷,比如错误配置的系统)。报告给邮件列表的缺陷还有丢失的危险,因为越来越多的涌向那个列表。然而,在缺陷追踪系统中,始终有对每一个缺陷及其当前状态的记录。大量信息在为将来的2.6Linux内核进行开的过程中,除了这些自动化的信息管理方法之外,开放源代码社区的不同成员还收集和追踪了数量惊人的信息。例如,在KernelNewbies站点上创建了一个状态列表,来保持对已经提出的内核新部件的。这个列表包含了以状态排序的条目,如果它们已经完成了,则说明它们已经包含在哪个内核中,如果还没有完成,则指出还需要多长时间。列表上很多条目的链接指向大型项目的Web站点,或者当条目较小时,链接指向一个解释相应部件的电子邮件信息的拷贝。
版本历史/内核
内核Linux的第一个公开版本是1991年10月的0.02版本。两个月以后,在1991年12月,Linus发布了0.11,这是第一个可以不依赖于Minix就可以使用的独立内核。0.12版本发布一个月以后,在3月,版本号跳到了0.95,反映出系统正变得成熟。不仅如此,直到两年后,也就是1994年3月,具有里程碑意义的1.0.0才完成。
大约从这时起开始使用两“路”编号方法标注内核的开发。偶数号的内核(比如1.0、2.2、2.4,现在是2.6)是稳定的,“产品”型号。同时,奇数号的内核版本(1.1、2.3)是前沿的或者“发展中的”内核。直接最近,一个稳定的内核发布以后几个月就开始新内核的开发工作。然而,2.5的开发工作是在2.4完成后几个月以后才开始的。“post-halloween文档”告诉用户即将到来的2.6内核有哪些可期待的东西。post-halloween文档的大部分讨论内容是用户需要注意的主要改变,以及需要更新的。关心这一信息人的主要是那些期望提前了解2.6内核中有哪些内容的Linux发行商,还有用户,这可以让他们确定为了能利用新部件是否有需要升级的程序。KernelJanitors项目保持了(实际上现在还在保持)一个列表,内容是需要修复的较小缺陷和解决方法。这些缺陷解决方法中大部分是由于向内核打较大的补丁时需要改动很多部分代码而导致的,比如有些地方会影响设备驱动程序。那些新近从事内核开发的人开始时的工作可以选择列表中的条目,这样让他们可以通过小项目学习如何编写内核代码,同时有机会为社区做出贡献。还有,在另一个预发布的项目中,追踪了在对每个已经发布的内核版本进行编译时发现的错误和警告。这些编译统计数字随着时间的流逝一直持续下降,而且,以系统的 内核形式来发布这些结果使得所取得的进展一目了然。在很多情况下,可以像使用KernelJanitors列表一样来利用这些警告和错误消息中的一部分,因为错误通常是由小的缺陷引起的,需要一些努力去修复。最后,还有AndrewMorton的“must-fix”列表。由于他已经被选定为2.6内核发布后的维护者,他运用他的特权概括地列出了那些他认为在最终的2.6内核发布前最迫切需要解决方案的问题。must-fix列表中包含了内核Bugzilla系统中的缺陷,需要完成的部件,以及其他已知的问题,这些问题如不解决将阻碍2.6发布。这一信息可以帮助指明在新内核发布前还需要哪些步骤;对那些关心这一万众期待的2.6内核发布何时能完成的人来说,它还可以提供有价值的信息。
2.6内核发布以后,这些资料中有一些已经明显不再进行维护了。其他的相关工作在主要版本发布后仍未结束,还要继续进行后期的更新。有趣的是能看到哪些又被重新提起,有了哪些革新,我们又一次接近了一个主要发布版本。多数人在考虑内核的一个新的稳定版本时,第一个问题通常是“这一版本中有什么新东西吗?”实际上除了一些新特性和修复之外,在还有一个随着时间而不断改进的过程。在Linux社区中,开放源代码开发日益兴旺。致力于Linux内核和其他方面工作的编码者之间联系是松散的,这就使得团队可以成功地适应变化。在许多方面,相对于已经完成的很多单个的改进和缺陷修复而言,Linux的开发和测试方法——尤其是这些方法随时间的推移得到了改进——对新内核的可靠性影响更为深远。&
万方数据期刊论文
万方数据期刊论文
西安交通大学学报
万方数据期刊论文
计算机研究与发展
&|&相关影像
互动百科的词条(含所附图片)系由网友上传,如果涉嫌侵权,请与客服联系,我们将按照法律之相关规定及时进行处理。未经许可,禁止商业网站等复制、抓取本站内容;合理使用者,请注明来源于。
登录后使用互动百科的服务,将会得到个性化的提示和帮助,还有机会和专业认证智愿者沟通。
此词条还可添加&
认领可获得以下专属权利:
编辑次数:26次
参与编辑人数:15位
最近更新时间: 07:33:28
贡献光荣榜

我要回帖

更多关于 自制操作系统内核 的文章

 

随机推荐