序 言
“有龙出没”,中世纪地图绘制者碰到未知和危险的地方就如此标记,可能你**敲入如下命令也有这样的感觉:
cd /usr/src/linux ; ls
“我该从何处入手?”你困惑不已。“我到底在寻找什么?所有这些是怎样放在一起并真正起作用的?”
功能全面的现代操作系统庞大而复杂,有为数不少的子系统,它们之间的交互更是错综复杂而微妙难言。不错,你的确拥有Linux内核源代码(稍后还会详述),但是,从何处开始阅读,应留意哪些地方,该以怎样的顺序浏览代码,这些问题的答案都不是显而易见的。
本书的作用正在于此。它一步一步地让你了解到内核的各个部分,它们如何工作,互相之间怎样关联。本书作者熟知内核,将这些知识贯穿于本书的始终。在学完本书之后,你和内核至少会成为好朋友,乃至产生深厚的情意。
Linux内核是“自由”软件。Richard Stallman在“自由软件的定义”一文中,说明了软件自由的含义。有两个级别,Freedom 0,运行软件是自由的,这是*基本的自由。紧跟其后的是Freedom 1,探究程序运行原理也是自由的。这种自由往往被忽略,实际上,这才是*重要的,因为学习的*好方式就是观察别人如何做事。在软件世界中,那就意味着阅读别人的程序,并了解他们哪方面做得好,哪方面做得不好。至少在我看来,GNU/Linux之所以在现代计算领域变成一股强大的力量,*根本的原因之一就是GPL协议的自由。你在使用GNU/Linux的每时每刻都会感到这种自由的可贵,别忘了经常默念一下它的好处。
本书充分利用Freedom 1,带你深入研究Linux内核源代码。你会看到所有的一切,有些方面的确做得不错,还有些事也并不尽人意。但是,由于有了Freedom 1,你会看到全貌,并从中学到很多。
况且,这也使我与“Prentice Hall开源软件开发系列丛书”结缘,本书是丛书的**辑。开发这个系列的想法来自于这样一个理���:阅读代码是学习编程的*好方法。如今这个世界,人们幸福地享有丰富而自由的开源软件,这些源代码?期待着(或许热切渴望着)被大家阅读、理解和赞许。该系列丛书旨在成为你软件开发学习过程中的领路人,即通过尽可能多地展示真实的代码帮助你学好编程。
我真诚地希望你会喜欢本书,并从中获益多多。我也希望本书能激发你的灵感,从而在自由软件和开源世界开创你自己的事业,用这种方式参与进来,那无疑是*令人愉快的了。 祝你学习愉快! Arnold Robbins
丛书主编 前 言
无论是一般性技术还是计算机技术,对于试图了解它们的人们来说都具有不可思议的?力。技术的发展使其影响力不断扩大,迫使人们对一些旧的概念重新评估。Linux操作系统已经对产业变革和商业营销方式转变做出了巨大贡献。它采用GNU公共许可证并与GNU软件良性互动,占据了**位置,围绕开源、自由软件和开发社区思想的各种争论都离不开它。Linux无疑是一个极其成功的典范,展现了开源操作系统无比强大的力量,其理论的魔力令世界各地的程序员们如痴如狂。
对于大多数计算机用户来说,使用Linux正变得越来越方便。有了各种各样的发布版、社区的支持,以及工业后盾,Linux的应用也找到了**的港湾,出现在大学、行业应?以及数以千计的家庭用户中。
使用大潮促进了技术支持和新功能需求的日益增长。这样一来,愈来愈多的程序员发现自己对Linux内核内幕感兴趣,因为大量现有的(还在快速增长的)应用需要支持不同的体系结构和种类繁多的新设备。
内核向Power体系结构的成功移植,也助长了Linux操作系统在高端服务器和嵌入式系统中的全面繁荣。许多公司购买基于Power PC的系统来运行Linux,因此越来越多的人需要知道Linux在该体系结构上的运行机理。
适合的读者
本书的读者包括初级和经验丰富的系统程序员、Linux的热衷者,以及应用程?的***,这些***渴望更好地理解自己的程序到底是如何工作的。只要有C语言知识,熟悉基本的Linux用法,如果想知道Linux如何工作,那么你就会发现这本书提供了所需的基本知识,可以说,本书是理解Linux内核如何工作的初级读本。
不管你是只登录过Linux并编写了些小程序,还是你本身就是一个系统程序员,正想深入理解某个子系统的特性,本书都会有你所要的信息。
内容组织
本书分为三部分,每部分都提供必要的知识,让读者能顺利地钻研Linux内幕。
**部分提供必要的工具和背景,便于对Linux内核展开进一步的探索。
第1章回顾了Linux和UNIX的历史,对比了很多发布版,并从用户空间的角度简述各种内核子系统。
第2章描述Linux内核中常用的数据结构和语言的用法,介绍x86和PowerPC体系结构的汇编语言,并简述一些工具和实用程序,可用来获取理解内核内幕所需的信息。
第二部分介绍了在每个内核子系统中所涉及的基本概念,并分析了执行子系统功能的代码。
第3章讨论了进程模型的实现。本章解释了为何引入进程,并讨论了进程往返于用户空间和内核空间时的控制流。我们还讨论了进程在内核中是如何实现的,并描述了与进程执行相关的所有数据结构。本章还介绍了中断和异常,描述了这些硬件机制在每种体系结构中是如何发生的,它们与Linux内核又是如何交互的。
第4章描述了Linux内核如何追踪和管理用户空间进程的可用内存和内核的可用内存。本章描述了内核对内存分类的方式,以及决定分配和释放内存的方式,也详细描述了缺页机制以及它怎样在硬件上执行。
第5章描述了处理器如何与其他设备进行交互,内核又是如何响应和控制这些交互的。本章还涵盖了各种设备及其在内核中的实现。
第6章概述文件和目录如何在内核中实现。本章引入了虚拟文件系统,它是用于支持多文件系统的抽象层。本章还跟踪了文件相关操作的执行,如打开和关闭文件。
第7章描述调度程序的操作,调度程序让多个进程运行起来就像系统中只有一个进程在运行一样。本章详细描述了内核如何选择执行哪一个任务,进程切换时如何与硬件进行交互。本章还叙述了什么是内核抢占,它又是怎样执行的。*后,描述了系统时钟的工作原理,内核怎样使用它计时。
第8章描述电源开和关时都发生些什么。本章对各种处理器处理内核加载的方式进行了跟踪,包括对BIOS、Open Firmware和bootloader的描述。然后,考察了内核启动和初始化时的?性顺序,涉及了前面章节中讨论的所有子系统。
第三部分,描述如何编译内核并与内核进行交互的有效途径。
第9章涵盖了编译内核所必需的工具链和所执行的对象文件的格式。还详细描述了内核源代码编译(Kernel Source Build)系统如何运作,怎样把配置选项加入内核编译系统中。
第10章描述了/dev/random操作,这在所有的Linux系统中都可以看到。本章用它来跟踪设备,并从更具实战性的角度介绍前面各章描述过的概念。*后介绍了如何在内核中实现自己的设备。
我们的探索方法
本书给读者介绍了理解内核的必要概念?我们遵循自顶向下的方式来组织内容,具体体现在以下两个方面。
首先,我们把内核的机理和用户空间操作的执行关联起来,因为读者对后者较熟悉,所以我们会将二者结合起来,解释内核的工作。在可能时,我们从用户空间的例子说起,并跟踪代码的执行到内核。但有时,这种跟踪方式并不有效,因为需要先介绍子系统的数据类型和子结构,而后才能解释其工作原理。在这些情况下,我们把对内核子系统的解释和它与用户空间程序如何联系的具体例子结合起来。有双重意图:其一,当内核一方面与用户空间打交道,另一方面与硬件打交道时,突出在内核中看到的层面;其二,通过跟踪代码和事件发生的顺序来解释子系统的工作原理。我们相信,这有助于读者将内核的工作原理与自己的认识匹配起来,也有利于读者了解一个特定的功能怎样与操作系统的其余部分产生联系。
其次,我们以自顶向下的角度,考察把数据结构视作子系统操作**,并观察它们怎样与系统管理的执行行为相联系。我们尽力刻画子系统操作**的结构,并像追踪子系统的操作一样持续关注这些数据结构。
约定
你会在全书中看到源代码列表。右上角存放有关源代码树根的源文件位置。代码中的行号是为了方便随后对代码?行解释。我们在解释内核子系统及其工作原理时,会不断引用源代码并给予解释。
命令行选项、函数名、函数输出以及变量名都用代码体加以区分。
引入一个新概念时就采用黑体。 第1章 概 述
本章内容
UNIX的历史
标准和通用接口
自由软件和开放源码
Linux发布版概览
内核版本信息
基于Power的Linux
什么是操作系统
内核组织
Linux内核概述
可移植性和体系结构的相关性 Linux操作系统诞生?1991年,是Linus Torvalds学生时代业余爱好的产物。与当下的迅猛发展势头相比,当初的Linux显得微不足道。Linux刚开发时运行在具有AT硬盘、x86体系结构的微处理器计算机上。**个发布版支持bash shell和gcc编译器。那时还不关心其可移植性,也没有设想其在学术界和工业界是否能够广泛应用,更没有商业计划或远景宣言。然而,从**天起,它就是免费可得的。
从早期的beta版开始,Linux就在Linus的指导和维护下,变成了一个协作项目。这填补了一项空白,即黑客们需要一个运行在x86体系结构上的免费操作系统。这些黑客们开始贡献源代码,为他们特定的需要提供支持。
通常说,Linux是一种UNIX。从技术上说,Linux是UNIX的克隆,因为它实现了POSIX UNIX规范 P1003.0。UNIX从1969年诞生以来就统治着非Intel工作站领域,被公认为是一个强大而又优雅的操作系统。UNIX既然定位在高性能工作站,就只在研究机构、学术单位以及开发机构中使用。Linux把UNIX系统的能量带入到了Intel个人计算机及其用户家里。如今,Linux在工业和学术领域得到广泛应用,支持众多的体系结构,如PowerPC。
本章简要介绍有关Linux的概念,**你概览内核的组成和特性,并介绍一些引人入胜的Linux功能。为了理解Linux内核的概念,你需要对其开发动机有一个基本理解。
1.1 UNIX的历史
刚刚说过,Linux是一种UNIX。尽管Linux并不是直接从现有的UNIX衍生而来的,但事实上,它实现了通用UNIX标准,因此有必要来看看UNIX的历史。
MULTICS(MULTiplexed Information and Computing Service)被认为是UNIX操作系统的鼻祖,它是麻省理工学院、贝尔实验室和通用电气公司的一家合资企业开发的操作系统,该企业主要从事计算机制造。MULTICS的开发是想让机器支持众多的分时用户。在合资企业成立的1965年,尽管当时操作系?支持多道程序设计(可在多个作业之间分时),但依然是只支持单用户的批处理系统。从用户提交一个作业到获得输出之间的响应时间要用小时计算。MULTICS的主要目的是开发一个多用户分时系统,让每个用户都可以访问自己的终端。尽管贝尔实验室和通用电气公司*终放弃了这一项目,但MULTICS还是在许多地方得以实际应用。
UNIX的开发始于移植精简的MULTICS版本,从而开发出一个PDP-7小型计算机上的操作系统,让这个新操作系统能支持一种新的文件系统,即UNIX文件系统的**个版本。由Ken Thompson开发的这个操作系统,支持两个用户,有一个?令解释器和对新文件系统进行文件操作的一组程序。1970年,UNIX被移植到PDP-11上,经修改后能支持更多的用户。这就是第1版的UNIX。
1973年发布的UNIX第4版,由Ken Thompson和Dennis Ritchie用C(由Ritchie刚刚开发的一种语言)重写。这就让操作系统脱离纯汇编语言,并打开操作系统可移植性的大门。想象一下这种转变的意义有多大!当时,操作系统完全是在系统的体系结构规范下建立起来的,因为汇编语言与体系结构密切相关,所以操作系统不易移植到其他体系结构。用C重写UNIX是向更可移植(和可读)的操作系统迈向的**步,也是让UNIX迅速普及的一步。
1974年是UNIX在大学中迅速普及的头一年。学术机构开始与贝尔实验室的UNIX系统开发组进行合作,开发出具有很多创新特色的第5版。这一版本可免费获得,其源代码可供大学用于教学。1979年,在众多创新、代码简化以及改善可移植性之后,UNIX 操作系统第7版(V7)诞生。这一版本包含了C编译器和**的B shell命令解释器。
20世纪80年代出现了个人计算机。工作站当时只用在企业和大学中。大量UNIX变体从第7版衍生而来。这些变体包括Berkeley UNIX(BSD)和AT&T UNIX System III和System V,其中BSD是由加利福尼亚大学伯克利分校开发的。每个变体又会演变出其他系统,如NetBSD和OpenBSD(BSD的变体)以及 AIX(IBM的System V变体)。事实上,UNIX的所有商用变体都来源于System V 或 BSD。
1991年,Linux出现,那时,UNIX非常流行,但不适用于PC。购买UNIX的价格令人望而却步,的确不是一般用户可以买的,除非他在大学工作。Linux*初是作为Minix操作系统(由Andrew Tanenbaum开发的一个教学用小型操作系统)的扩展而实现的。
随后的几年,Linux内核与FSF(Free Software Foundation,自由软件基金会)的GNU项目所提供的系统软件相结合,使得Linux 发展成为非常坚实的系统,吸引着贡献代码的黑客以外的大量人员的注意。1994年,Linux的1.0版发布。从那时开始,Linux迅速成长,发布版的需求量剧增,大批大学、公司及个人用户需要各种体系结构上的支持。
1.2 标准和通用接口
通用标准在不同变体的UNIX之间架起一座桥梁。用户选用哪种UNIX的决定影响到它的移植性,从而影响到它的潜在市场。如果你是一名程序***,很显然,你的程序能够拥有的市场只限于那些使用同一系统的人,除非你不辞辛苦地对该程序进行移植。标准的产生源于需要一种通用的编程接口规范,使得在一种操作?统上开发的程序在不修订或尽可能少修订的情况下可以在另一种操作系统上运行。各种标准组织都开始为UNIX制定规范。POSIX就是其中的一种,这是由IEEE制定的一种用于计算机环境的可移植操作系统标准,Linux就遵从这一标准。
1.3 自由软件和开放源码
Linux是开放源码软件*成功的例子之一。所谓开放源码软件,就是它的源代码可以自由获取,这样任何人都可以修改、阅读和重新发布它。与之对立的是封闭源码软件,它仅以二进制形式发布。
开放源码允许用户按自己的意愿开发软件,以满足自己的需求。不过,根据许可证的内容,某些约束会对代码有限制。这样做的好处是用户决不受限于其他用户曾经开发的东西,因为他们可以自由地修改代码以满足自己的需要。任何人都可以对Linux操作系统进一步开发并贡献自己的智慧。这使得Linux的演进速度快得惊人,不管是开发、测试还是文档方面。
有各种各样的开源许可证存在:特别说明的是,Linux是按GNU的GPL(General Public License)版本2的许可发布的。在源代码根目录的COPYING文件中,可以找到该许可证的副本。如果你打算修改Linux内核,那么*好熟悉这个许可证的条款,这样,你就可以知道自己贡献的合法权益。
关于自由和开放源码软件的产权有两大主要阵营。自由软件基金会(Free Software Foundation)和开源社区在意识形态方面有所不同。自由软件基金会是两大组织中较早出现的,他们坚持认为说软件是自由的,就像说言论是自由的一样。开源社区则认为自由而开放的软件是与专有软件并列的一种不同的方法论。更多的信息,请到http://www.fsf.org和http://www.open- source.org网站查阅。
1.4 Linux发布版概览
我们曾提到,Linux内核只是通常所说的“Linux”的一部分。Linux发布版包含Linux内核、各种工具、窗口管理器以及很多?他应用程序。Linux中使用的很多系统程序都是由FSF GNU项目开发和维护的。随着Linux日益流行和需求的不断增长,把内核和这些工具打包在一起成为一件意义重大且可从中获益的事。许多人和公司开始为一组特定的目标提供一个特定的发布版。我们不必关注过多的细节,只回顾一下到写本书为止的主要Linux发布版即可。新Linux发布版仍在继续发布着。
大多数Linux发布版都把工具和应用程序按头文件和可执行文件分组。这种分组就是所谓的包,使用Linux发布版就有这样的好处,不用自己去下载头文件并编译源代码。GPL许可规定开源软件的增值部分是?以收费的,例如在代码重发布时提供的一些服务。
1.4.1 Debian
Debian 是一种GNU/Linux操作系统。与其他发布版类似,大多数应用程序和工具都来自GNU软件和Linux内核。Debian具有较好的包管理系统apt(advanced packaging tool)。Debian的主要缺点是*初的安装过程,会使Linux新手感到迷惑。Debian并不属于某个公司,它是由自愿者社区开��的。
1.4.2 Red Hat/Fedora
Red Hat (公司)是开源软件开发领域的大公司。Red Hat Linux以前是该公司的Linux发布版,到了2002~2003年,它开始提供两种独立的发布版:Red Hat企业级Linux 和Fedora Core。Red Hat企业级Linux的目标是为企业、政府或者其他行业提供稳定并得到支持的Linux环境,而Fedora Core面向的是个人用户和爱好者。两个发布版之间的主要差异是一个比较稳定,一个新特性较多。相对于企业版,Fedora会有较新但不太稳定的代码包含在发布版中。在美国,Red Hat是Linux企业级版本的**。
1.4.3 Mandriva
Mandriva Linux (从前叫做Mandrake Linux)起源于Red Hat Linux,是它的一种易安装版,但之后分枝为一个独立的发布版,该发布版面向的是Linux个人用户。Mandriva Linux的主要特点是系?配置和安装都易上手。
1.4.4 SUSE
SUSE Linux 是Linux平台的另一个主要参与者。SUSE面向企业、政府、工业和个人用户。SUSE的主要优势是它有一个安装和管理工具Yast2。在欧洲,SUSE是Linux企业级版本的**。
1.4.5 Gentoo
Gentoo 是新近上市的Linux发布版,它已经赢得众多赞许。Gentoo Linux的主要特点就是它的所有包是根据自己机器的特殊配置从源代码编译而来的,这可以通过Gentoo portage系统来完成。
1.4.6 Yellow Dog
Yellow Dog(黄狗)Linux 是一种主要的基于PowerPC的Linux发布版。尽管刚才溜说的很多发布版也都可以运行于PowerPC平台,但它们的**还是基于i386的Linux版本。黄狗Linux非常类似于Red Hat Linux,但进行了扩充开发,以支持较为普遍的PowerPC平台和基于Apple的专有硬件。
1.4.7 其他发布版
Linux用户可以热情高昂地选择适合自己需要的发布版,而且选择余地很大。Slackware是一款经典的发布版,MontaVista更适合于嵌入式系统,当然,你还可以定制自己的发布版。要想进一步了解Linux各种各样的发布版,可以访问Wikipedia 条目http://en.wikipedia.org/wiki/Category: Linux_distributions来查阅相关资料。
这里可能包含*新的信息,即使没有*新的信息,还可以通过链接访问其他站点上的信息。
1.5 内核版本信息
如果你想成为一名贡献者参与开发,与任何软件项目一样,理解项目的版本记录格式是很重要的。在Linux内核2.6版之前,开发社区遵循一种相当简单的版本和开发树编码方法。偶数版本(2.2、2.4和2.6)被认为是树的稳定分支。只有加入稳定分支的代码才是修订了错误的代码。内核开发会在标记为奇数(2.1、2.3和2.5)的开发树中继续进行。*终接受了大多数代码后,开发树几近完成,此时再发布一个新的稳定树。
在2004年中期,标准的发布周期有了一个变化:本该正常加入开发树的代码被包含进稳定的2.6树中。特别声明,“……主流内核将会是*快、特性*丰富的内核,但没必要是*稳定的。*终的稳定性是由发布者完成的(目前的确如此),但是,还是期望发布者能把他们的补丁快速融合进来。”(Jonathan Corbet通过http://kerneltrap.org/node/view/3513发布。)
因为这是一个较新的开发模式,只有时间会证明这种发布周期今后是否会有重大变化。
1.6 基于Power的Linux
基于Power的Linux(Linux系统运行在Power或者说PowerPC处理器上)需求急增,变得日益流行。许多企业和公司购买基于PowerPC的系统来运行Linux,其主要原因是PowerPC微处理器提供了**扩展性的体系结构,能满足广泛的需求。
PowerPC体系结构已经大举进入嵌入式市场,AMCC PowerPC 和 Motorola PowerPC均推出了32位片上系统(system-on-chip,SOC)集成产品。这些SOC包含的东西除处理器外还有内置的时钟、内存、总线、控制器以及外设。
拥有PowerPC许可证的公司包括AMCC、IBM和Motorola。尽管这三家公司都独立地开发自己的芯片,但是,这些芯片享有共同的指令集,相互之间是兼容的。
Linux运行在基于PowerPC的游戏控制台、大型机以及桌面计算机上。Linux在这日益主流的体系结构上的快速普及源于开源和私有企业积极主动的共同努力,前者如http://www.penguin- ppc.org,后者如IBM的Linux技术**。
随着Linux在这一平台的日益流行,我们需要探究Linux如何与PowerPC进行交互并使用其功能。
许多站点包含基于Power的Linux的有用信息,当我们进行这种探索时会随时参考那些信息。http://www.penguinppc.org就是追踪Linux PPC版本的站点,PowerPC***社区也在这里了解基于Power的Linux的新闻。
1.7 什么是操作系统
现在我们考虑一下操作系统的一般概念、Linux的基本用法与特点及它们之间如何联系起来。本节简要介绍一下这些概念,更详细的内容将在后续章节中介绍。如果你已经熟悉这些概念,就可以跳过本节,直奔第2章。
操作系统让裸机变为可用的计算机。它既负责管理系统硬件资源,也为应用程序的开发和执行提供基础。如果没有操作系统,每个程序就得为想要使用的所有硬件提供驱动程序,但那是应用程序***无法做到的。
操作系统的内部结构取决于其类型。Linux和大多数UNIX的变体都是单模块系统。当我们说一个系统是单模块时,并不是指它是庞大无比的(尽管在大多数情况下,第二种解释也完全适用)。更确切地说,我们指它是由一个单独的模块单元——也就是一个单独的目标文件组成。操作系统是由很多例程经编译和链接在一起而形成的。例程之间交互的方式决定了单模块系统的内部结构。
在Linux中,我们把操作系统划分为内核空间和用户空间两部分。用户是通过用户空间与操作系统打交道的,程序员开发或使用的应用程序也位于用户空间。用户空间不能直接访问内核(从而不能访问硬件资源),但是可以通过内核定义的*外层例程——系统调用来访?。内核空间是硬件管理功能发挥作用的区域。在内核中,系统调用例程调用用户空间无法访问的内核例程来使用内核更细粒度的功能。
有一组例程对用户空间是不可见的,它们是各个设备驱动程序的函数和内核子系统的函数。设备驱动程序也提供了定义明确的接口函数,供系统调用或内核子系统访问。图1-1显示了Linux的结构。
Linux还能提供动态可装载设备驱动程序,这弥补了单模块操作系统内在的一个主要缺陷。动态可装载驱动程序允许系统程序员把系统代码整合进内核中,而不是把代码编译进内核映像中。后者意味着较长时间的等待(具体时间取决于你系统的能力),并要重新起动,这极大地增加了系统程序员开发的用时。有了动态可装载设备驱动程序,系统程序员可以实时地装载和卸载他的设备驱动程序,无需重新编译整个内核,也无需重起系统。
Linux的这些亮点“部分”会贯穿本书。我们尽可能遵循自顶向下的方法,以应用程序的例子开始,追踪其执行路径直到系统调用和内核子系统函数。通过这种方式,你可以把较为熟悉的用户空间功能与对应的内核实现部分联系起来。
......