星期五, 二月 23, 2007

再读《人月神话》笔记

《人月神话》
1.在众多软件项目中,缺乏合理的进度安排是造成项目滞后的最主要原因,它比其他所有因素加起来的影响还要大。导致这种灾难如此普遍的原因是:

a: 我们对估算技术缺乏有效的研究,更加严肃地说,它反映了一种悄无声息,但并不真实的假设-----一切都将运作良好。
1:所有的编程人员都是乐观主义者(这次它肯定会运行,我刚刚找到最后一个bug)
2:进度安排背后的第一个错误的假设是:一切都将运作良好,每一项任务仅花费它所“应该”花费的时间
3:在单个任务中,“一切都将运转正常”的假设在进度上具有可实现性。因为所遇到的延迟是一个概率。
4:分布曲线,“不会延迟”具有限定的概率,所以现实情况可能会像计划安排得那样顺利。然而大型的编程工作,或多或少包含了很多任务,某些任务间还具有前后的次序,从而一切正常的概率变得非常小,甚至接近于无。

b: 我们采用的估算技术隐含地假设人和月可以互换,错误地将进度与工作量相混淆。
1:成本的确随开发产品的人数和时间的不同,有着很大的变化,进度却不是如此。
2:用人月作为衡量一项工作的规模是一个危险和带有欺骗性的神话。
3:人数和时间的互换仅仅适用于以下情况:某个任务可以分解给参与人员,并且它们之间不需要相互的交流。这在割小麦或收获棉花的工作中是可行的。
4:无论哪个母亲,孕育一个生命都需要10个月。
5:对于可以分解,但子任务之间需要相互沟通和交流的任务,必须在计划工作中考虑沟通的工作量。因此,相同人月的前提下,采用增加人手来减少时间得到的最好情况,也比未调整前要差一些。沟通所增加的负担由两个部分组成,培训和相互交流。培训增加的工作量(不能分解)随人员的数量呈线性变化。相互交流的工作量按照n(n-1)/2递增。

c: 由于对自己的估算缺乏信心,软件经理通常不会有耐心持续地估算这项工作。
在进度安排中,顺序限制所造成的影响,没有哪个部分比单元测试和系统测试所受到的牵涉更彻底。
进度安排参考法则:
1/3计划
1/6编码
1/4构件测试和早期系统测试
1/4系统测试,所有的构件已完成
在许多重要的方面,上面的进度安排与传统的方法不同:
分配个计划的时间比平常的多。即便如此,只是勉强够产生详细和稳定的计划规格说明,并不足以容纳对全新技术的研究和探索。
对所完成代码的调试和测试,投入近一半的时间。
容易评估的部分,如编码,仅仅分配了1/6的时间。研究发现,很少有项目允许为测试分配一半的时间。但大多数项目的测试实际上是花费了进度中一半的时间。他们中许多项目,在系统测试之前还能保持进度。

d: 对进度缺少跟踪和监督。
 非阶段化方法的采用,少得可怜的数据支持,加上完全借助软件经理的知觉,这样的方法很难生产出有力的,看似可靠的和规避风险的估计。
 开发并推行生产率图表、缺陷率图表、估算规则等等,而整个组织最终会从这些数据的共享上获益。

e: 当意识到进度的偏移时,下意识(以及传统)的反应是增加人力。这就像使用汽油灭火一样,只会使事情更糟。
 项目的时间依赖于顺序上的限制。人员的最大数量依赖于独立子任务的数量。



2. 需要协作沟通的人员数量影响着开发成本,因为成本的主要组成部分是相互沟通和交流,以及更正沟通不当所引起的不良结果(系统调试)。这一点也暗示,系统应该有尽可能少的人员来开发。



3. 对于效率和概念的完整性来说,最好有少数干练的人员来设计和开发,而对于大型系统,则需要大量的人手,以使产品能在时间上满足要求,如何调和这两方面的矛盾。Mills的建议:大型项目的每一个部分由一个团队解决,但是该团队以类似外科手术的方式组建,而并非一拥而上。也就是说,同每一成员截取问题某个部分的做法相反,由一个人来完成问题的分解,其他人给予他所需要的支持,以提高效率和生产力。



4. 外科手术团队:
 外科医生(首席程序员):定义功能、设计、编码、测试、要很高经验,熟悉业务
 副手:外科医生的保险机制。提供设计、编码、功能的建议,不负责具体的事务
 管理人员:管理财务、人员、工作地点、办公设备,听从外科医生
 编辑:文档的生成,维护。来自于外科医生的口述或者草稿。
 两个文秘:管理人员和编辑各配一个文秘。负责项目的协作一致。
 程序职员:负责团队所有的程序和数据的管理。
 工具维护人员:保证所有基本服务的可靠性,一致性以及构建,升级,维护。
 测试人员:编写测试用例和准备测试数据。
 语言专家:语言方面的专家,咨询。解决复杂,棘手的问题。



5. 对于计算机系统而言,出现概念差异和不一致性的问题,是由于设计被分成了由若干人完成的若干任务。所以需要具备设计上的完整性,就需要一个系统架构师从上之下的进行所有的设计。要使工作易于管理,必须清晰地划分体系结构设计和实现之间的界线,系统架构师必须一丝不苟的专注于体系结构。(本文的体系结构指的是完整和详细的用户接口说明,更偏重于现今的需求概念,不是指系统的框架。)



6. 功能与理解上复杂程度的比值才是系统设计的最终测试标准,也就是易用性。而易用性需要设计的一致性和概念上的完整性来保证。体系结构和实现必须要严格分离开来。产品的成本性能比很大程度上依靠实现人员,就如同易用性很大程度上依赖架构师一样。两者都有需要很高的创造性。



7. 系统架构规定实际上是增强,而不是限制了实现小组的创造性。一旦他们将注意力集中在没有人解决过的问题上,创意就开始奔涌而出。在毫无限制的实现小组中,在进行系统架构上的决策时,会出现大量的想法和争议,对具体实现的关注反而会比较少。



8. 当建议由小型体系结构团队来编写系统的所有外部技术说明时,编程人员常常会提出这样的反对意见:
 该说明中的功能过于繁多,而对实际情况中的成本考虑比较少。
 警惕second-system effect。在设计第一个项目时,架构师会面对不断产生的装饰和润色功能,但这些功能都会被搁置在一边,作为“下一个”项目的内容。第二个系统是设计师们所设计最危险的系统。而当着手第三第四个系统时,先前的经验会相互验证,得到对此类系统通用特性的判断,而且系统之间的差异会帮助他识别出经验中不够通用的部分。
 second-system effect:一种普遍倾向是过分的设计第二个系统,向系统中添加很多修饰功能和想法。另一种倾向是对某些技术进行细化、精炼的趋势,由于基本系统设想发生了变化,这些技术已经显得落后。
 架构师获得了所有创造发明的快乐,剥夺了实现人员的创造力。
 具体实现中创造和发明的机会,并不会因为指定了外部技术说明而大为减少,相反创造性活动会因为规范化而得到增强,整个产品也是一样。
 当体系结构的队伍缓慢工作时,很多实现人员只能空闲的坐着等待。
 在外部技术说明完成的时候,才雇佣实现人员。 ;)



9. 把工作进行垂直划分,保证概念的完整性;尽量避免水平划分。



10. 文档化的规格说明----手册:手册是产品的外部规格说明,它描述和规定了用户所见的每一个细节;同时它还避免描述用户看不见的事物。后者是编程实现人员的工作范畴,而这里他的设计和创造自由是不应该被限制的。体系结构设计人员必须为自己描述的任何特性准备一种实现方法,但是他不应该试图支配具体的实现过程。



11. 周例会是每周半天的会议,有所有的架构师,加上硬件和软件实现人员代表和市场计划人员参与,由首席系统架构师主持。会议中,任何人可以提出问题和修改意见,但是建议书通常是以书面的形式,在会议之前分发。周例会的决策会给出迅速的结论,使工作继续开展下去。这种会议的卓有成效是在于:
 数月内,相同小组-----架构师、用户和实现人员-----每周交流一次。因此大家对项目相关的内容比较了解,不需要安排额外时间对人员进行培训。
 上述小组十分睿智和敏锐,深刻理解所面对的问题,并且与产品密切相关。没有人是“顾问”的角色,每个人都要承担义务。
 当问题出现时,在界线的内部和外部同时寻找解决方案。
 正式的书面建议集中了注意力,强制了决策的制定,避免了会议草稿纪要方式的不一致。
 明确的授予首席架构师决策的权力,避免了妥协和拖延。



12. 电话日志:日志中,它记录了每一个问题和相应的回答。QA表



13. 产品测试:项目经理最好的朋友就是他每天要面对的对手----独立的产品测试机构/小组。每一个开发机构都需要这样一个独立的技术监督部门,来保证其公正性。测试人员不时地总会发现一些没有贯彻执行、设计决策没有正确或准确实现的地方。出于这方面的原因,设立测试小组是使设计决策得以贯彻执行的必要手段,同样也是需要尽早着手,与设计同时实施的重要环节。

敏捷方法书籍(转载)

“敏捷软件开发宣言:我们正在通过亲身实践和帮助其他人实践,揭示更好的软件开发方法,通过这项工作,我们认为:
人和交流胜过过程和工具
可工作的软件胜过面面俱到的文档
客户协作胜过合同谈判
响应变化胜过遵循计划
虽然右项也有价值,但是我们认为左项更重要。”
—— Kent Beck,Mike Beedle,Arie van Bennekum,Alistair Cockburn,Ward Cunningham,Martin Fowler,James Grenning,Jim Highsmith,Andrew Hunt,Ron Jeffries,Jon Kern,Brian Marick, Robert C. Martin,Steve Mellor,Ken Schwaber,Jeff Sutherland,Dave Thomas

敏捷软件开发这个词在2006年的中国软件界听起来仍然显得有些陌生。自2001年敏捷联盟被发起以来,敏捷方法的实践经验和理论研究都在不断的更新。而我国的大多数程序员还是只能在书本上读到敏捷的好处,很难在项目中进行实践。这其中的原因,主要是缺乏拥有实际敏捷项目经验的人来带领实施敏捷。虽然敏捷开发是种实践行为,很难从书本上直接学习,不过多数程序员了解敏捷,却都是先从书本开始的。无论结果怎样,从认识到实践的过程是免不了的。

敏捷软件开发之方法论篇
大家都知道敏捷软件开发方法包括了多种方法论,主要有:SCRUM,Crystal,特征驱动软件开发(FDD),自适应软件开发(ASD),以及最著名的极限编程(XP)。这些方法论分别在不同的著作上专门论述过:
SCRUM:《Agile Software Development with Scrum》 by Ken Schwaber, Mike Beedle,《Agile Project Management With Scrum》by Ken Schwaber
FDD: 《Java Modeling in Color with UML》by Peter Coad, 《A Practical Guide to Feature-Driven Development》(特征驱动开发) by Stephen R Palmer, John M. Felsing,
Crystal: 《Crystal Clear》by Alistair Cockburn
ASD: 《Adaptive Software Development》(自适应软件开发)by James A. Highsmith

其中尤以XP系列的书籍居多。人民邮电出版社的一系列极限编程系列丛书,在国内引进较早。在还没有统一敏捷词汇的情况下,引发了一批敏捷先锋人士的热情,是我国程序员的敏捷启蒙教材。这些书包括《Extreme Programming Explained》(解析极限编程),《Extreme Programming Examined》(极限编程研究),《Extreme Programming Installed》(极限编程实施),《Extreme Programming Explored》(探索极限编程),《Extreme Programming Applied》(应用极限编程)《Extreme Programming in Practice》(极限编程实践),《Planning Extreme Programming》(规划极限编程)等,这些书有的是作者的XP实践论文,有些是对XP项目的介绍,其中,值得推荐的是下面两部著作。

《Extreme Programming Explained: Embrace Change》by Kent Beck
第一版中译版:《解析极限编程:拥抱变化》,唐东铭,人民邮电出版社
第二版中译版:雷剑文,电子工业出版社
作为XP的开山之作,目前已经出版了第二版。在第一版中,Kent Beck对XP作了详细的描述。从当前软件开发的现状和问题谈起,从需求的变化到如何拥抱变化,给出了XP的四项价值观和十二项实践。对于想了解敏捷的来龙去脉的人,此书属于必读之类。在第二版,Kent根据几年来的实践,为XP增加了一项价值观:尊重,并增加了原则的概念,同时增加和删改了一些实践。
该书第一版是程序员的宣言,这和Kent的背景很有关系。随后XP经历了五六年的发展和实践,Kent自己也逐渐意识到,这样的观点太狭隘了。因此就有了第二版,与其说这是技术书籍,到更像是纯粹意义的软工书籍。期间也可以看出XP的体系更加完备。这其中尤为突出的是把人放到了更为重要的地位。

《Extreme Programming in Practice》by James Newkirk, Robert C. Martin
中译版:《极限编程实践》,王钧,人民邮电出版社
读过了一些列的XP书籍,程序员们都会觉得XP非常好,但到底如何才能开始实施XP呢?还不是太清楚。本系列中的这本书用一个完整的小项目作例子,从头到尾教给人如何敏捷开发,是一本不可多得的实践教材。如果想直接实施XP开发,这本书可以给你很大启示。

敏捷软件开发之实践篇
一、极限编程最佳实践
由于极限编程是如此的流行,多数敏捷团队都会或多或少的借鉴一些XP中的敏捷实践,而XP的每一个敏捷实践也确实值得大书特书,而其中最著名的是测试驱动开发和重构实践:

《Test-Driven Development》 by Kent Beck
中译版:《测试驱动开发》,崔凯,中国电力出版社
测试驱动开发是Kent Beck另一部力作。“Clean Code That Works”是敏捷开发的目标之一,那么如何达到这个目标?TDD给出了一种方式。测试实质上是需求。由需求产生出的代码肯定是能够工作的功能代码,而要实现Class本身的可测试性,就不得不写出高度解耦合的Clean的代码。本书从一个Money的例子入手,从最初的一点需求开始,逐步增加需求,完成整个货币系统的代码。后面又给出了Unit Test中的一些最佳实践和模式供参考。
然而,本书的教导意义比其实践意义更突出。作为一本TDD的教程或入门教材,这本书无疑是最佳的,其中提出的一些最佳实践更是值得经常阅读来温习。本书面向的是单元测试,而实际开发中面对的数据库测试,Web测试等问题并不属于单元测试的范畴。因此读者并不能从中直接进入到实战。
另一本同名书《Test Driven Development: A Practical Guide》由Davis Astels撰写,他将该书看作是Kent著作的补充,重点阐述利用TDD开发所必要的技术和工具上,因此对实际开发更具实用性。

《Refactoring: Improving the Design of Existing Code》by Martin Fowler
中译版:《重构:改善既有代码的设计》,侯捷,熊节,中国电力出版社
重构这本书的意义在于,他提供了一种让你写出更加优美代码的能力。在测试的保证下,重构能够发挥强大的威力。敏捷团队中,不断的重构出简单且高效的代码才能够保持拥抱不断变化的需求。后来的一本书《Refactoring to Patterns》(从重构到模式)by Joshua Kerievsky,更是将重构的威力发挥到极限。
重构曾被称为软件开发图书的双璧,另一本书是《Design Patterns》(设计模式) by GoF。当然,对现在的软件开发这二者已经不是最重要的。ThoughtWorks的首席科学家Martin Fowler总结了朋友们的各种实践心得,写出了这本书。从几年后的目光来看,这本书中的多数实践都被各种IDE做到了操作菜单中。虽然IDE提供了大量重构功能,但仅靠IDE是无法写出简洁美妙代码的,多数的敏捷团队重构工作做得还是不够。

另外有一本专门介绍结对编程的书,《Pair Programming Illuminated》(结对编程技术)by by Laurie Williams and Robert Kessler,指出了为什么要结对?并从各种不同水平不同性格的程序员结对情况来讨论该实践的优劣。对此有兴趣的程序员不妨一读。

二、敏捷软件开发实践
自从2001年敏捷联盟成立以来,单独推广极限编程的书变少了,而统一口径推广敏捷的书变得越来越多。两本同名的敏捷软件开发都是不可多得的好书,

《Agile Software Development:Principles, Patterns, and Practices》by Robert C. Martin
中译版:《敏捷软件开发:原则,模式与实践》,邓辉,清华大学出版社
被业内人士称为Uncle Bob的Robert C Martin在沉寂几年后写出了这部书。该书可以算是从软件开发角度对敏捷方法阐述的最详细和全面的一本。之前的敏捷书籍多是关注于过程改进,而对如何从技术角度实施讲的比较少。本书一开始先介绍了敏捷联盟和敏捷开发过程。之后详细论述了面向对象设计的原则,这些原则是本书的精华之一。后面通过几个项目介绍了如何将设计模式应用于项目中。
Uncle Bob不愧是实践的大师,写出来的书也是拥有很强的实践意义。在敏捷团队的办公桌上,应当常备此书,一来可作为参考查询,二来可以作为新成员的必读书目。

《Agile Software Development》by Alistair Cockburn
中译版:《敏捷软件开发》,俞涓,人民邮电出版社
这本书更加适合管理者来阅读。Alistair从项目人数和交流难易程度,将敏捷的各种方法划分了其适用范围。人数多的或分布式项目就需要靠其他手段来加强交流,人数少的就可以靠pair programming等进行面对面的交流。交流和反馈是敏捷的核心。同时Alistair也介绍了一下他提出的Crystal方法族。

三.敏捷项目管理和敏捷需求分析
在推广敏捷一段时间后,敏捷社群也意识到,多数书籍更像是面向开发人员,过于技术化,难以吸引项目经理或主管。因此,一批面向管理者视角的书也开始浮出水面,这些书包括:
《Agile and Iterative Development》(敏捷迭代开发)by Craig Larman
《Lean Software Development》(敏捷软件开发工具—精益开发方法)by Mary Poppendieck
《Agile Software Development Ecosystems》(敏捷软件开发生态系统)by Jim Highsmith
书中从各种角度比较和分析各种敏捷方法的优劣,异同,起源,适用范围等。这些书对于一个项目主管决策使用何种过程来在自己的团队中实践敏捷有很好的参考作用。

近两年,人们开始逐渐意识到敏捷开发的侧重点不仅仅是开发过程和开发实践,还包括对需求和项目管理等其他相关方面的实践。一些相关的书籍也悄然出现在人们的视野:
《Agile Project Management》(敏捷项目管理)by Jim Highsmith
《User Stories Applied》by Mike Cohn
《Agile Estimating and Planning》by Mike Cohn
《Agile Requirements & User Stories》 by Louis Molnar
这些书不同于以往强调新方法,新过程的书目。敏捷项目管理类的书主要介绍如何管理敏捷团队,如何计划要开发的需求,如何为客户提供最大的价值。介绍敏捷需求分析的书主要帮助商务分析师或项目经理挖掘和分析用户需求,写出用户故事,评估和计划用户故事等。人们已经意识到,各种方法论的实质是相同的,都是提供商业价值,减少浪费,增加交流,快速反馈。因此不需要着重于区分是使用了那种方法。对项目经理来说,不同的项目或团队应当采用适应其特殊情况的方法,而这些方法的基本原则是相同的。

四.敏捷软件开发新方向
对架构师或程序员来说,近年来的技术进展,也使得敏捷开发有了新的研究方向:
《Agile Web Development with Rails》by Dave Thomas, David Hansson, Leon Breedt, and Mike Clark
该书是获得2006JOLT奖的书,讲得是采用Ruby on Rails这个Web开发工具新贵来快速开发Web项目,从而达到快速反馈拥抱变化的目的。
《Refactoring Databases》by Scott W Ambler
此书是Scott的新作,延续和继承了《Agile Modeling》(敏捷建模)和《Agile Database Techniques》(敏捷数据)的思想。在敏捷开发过程中,作为持久化最常见技术的数据库如果不能够敏捷,怎么能够适应一次次迭代和一次次发布的修改呢?书中介绍了如何进行数据库演化,如何保证升级后数据库数据的正确性,以及最佳实践。

我们可以看到,随着敏捷方法和市场的不断成熟,敏捷的书籍也从理论性转向了实用和最佳实践类型。然而,不可否认的是,一个团队的敏捷化很难仅靠阅读书本来完成,由成功实践过敏捷的开发者手把手的带领,才是最好的方法。

星期四, 二月 08, 2007

漫谈创业和管理-程序员5大思维障碍(转载)

程序员是最容易创业的,或者说是创业成本最低的职业。只要有一台电脑和投入自己的时间,就可以写出畅销天下的软件,这是每个程序员的梦想。更何况世界首富常年以来就是程序员出身的比尔盖茨,这也刺激了更多的程序员走上创业之路。
可是等到真的开始创业,才发现这条路并不容易.由于创办CSDN网站和《程序员》杂志的原因,接触了大量的技术创业者,或者从技术转向管理的程序员。我发现真正程序员创业成功的例子非常罕见,我自己也曾经创业三次,经历了很多的挫折和失败。
我总结了一下,由于程序员的思维习惯给创业或者管理带来的障碍:(为什么要谈管理,因为真正创业做企业,靠一个人是不行的,必须有团队,团队如何管理就是第一步创业的挑战)

程序员思维定势:
1)机器思维
优秀的程序员最擅长和电脑程序打交道,并通过代码去控制反馈。而管理需要和人打交道,需要收集人的反馈。电脑是按逻辑来执行的,而人却要复杂很多,特别是团队中有女性成员,挑战难度就更大。
由于长期和电脑接触,很多程序员缺乏和别人沟通的技巧,或者说情商相对较低。这在管理上是比较致命的缺点。

2)BUG思维
优秀的程序员追求完美,看自己或者别人代码时第一有限是看什么地方可能有BUG, 管理时如果带着BUG思维,就会只看到别人的不足和错误,而不去表扬其有进步的地方。(完美思维的坏处还有一个,就是过于关注细节)如果方向和前提有问题,过于关注细节反而会带来延误
3)工匠思维
程序员靠手艺吃饭,创业总是会碰到各种困难,在碰到困境的时候程序员出身的创业者是有退路的,大不了我再回去写程序搞技术好了。有时候创业就是差那么一步,不能坚持到底,也就不能收获果实。

4)大侠思维
以技术创业起家的容易迷信技术,忽视市场,忽视管理,总以为只有自己的是最好的。遗憾的是技术变迁实在太快,一时的先进不能代表永远的先进。先进的技术也不一定就是致胜的法宝。

5)边界思维
程序员设计代码和系统时,常常会考虑要处理边界和异常。反映到思维习惯上,就是遇到问题,就会全面的思考各种情况。这是很好的优点,但做事业时,这有时候反而会是缺点。

上面五类有不少具体例子,大家也可以看看自己的思维习惯里面是不是这样?
习惯是很难改变的,最好的处理方式是找到搭档,能弥补自己的不足,这样成功的概率才会加大。HP, Apple Microsoft, Oracle,Adobe, 都是两个主要创始人搭档创业成功的。

from http://blog.csdn.net/jiangtao/archive/2007/02/07/1504627.aspx

星期五, 二月 02, 2007

list of the coding requirements for XHTML compliance

A short list of the coding requirements for XHTML compliance

1. Declare a DOCTYPE
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
<"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
<"http://www.w3.org/TR/html4/loose.dtd">

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
<"http://www.w3.org/TR/html4/frameset.dtd">

2. Declare an XML namespace
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">

3. Declare your content type
<meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />

4. Close every tag, whether enclosing or nonenclosing

5. All tags must be nested correctly

6. Inline tags can't contain block level tags

7. Write tags entirely in lowercase

8. Attributes must have values and must be quoted

9. Use the encoded equivalents for a left angle bracket and ampersand within content

bash点滴

bash的配置文件有三个,.bash_profile .bashrc .bash_logout,它们一般就在用户home目录下。没有的情况下会使用系统默认的/etc/bashrc

1: .bash_profile:用户Login时执行,设置个人环境。
.bash_profile的修改要在用户重新Login的时候才会生效。也可以使用source命令或者.命令执行,使之生效。
bash向前兼容C shell的.login(bash中更名为.bash_login)和Bourne shell和Korn shell的.profile配置文件。当你Login的时候,这三个文件只有一个会被执行,配置文件搜索顺序:.bash_profile---> .bash_login---> .profile

2: .bashrc:用户调用shell时执行,设置调用shell的运行环境。 (bash的环境配置文件)
提供将用户的Login环境和subshell环境区分开来的灵活性。
  用户特定的aliases和functions在.bashrc里定义。避免subshell运行舒畅,尽可能把所有的定义放在这里
用户自己的特定配置和需要启动的程序在.bash_profile里给出

3: .bash_logout:顾名思义,用户Logout时执行,做一些清理工作。



Alias 为命令创建合适的名字
格式:alias name=command
bash在执行命令前,会将所有的aliases进行文本置换,所以通配符及特殊字符不能包含在alias名字中
对文件夹的名字进行alias,需要一点技巧。alias anim='/work/fisrt/second/third/fourth' cd anim的时候,系统会报错,找不到这个文件或者文件夹。需要再加一个alias cd='cd ' 或者 shopt -s cdable_vars 允许cd的参数是变量
不建议用很多aliases,shell脚本和function能够提供更强大的功能

Option
格式:set -o | set +o +表示关闭option -表示打开option(The reason for this incongruity is that the dash (-) is the conventional UNIX way of specifying options to a command, while the use of + is an afterthought)
或者使用shopt -psuqo

varible
声明:variable=value 使用:$variable 删除:unset variable(此时只有当set -o nounset的时候,才有用。因为对于bash来说,所有未定义变量的值为null(即等于''),nounset 选项使得bash在碰到一个未定义的变量时,提示错误)

在命令行执行命令时,命令的执行优先级
1: Aliases
2: Keywords such as function if for
3: Functions
4: Built-ins like cd and type
5: Scirpt and Executable programs in directories listed in the PATH

星期四, 二月 01, 2007

FC6启动过程分析——启动图形界面

上次我们说到,inittab启动到最后,使用/etc/X11/prefdm这个脚本来选择一个DM(Display Manager)来启动图形界面.这个脚本根据/etc/sysconfig/desktop中的配置来选择是该运行gdm,kdm还是xdm,默认将会使用gdm。

无论是gdm,xdm还是kdm,所做的事情都是类似的,及启动一个X窗口,基于这个X窗口提供一个图形化的用户登录界面,以便在实际进入X窗口系统之前,对用户进行验证,并且提供用户选择自己希望的语言,窗口管理器等的机会。除此之外,dm程序一般还支持别的一些操作,比如提供直接关机的选项以及根据配置决定是否打开XDMCP服务的端口等。

XDMCP服务是X窗口显示管理器控制协议的缩写,它允许用户在远程电脑上运行X窗口服务,然后通过XDMCP协议使用本地的XDM登录,登录以后的后续操作将使用远程的X窗口作为显示系统。一个很简单的例子,首先使用gdmsetup程序(管理菜单的登录屏幕)打开XDMCP的支持(远程选项卡更改为与本地相同),然后打开一个终端窗口,运行Xnest :1 -query 127.0.0.1命令(Xnest并不是默认安装的命令),你将在一个新开的窗口中看到和你的登录屏幕一模一样的登录屏幕,你可以登录其它用户,进行所有和本地用户一样的操作。显然如果你是在另外一台电脑上,只需要把相应的ip地址改掉就可以了。

并不一定非要使用Xnest程序,你甚至可以在远程的Win32系统上进行基于XDMCP的远程登录,这首先需要你在你的windows系统上运行一个X 窗口系统,有很多种类似的实现,包括X-win32和cygwin在内的各种免费和收费版本都是一个不错的选择,事实上,一台强劲的服务器可以通过这种方法可以将N台落魄的486PC转变成可以运行高级科学运算的X终端。

说到远程X终端,除了上面提到的方法,你还可以使用内置于gnome之中的vino程序,这个程序可以基于本地的X窗口打开一个兼容于vnc的服务,你可以使用各种类型的vncviewer来连接这个服务并进行远程操作(参见首选项菜单中的远程桌面),这种实现方式下,远程显示的屏幕和本地屏幕是完全相同的。或者你也可以使用单独的vncserver,这种使用方式和XDMCP的使用方法类似,只是登录的用户和使用的窗口管理器都是由vncserver指定好的。

gdm的配置定义在/etc/gdm/custom.conf中,对于其预定义配置的默认值你还可以查询/usr/share/gdm/defaults.conf文件,它们都采用了类似windows下ini文件的文件格式。

在用户选择了语言和窗口管理器以后,DM根据用户的选择设置相应的locale变量,然后运行和那个窗口管理器对应的命令。通常在语言选择菜单中,我们只能看到区域的选择,比如中国大陆,中国香港这样的选项,但是中国大陆的选项可以使用GBK的编码方式,也可以使用UTF8的编码方式,选择的区域是怎样和编码方式对应的呢?所有gdm相关的配置文件保存在/etc/gdm这个目录中,在这个目录中有一个 locale.alias文件,这个文件列出了系统支持的语言以及各个语言对应的编码方式,你可以通过更改这个文件,以便在选择中国大陆的语言时,将编码由默认的UTF8更改为GBK,当然,适应UTF8的编码方式也是一个不错的选择,如果你不需要经常的和windows环境打交道,你应该保留这个默认的设置。

除了选择语言以外,gdm还允许你选择要登录的会话. 系统内建的几个会话包括安全模式终端,安全模式gnome以及上一次的成功登录等,其它的会话则是从配置文件中读取的,gdm将会在多个目录中寻找设定的会话,包括/etc/X11/sessions/,/usr/share/gdm/BuiltInSessions/, /usr/share/xsessions/等,路径可以通过daemon/SessionDesktopDir配置项进行更改,gdm在这些目录中寻找扩展名为desktop的文件,比如默认会话对应的文件是 /usr/share/gdm/BuiltInSessions/default.desktop,而gnome会话对应的文件为 /usr/share/xsessions/gnome.desktop.这些配置文件定义了在不同的语言中这个会话要显示的名称。

当用户选择了一个对话,输入了正确的用户名和密码以后,gdm执行命令的顺序依次是,首先它将执行位于daemon/PreSessionScriptDir 配置项路径下(默认为/etc/gdm/PreSession/)的所有脚本文件,来执行启动会话前的一些任务,比如更改X窗口的默认背景之类,然后它将调用位于daemon/PostLoginScriptDir配置的目录中(默认为/etc/gdm/PostLogin)的脚本,执行一些在刚刚登录以后需要运行的命令,然后它将以前面提到的desktop文件中定义的exec参数的值作为参数,调用daemon/BaseXsession配置项指定的脚本(默认为 /etc/gdm/Xsession),比如如果你选择的是默认会话,那么执行的命令将会是/etc/gdm/Xsession default,如果你查看这个文件你将发现,在这种情况下,它将首先检查是否存在主目录的.xsession文件,如果存在就执行它,否则检查是否存在主目录下的.Xclients文件,如果存在则执行它,否则就将执行/etc/X11/xinit/Xclients文件,这个文件根据 /etc/sysconfig/desktop配置文件中的设置选择相应的命令执行,默认为执行gnome-session.

而配置在 daemon/PostSessionScriptDir配置项(默认值为/etc/gdm/PostSession/)所设定的目录中的脚本将在会话结束以后运行,这意味着无论出于什么原因,gnome程序已经完全退出了,也许是你选择了注销命令,也许是X窗口崩溃了,如果你有这方面的需要,可以将相应的脚本放在对应的目录中。

最后还有一点要说的是,gdm通常是由/etc/X11/prefdm脚本启动的,这个脚本中将会重复启动自己,因此,如果你想在运行级别5的时候完全退出X窗口,你应该将prefdm这个程序杀死,而不是简单的杀死X窗口或者是gdm。

引导器GNU GRUB简析

GNU GRUB是我见到的最灵活的、功能最强大的、加载速度最快的、最精炼的引导器。(除了GRUB,我还用过NT loader, OS/2 loader, DOS MBR loader, PQ BOOT, System Commander, Lilo等引导器)。因此,GRUB成为现在各大Linux发布版的主流引导器。

一、安装GRUB的四大要素

想玩转GRUB,必须理解GRUB的组成部分和其大概的功能、实现的思路。如下,是我的GRUB目录的文件:

menulst
stage1
stage2
e2fs_stage1_5
fat_stage1_5
ffs_stage1_5
jfs_stage1_5
minix_stage1_5
reiserfs_stage1_5
vstafs_stage1_5
xfs_stage1_5


如果你下载一个grub,编译后装上,那么产生的就是这些文件和一个从linux环境进入grub环境的程序grub,但是不包括menulst(也可能有个例子文件menulst.conf,记不清了)。这些文件是通用的,copy到别的机器上也能用,这就是一大灵活之处。

stage1是用来装入到MBR或者primary disk的super block的,它应该是16位环境引导模块;stage1引导完成后,就切换到32位保护模式,把stage2从磁盘读到内存,然后把控制权交给stage2执行。stage2会读取menulst,从而确定如何引导Linux。menulst文件中详细写明了引导菜单和内核的位置,这个纯文本文件是需要手工配置的。其余的那些文件,如e2fs_stage1_5那些,偶不说你也明白,是对应于各种类型文件系统的驱动程序,在stage1从磁盘读取stage2时会加载它先。

下面是偶得menulst,我用了最简捷的语法:

#-----------------------------------------

default 1
timeout 3

#-------------------------------
#-- Slackware Linux 10 kernel 2.4.26
title Slackware Linux 10 kernel 2.4.26
kernel (hd0,4)/boot/vmlinuz ro root=/dev/hda5
boot

#-----------------------------------------


default 指定默认引导的系统,对应序号是引导菜单的条目序号,从1开始计数。timeout 指定引导菜单延迟等待的时间,如果timeout为0你就看不到引导菜单了。title指定引导菜单上显示的文字,kernel指定引导内核和root设备的位置。这里要注意,(hd0,4)是grub指定文件设备的语法,跟Linux通用的/dev/hda5表示法类似,只不过它的数字是从0开始计数的,而/dev/hda5这种表示法是从1开始计数的。(hd0)指向/dev/hda, (hd1)指向/dev/hdb,(hd0,1)指向/dev/hda2,(hd2,6)指向/dev/hdc7,以此类推......

要注意,根设备前要加上ro,指定为只读挂载,不然后患无穷。

还有一点一定要注意,(hd0,4)/boot/vmlinuz中(hd0,4)指的是你的内核vmlinuz所在的设备根路径,和root设备未必相同(比方说我把vmlinuz放在(hd0,0)上,但是我的linux root确在(hd0,4)上),切记切记。有时候,你会看到grub中用root (hd0,0) 这里的root指的不是你的linux的root所在的设备路径,而指的是在grub中的 / 所对应的设备路径,如:
kernel (hd0,4)/boot/vmlinuz ro root=/dev/hda5
等同于:
root (hd0,4)
kernel /boot/vmlinuz ro root=/dev/hda5
这点要仔细理解。

二、灵活自如的安装方法

至少有2种方法来安装grub,1>在Linux中把grub安装包装上,会同时装一个从Linux中进入grub环境的工具grub,这样你就可以执行grub进入grub环境,然后再执行安装指令;2>从grub引导盘(很多D盘都有提供,还有那种光盘的Linux修复环境也有提供)进入grub,执行安装指令。

安装指令很简单,就1行命令:
install <要安装到那个引导位置> p

如我的,就是:
grub>install (hd0,0)/grub/stage1 (hd0) (hd0,0)/boot/grub/stage2 p (hd0,0)/grub/menulst

这里要说明一点,有很多人分不清/boot是什么意思,以至于老是在任何环境下都这么写(hd0,0)/boot/grub/stage1。这是不对的。Linux下的几乎所有目录都是可以挂载的,例如我把/boot挂载到了/dev/hda1上,这样,我写(hd0,0)/grub/stage1就相当于/boot/grub/stage1,这点一定要弄清楚!在举一个例子,如果我把/home挂载到/dev/hda7上,那么(hd0,6)/smileonce指向的就是/home/smileonce。

三、grub引导环境的使用技巧

有一些tip可能你花了很上时间才学到,比如我当初就想像不出在输入grub命令的时候,按tab键居然有提示,比如install (hd0,然后我按tab,那么会提示一堆设备列表的清单。在系统启动的时候,按c可以进入grub命令环境,按e可以编辑当前菜单条目的命令,按b重新执行boot等等。这些需要看一些grub的文档才能彻底掌握。

四、发烧级技巧

有很多人问:XX版本的Linux只有ISO能不能从硬盘安装?其实现在发行的版本(老版本可能不支持),都支持从硬盘安装,并且连ISO都不需要解压缩开。

实现原理说穿不值一钱:利用ISO一个叫做boot目录中提供的boot盘引导用的压缩内核,用grub挂载引导这个内核,而Linux安装程序又支持从ISO中寻找目录。注意,如果安装盘有几张,ISO应该除序号外名字都一致(默认你下载的光盘镜像都是这样的),这样安装程序才会自动换盘;否则就需要手动切换控制台,然后手动mount,就麻烦了。

举个例子如下:

title From HD
root (hd0,6)
kernel /isolinux/kernel devfs=nomount vga=normal load_ramdisk=1 prompt_ramdisk=0 ramdisk_size=22000 root=/dev/ram0 rw
initrd /isolinux/rescue.gz

(hd0,6)是ISO文件所在的设备位置(改成你的实际位置),把ISO放在该分区根目录。那个kernel和rescue.gz文件在你Linux安装盘的第一张盘的boot目录里面有,自己找一下,不同版本Linux可能名字不同。

FC6启动过程分析——从run level 到 log in

根据inittab的指示,在启动完rc.sysinit之后,init程序将进入相应的运行级别,并运行这个级别的脚本。 默认的运行级别也是在inittab中指定的,一般设置为3或者5,两者的区别在于是否默认进入图形模式(启动XWindow)。

启动脚本是通过/etc/rc.d/rc这个程序运行的,它做的事情也不算很复杂,首先它将根据你是否在前面的rc.sysinit的时候摁下"I"键来决定是交互启动模式还是非交互启动模式并且进行相应的输出,然后,依次运行位于相应启动级别目录(/etc/rc.d/rc启动级别.d/)中的脚本,运行的次序是,首先按照名称顺序运行那些K打头的脚本,然后按照名称顺序运行那些S打头的脚本。如果是交互启动模式,它将在运行每个S打头的脚本之前,询问你是否运行这个脚本。

由于脚本运行的顺序是按照字母顺序,你就可以理解为什么在每个脚本之前要被加上一个两位的数字,这只是为了在排列脚本执行顺序的时候显的更直观,另外,所有的启动脚本文件都存在在/etc/rc.d/init.d目录中,位于不同启动级别下的脚本是指向 /etc/rc.d/init.d目录中相应脚本的符号链接。启动脚本符号链接中的数字是怎么来的呢,它是由你指定的,如果你要增加自己的启动脚本到相应的启动级别中去,这个数字当然应该由你指定,因为只有你才知道这个脚本应该以什么样的优先级启动。但是对于那些已经存在的启动脚本,作为FC6发行的一部分,它们的优先级是在脚本中最前面的注释行中的chkconfig这一行指定的,在这一行中,你可以看到类似# chkconfig: 35 99 95的字样,它的含义是:这个脚本应该增加到运行级别3和运行级别5中,启动的优先级是99,关闭的优先级是95,当然,这些数字是由那些FC6的开发者测试过没有问题,所以才写在这里的。一个二进制的程序/sbin/chkconfig将会读取这一行,并且在将服务增加到启动级别中去的时候自动生成文件名。

文件名中的第一个字符S和K代表了什么含义呢?它代表了你在services控制面中选择了打开这个服务还是关闭这个服务,如果你在那里打开了这个服务,则以S作为前导符,否则为K,结合我们前面介绍的启动过程,你就可以知道,在启动的时候,FC6会首先保证那些K打头的脚本是关闭的(通过以stop参数调用这个脚本),然后才会逐个启动那些S打头的脚本(通过以start参数调用这个脚本)。

对于每个启动脚本文件,如果想知道启动了时候都做了些什么,可以查看相应脚本中的start()函数,比如对于avahi-dnsconfd这个脚本,我们可以看到,它只是运行了/usr/sbin/avahi-dnsconfd -D这个命令。

除了位于系统控制面板中对各个services的简单描述以外,你还可以在 http://www.mjmwired.net/resources/mjm-services-fc5.html 找到对各个Fedora Core服务功能的描述以便决定是启动还是关闭某个服务。

在所有需要启动的服务都启动完毕以后,rc程序通过rhgb-client程序通知rhgb图形界面退出,rhgb的使命就完成了。

接下来,init程序在tty1-tty6启动mingetty程序,从现在开始,你可以通过Ctrl-Alt+F1..F6在各个不同的tty之间进行切换了。

然后,如果当前启动级别为5,init程序通过调用/etc/X11/prefdm程序,启动一个图形界面的登录屏幕,让用户登录。这个程序将会读取位于 /etc/sysconfig/desktop中的配置文件,如果没有指定任何配置文件,prefdm运行的顺序依次为gdm,kdm和xdm.

后面的启动部分就属于Gnome,Kde或者其它相应的窗口管理器了。

FC6启动过程分析——Init 过程

在上一篇文章中,我们介绍了从上电到切换到真正的root之前,FC6都做了哪些事情,接下来,我们将开始介绍从切换到真正的Root到图形界面的用户登录,FC6都做了哪些事情。在切换到真正的Root以后,FC6将电脑的控制权交给真正的init程序,通常使用的都是标准的SysVinit程序,这个程序读取配置文件/ets/inittab,然后按照其中的配置执行指定的任务。研究这个文件,就可以了解从切换到新Root,到提示用户登录,FC6都做了哪些事情。

首先,这个配置文件指定运行文件/etc/rc.d/rc.sysinit,这是个使用bash的执行的脚本文件,它首先检测一些基本系统的挂载情况,然后从/etc/sysconfig/network文件中读取网络配置,检查SELinux(安全性增强Linux)的状态,然后开始设置终端字体(使用/sbin/setsysfont命令,这个命令将读取/etc/sysconfig/i18n配置文件,然后开始打印 "Welcome to Fedora ..."的字样,其中Fedora是从配置文件/etc/redhat-release中读取的。然后开始提示按"I"键将进入交互启动模式,在这种模式,你可以选择是否启动某个特定的服务。然后,使用/sbin/hwclock这个程序从BIOS中读取系统时间,其间使用了配置文件 /etc/sysconfig/clock,然后杀死所有的nash(我们在initrd中使用的shell)进程,启动udev(动态设备管理进程,通过监视sysfs按照规则动态创建/dev目录中的设备,已经逐渐取代了hotplug和coldplug).

然后rc.sysinit程序检查/etc/sysconfig/modules/下的所有的脚本,如果找到可以执行的脚本,就执行它,这里的脚本通常用来定义一些用户级别的模块加载。我想如果你希望额外加载一些比如遥控器之类的模块,你可以在这里增加脚本。

然后,FC6准备进入图形界面继续init过程,进入初始化需要满足的条件包括内核命令行参数中包含rhgb参数并且不包含early-login参数, BOOTUP="color",GRAPHICAL="yes"(这些变量在/etc/sysconfig/init定义)并且 /usr/bin/rhgb是可执行的程序。如果所有这些条件都满足,那么现在将执行rhgb程序. rhgb程序的作用是在启动的时候建立一个临时的仅使用loopback网络的X窗口服务器,然后在这个窗口上显示启动进度,init程序的其他部分可以通过rhgb-client程序向这个进度窗口发送消息,rhgb-client使用到的update参数是在rhgb的代码中写死的,总共有20个步骤,从最开始的"RCclock"到最后的"loginscreen"。

现在我们已经进入了FC6启动的图形界面部分。解下来首先做的事情是在运行期配置内核参数,读取的配置文件是/etc/sysctl.conf,然后是加载键盘配置,你在安装FC6的时候可能被问到这个选项,配置文件是/etc/sysconfig/keyboard,然后是使用hostname命令设置主机名,接下来如果/proc/acpi目录存在则尝试加载所有位于/lib/modules/$unamer/kernel/drivers/acpi/中的模块。然后尝试设置RAID(磁盘阵列) 加载相应模块并按需进行磁盘加密, 根据配置文件/etc/sysconfig/autofsck,/etc/sysconfig/readonly-root以及命令行参数中指定的属性决定是否对磁盘进行检查以及判断检查的结果。根据内核命令行参数决定是否进行磁盘限额检查。

重新将根分区挂载为读写模式,删除在磁盘检查过程中产生的临时文件并更新/etc/mtab文件,挂载fstab中所有非网络分区的分区并且打开磁盘限额配置。

按需要进行本机配置:如果存在/.unconfigured文件,调用/usr/bin/system-config-keyboard配置键盘,调用 /usr/bin/passwd root配置超级用户密码,调用/usr/sbin/netconfig配置网络,调用/usr/sbin/timeconfig配置时区,调用 /usr/sbin/authconfig --nostart配置网络登录,调用/usr/sbin/ntsysv配置默认的运行级别。清空包括/var/lock/,/var/run/, /tmp等在内的临时目录并创建新的临时文件,初始化串口,按照内核命令行参数的指示加载scsi相关模块,进行网络配置等(netprofile=), 创建/.autofsck文件,这个文件应该在关机的时候被删除,如果没有被删除说明没有正常关机,将会执行磁盘检测。

现在,检查用户是否按下了"I"键来决定是否运行交互模式的启动。至此,rc.sysinit完成了。


附 inittab 文件的解析

Linux 内核启动之后,启动的第一个进程就是 init 进程。该进程从 /etc/inittab 文件中读取配置,对系统进行一系列的初始化工作。下面我们来分析一下 /etc/inittab 文件的结构。

inittab 文件的每一行的基本格式如下所示:

l3:3:wait:/etc/rc.d/rc 3
可以看到,该行使用冒号分成了四段。各段的意思如下。

1. ID
用于标识该配置项的 id,长度为 1~4 个字符。
2. 运行级
列出在哪些运行级时执行该配置项的命令。本例表明使用运行级3 启动系统时执行该条指令。
3. 动作
指示该条命令应该如何执行。本例中动作为 wait,表示在这条命令未执行完之前不要去执行下一条命令。
4. 进程
执行该项时需要启动的可执行文件名及其参数。

常见的动作如下所示。

initdefault: 该配置项指定系统的默认运行级。
respawn: 该配置项所指定的进程如果被结束,则重新启动该进程。
wait: 该配置项指定的进程在运行结束之前不要执行下一条配置项。
once: 切换到对应的运行级之后,仅执行指定的进程一次。
sysinit: 无论以什么运行级启动,系统启动时都要执行该配置项指定的进程。
boot: 仅在系统启动时执行一次。
bootwait: 仅在系统启动时执行一次,在执行结束之前不执行下一条配置项
powerfail: 当接收到UPS的断电通知时执行该项指定的进程。
powerwait: 与powerfail相同,但init会等待进程执行结束。
powerokwait: 接收到UPS的供电通知时执行。
ctrlaltdel: 当用户同时按下 Ctrl+Alt+Del 时执行该项指定的进程。
下面我们看看实际的例子。这是 Fedore Core 3 的 /etc/inittab 文件。删掉了其中的一些注释,并适当地对每一行增加了解释。

# 下面用 initdefault 动作设置默认的运行级。注意该项没有指定进程,
# 但是最后的分号不要漏掉
# 0 - 关机 (不要将此运行级设为默认)
# 1 - 单用户模式
# 2 - 多用户,不支持 NFS。若无网络则与 3 相同
# 3 - 完整的多用户模式
# 4 - 未使用
# 5 - X11图形界面
# 6 - 重新启动 (不要将此运行级设为默认)

# id:5:initdefault:

# 系统初始化,包括主机名设置、激活交换分区、检查根分区、
# 以读写方式加载根分区、加载 /etc/fstab 中的分区、
# 激活磁盘配额、加载内核模块等功能
si::sysinit:/etc/rc.d/rc.sysinit

# 执行 rc 脚本,启动各种系统服务
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

# 接管 CTRL-ALT-DELETE,按下时重新启动系统 ca::ctrlaltdel:/sbin/shutdown -t3 -r now
# 当 UPS 发来断电通知时,准备在 2 分钟之后关闭系统 pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"

# 如果在关闭系统之前 UPS 恢复供电,则取消关闭系统 pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"

# 在标准运行级时启动虚拟终端。这里准备了六个虚拟终端,

# 要想改变虚拟终端的数目,只需要增删这里的配置项即可 1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

# 运行级 5 的时候启动 X 的登录管理器
x:5:respawn:/etc/X11/prefdm -nodaemon

FC6启动过程分析——从power on 到switch root

让我们从启动开始,看看FC6都做了些什么。

众所周知,和所有别的发行版本一样,FC6是由grub引导的,grub通常被安装在主引导扇区即MBR-Master Boot Record,也就是说,如果你在主板的bios中设置了从硬盘启动,那么主板自检以后所执行的第一部分代码就是grub,grub将在其安装时候指定的位置寻找 menu.lst这个文件,并且根据这个文件的配置,加载相应的内核,启动linux。这里值得我们注意的是,由于grub的这种机制,即使是格式化你觉得已经完全不再使用的硬盘分区,也可能造成灾难性的后果,假设我们把grub安装在了 mbr,并且将配置文件放置在hda2,hda1安装了一套windows操作系统,通过grub实现多重引导,但是现在我们想放弃hda2的linux 系统,或者想把它换成另外一套linux发行版,我们可能会选择格式化hda2,虽然grub被安装在mbr,但是hda2的被格式化仍然会破坏其配置文件所在的目录,grub将无法正常启动,你也就无法正常引导位于hda1的windows系统了,因为grub会提示错误,不给你选择系统的机会。这种情况在实际的双系统使用过程中,可能经常会遇到。 遇到这种问题,常见的修复方法是使用软盘启动windows,使用fdisk /mbr命令使用windows系统提供的mbr覆盖gurb的mbr代码,或者使用其他方式启动linux(软盘,U盘或者光盘),重新安装grub。虽然天不会塌下来,但是相信也会让你很不爽了,所以要小心。

内核是所有linux的核心,grub在成功的读取了配置文件以后,将会找到 kernel所在的位置,加载内核并且把电脑的控制权交给kernel程序,在FC对应的grub的menu.lst文件中,我们通常可以看到类似这样的语句:root (hd0,2) 这句话告诉我们,从现在开始,根路径(这里的根路径并不是系统所安装的根目录路径,而是指启动进程的根文件系统所挂载的路径,一般挂载在/boot/)将被设置为第一个硬盘的第3个分区,然后是kernel /boot/vmlinuz-2.6.18-1.2798.fc6 ro root=LABEL=/ rhgb quiet,这句话告诉我们,从根分区的boot目录的vmlinuz-2.6.18-1.2798.fc6这个文件中读取内核,执行内核的时候使用 "ro root=LABEL=/ rhgb quiet"这样的参数,内核的执行参数可以控制内核的行为,比如ro参数告诉内核,以只读方式挂载根分区,而quiet则告诉内核,启动的时候不要打印任何信息。这些参数不光影响内核的执行,大多数的发行版也使用这些参数控制启动完毕以后后续的动作。这些参数可以在任何时候从/proc/cmdline这个文件中获得。

现在,grub找到了内核(hd0,2) /boot/vmlinuz-2.6.18-1.2798.fc6,它将整个电脑的控制权交给了这个程序,内核开始进行各种初始化的动作,你可以将 quiet参数去掉,以便看看内核都做了哪些事情,也可以在系统启动成功以后,使用dmesg这个命令查看内核启动的时候,都打印了哪些东西,总的来说,内核做的都是一些和硬件打交道的事情,比如初始化内存,检测并初始化硬件等,在内核启动的最后,它将寻找init程序并将电脑的控制权交给这个程序。

有越来越多的新硬件需要linux的支持,如果把所有的硬件检测工作都放在内核中完成,内核会变得无比巨大,这不光是没有效率的,事实上也是不可能和不允许的,因此,如果你清楚的知道你的电脑都拥有哪些硬件,并且在未来不会添加新的硬件,你可以只将那些你需要的硬件编译到内核中去,然后直接启动你的 linux系统(事实上,早期的Gentoo系统要求每个安装者在安装的时候编译自己的内核),但是对于FC6这样的发行版来说,为了让全球大多数的PC 都可以顺利使用它,它使用模块的方式编译了尽可能多的硬件支持,并且在启动的时候在grub的配置文件中指定了initrd参数。

initrd 参数指定一个小的文件系统,这个文件系统虽然很小,但是比起内核来可以大很多,如果指定了initrd参数,内核在进行完自己的任务之后,将会运行 initrd这个小文件系统中的init程序,由这个程序完成进一步的系统初始化动作,加载更多的硬件支持以便找到真正的根文件系统。在menu.lst 文件中,这是通过initrd /boot/initrd-2.6.18-1.2798.fc6.img这一行来完成的,扩展名img通常预示着这是一个小的系统镜像文件。

使用file命令,我们可以看到/boot/initrd-2.6.18-1.2798.fc6.img是一个使用gzip压缩过的文件,解压缩以后再使用 file命令,看到这是一个cpio文件,再解压缩这个文件,我们就可以看到initrd文件系统了,这个系统中的文件不多,在根目录中包含一个init 文件,这就是内核初始化完毕以后要运行的文件,这是一个脚本文件,它使用nash解释执行,nash是专门为initrd定制的脚本解释器,它的功能小而专业,内建了很多initrd很需要的命令,我们在FC6启动的时候看到的"Red Hat nash version xxx starting "这句话,就是这个时候打印出来的。

我们来具体看看FC6的initrd做了哪些事情,首先为了让包含在initrd镜像中的那些程序顺利执行,它需要完备当前的文件系统,包括挂载proc和sys文件系统(这些是内核支持的系统目录,需要将其挂载到用户区),创建/dev目录,并且在 /dev目录中创建系统初始化所需的那些设备,最典型的设备比如console,有了这个设备,echo命令才能把信息显示到终端上,这个阶段FC6的 initrd中的init程序创建的设备达到数十个之多。然后启动hotplug支持热插拔,这里的hotplug是nash内建支持的命令之一,然后使用内建的mkblkdevs命令根据/sys/block目录下的文件信息创建/dev目录中对应的设备文件,然后加载usb和ext3相关的模块,在这个过程中可能又有新的设备被发现,因此需要使用mkblkdevs命令再次更新设备目录,在准备好了/dev设备目录以后,init程序开始调用内建的 mkrootdev命令来创建/dev/root这个设备作为后续操作的根分区,这个命令的大致逻辑是:如果内核命令行中指定了root参数,则使用其指定的那个参数作为root设备,如果指定的为LABEL,则检查所有的块设备并且寻找卷标为指定值的设备作为root设备,如果没有指定root参数,则使用/proc/sys/kernel/real-root-dev指定的设备,这个命令除了将创建/dev目录中相应的root设备文件以外,还将更新 fstab文件,将当前找到的root文件的mount参数写入/etc/fstab文件,这样在接下来的命令中,可以直接使用mount命令加载根分区,成功加载完根分区以后,init使用nash内建的setuproot命令,将所有的sys,proc,dev等这些已经挂载在initrd文件系统中的目录重新转移至新的根分区,然后使用nash内建的switchroot命令(内核2.6以上的版本)将当前文件系统切换至新的根分区,并且执行新的根分区的init命令,这样.initrd也完成了自己的使命,剩下的事情就是真实的根分区中的init程序的工作了。