SlideShare a Scribd company logo
1 of 554
Download to read offline
0 10 5
 . (. )
 1 9
Linux 内核 0.11 完全注释
A Heavily Commented Linux Kernel Source Code
             Linux Version 0.11


               修正版 1.2.2
             (Revision 1.2.2)




                   赵炯
                Zhao Jiong

             gohigh@sh163.net




              www.plinux.org
             www.oldlinux.org

                2003-11-26
内容简介

   本书对 Linux 早期操作系统内核(v0.11)全部代码文件进行了详细全面的注释和说明,旨在使读者能够在尽量短的时间
内对 Linux 的工作机理获得全面而深刻的理解,为进一步学习和研究 Linux 系统打下坚实的基础。虽然所选择的版本较低,
但该内核已能够正常编译运行,其中已经包括了 LINUX 工作原理的精髓,通过阅读其源代码能快速地完全理解内核的运作
机制。书中首先以 Linux 源代码版本的变迁历史为主线,详细介绍了 Linux 系统的发展历史,着重说明了各个内核版本之间
的重要区别和改进方面,给出了选择 0.11(0.95)版作为研究的对象的原因。另外介绍了内核源代码的组织结构及相互关系,
同时还说明了编译和运行该版本内核的方法。        然后本书依据内核源代码的组织结构对所有内核程序和文件进行了注释和详细
说明。每章的安排基本上分为具体研究对象的概述、每个文件的功能介绍、代码内注释、代码中难点及相关资料介绍、与当
前版本的主要区别等部分。最后一章内容总结性地介绍了继续研究 Linux 系统的方法和着手点。




版权说明
    作者保留本电子书籍的修改和正式出版的所有权利.读者可以自由传播本书全部和部分章节的内容,但需要注明出处.由
于目前本书尚为草稿阶段,因此存在许多错误和不足之处,希望读者能踊跃给予批评指正或建议.可以通过电子邮件给我发信
息:gohigh@sh163.net, 或直接来信至:上海同济大学 机械电子工程研究所(上海四平路 1239 号,邮编:200092).


© 2002,2003 by Zhao Jiong
© 2002,2003 赵炯 版权所有.
“RTFSC – Read The F**king Source Code ☺!”

                 –Linus Benedict Torvalds
目录




                                                                                目录

                                                                                                5.7 SCHED.C 程序 .................................................... 104
序言 ................................................................................ 1
                                                                                                5.8 SIGNAL.C 程序................................................... 116
   本书的主要目标 ........................................................ 1                           5.9 EXIT.C 程序 ....................................................... 122
   现有书籍不足之处 .................................................... 1                              5.10 FORK.C 程序.................................................... 127
   阅读早期内核其它的好处? .................................... 2                                          5.11 SYS.C 程序....................................................... 132
   阅读完整源代码的重要性和必要性 ........................ 2                                                   5.12 VSPRINTF.C 程序.............................................. 138
   如何选择要阅读的内核代码版本 ............................ 2                                                5.13 PRINTK.C 程序 ................................................. 146
   阅读本书需具备的基础知识 .................................... 3                                          5.14 PANIC.C 程序 ................................................... 147
   使用早期版本是否过时? ........................................ 3                                       5.15 本章小结........................................................ 148
   EXT2 文件系统与 MINIX 文件系统? ...................... 4
                                                                                             第 6 章 块设备驱动程序(BLOCK DRIVER)......... 149
第 1 章 概述 .................................................................. 5
                                                                                                6.1 概述 ................................................................. 149
   1.1 LINUX 的诞生和发展 ........................................... 5                               6.2 总体功能.......................................................... 149
   1.2 内容综述 ............................................................. 9                     6.3 MAKEFILE 文件................................................. 149
   1.3 本章小结 ........................................................... 12                      6.4 BLK.H 文件........................................................ 151
                                                                                                6.5 HD.C 程序.......................................................... 154
第 2 章 LINUX 内核体系结构 .................................. 13
                                                                                                6.6 LL_RW_BLK.C 程序 ........................................... 167
   2.1 LINUX 内核模式 ................................................. 13                          6.7 RAMDISK.C 程序................................................ 171
   2.2 LINUX 内核系统体系结构 ................................. 14                                      6.8 FLOPPY.C 程序 ................................................... 175
   2.3 LINUX 内核进程控制 ......................................... 15
                                                                                             第 7 章 字符设备驱动程序(CHAR DRIVER) ....... 189
   2.4 LINUX 内核对内存的使用方法 ......................... 16
   2.5 LINUX 内核源代码的目录结构 ......................... 18                                            7.1 概述 ................................................................. 189
   2.6 内核系统与用户程序的关系............................ 23                                              7.2 总体功能描述.................................................. 189
   2.7 LINUX 内核的编译实验环境 ............................. 23                                         7.3 MAKEFILE 文件................................................. 192
   2.8 LINUX/MAKEFILE 文件........................................ 25                             7.4 KEYBOARD.S 程序 ............................................. 194
   2.9 本章小结 ........................................................... 33                      7.5 CONSOLE.C 程序................................................ 211
                                                                                                7.6 SERIAL.C 程序 ................................................... 234
第 3 章 引导启动程序(BOOT)............................. 35
                                                                                                7.7 RS_IO.S 程序 ..................................................... 237
   3.1 概述 ................................................................... 35                7.8 TTY_IO.C 程序 ................................................... 240
   3.2 总体功能 ........................................................... 35                      7.9 TTY_IOCTL.C 程序 ............................................. 250
   3.3 BOOTSECT.S 程序 ................................................ 36
                                                                                             第 8 章 数学协处理器(MATH)................................ 257
   3.4 SETUP.S 程序 ....................................................... 43
   3.5 HEAD.S 程序........................................................ 55                     8.1 概述 ................................................................. 257
   3.6 本章小结 ........................................................... 63                      8.2 MAKEFILE 文件................................................. 257
                                                                                                8.3 MATH-EMULATION.C 程序.................................. 258
第 4 章 初始化程序(INIT) ......................................... 65
                                                                                             第 9 章 文件系统(FS) ............................................... 261
   4.1 概述 ................................................................... 65
   4.2 MAIN.C 程序........................................................ 65                     9.1 概述 ................................................................. 261
   4.3 本章小结 ........................................................... 73                      9.2 总体功能描述.................................................. 261
                                                                                                9.3 MAKEFILE 文件................................................. 267
第 5 章 内核代码(KERNEL)..................................... 75
                                                                                                9.4 BUFFER.C 程序 .................................................. 269
   5.1 概述 ................................................................... 75                9.5 BITMAP.C 程序................................................... 283
   5.2 MAKEFILE 文件................................................... 78                        9.6 INODE.C 程序 .................................................... 288
   5.3 ASM.S 程序.......................................................... 80                    9.7 SUPER.C 程序 .................................................... 298
   5.4 TRAPS.C 程序 ...................................................... 87                     9.8 NAMEI.C 程序 .................................................... 306
   5.5 SYSTEM_CALL.S 程序.......................................... 94                            9.9 FILE_TABLE.C 程序............................................ 328
   5.6 MKTIME.C 程序 ................................................. 102                        9.10 BLOCK_DEV.C 程序.......................................... 328

                                                                                    - I -
目录


  9.11 FILE_DEV.C 程序.............................................. 331                  11.25 HDREG.H 文件................................................ 452
  9.12 PIPE.C 程序...................................................... 333              11.26 HEAD.H 文件.................................................. 454
  9.13 CHAR_DEV.C 程序 ........................................... 337                    11.27 KERNEL.H 文件.............................................. 455
  9.14 READ_WRITE.C 程序........................................ 340                      11.28 MM.H 文件..................................................... 456
  9.15 TRUNCATE.C 程序............................................ 343                    11.29 SCHED.H 文件 ................................................ 456
  9.16 OPEN.C 程序.................................................... 346                11.30 SYS.H 文件..................................................... 464
  9.17 EXEC.C 程序.................................................... 352                11.31 TTY.H 文件..................................................... 466
  9.18 STAT.C 程序 ..................................................... 366              11.32 INCLUDE/SYS/目录中的文件......................... 469
  9.19 FCNTL.C 程序 .................................................. 367                11.33 STAT.H 文件 ................................................... 469
  9.20 IOCTL.C 程序................................................... 369                11.34 TIMES.H 文件................................................. 470
                                                                                        11.35 TYPES.H 文件................................................. 471
第 10 章 内存管理(MM) .......................................... 371
                                                                                        11.36 UTSNAME.H 文件 ........................................... 472
  10.1 概述 ............................................................... 371           11.37 WAIT.H 文件................................................... 472
  10.2 总体功能描述 ............................................... 371
                                                                                     第 12 章 库文件(LIB)............................................... 475
  10.3 MAKEFILE 文件............................................... 375
  10.4 MEMORY.C 程序............................................... 377                   12.1 概述 ............................................................... 475
  10.5 PAGE.S 程序..................................................... 390               12.2 MAKEFILE 文件............................................... 475
                                                                                        12.3 _EXIT.C 程序 ................................................... 477
第 11 章 包含文件(INCLUDE) ............................... 393
                                                                                        12.4 CLOSE.C 程序 .................................................. 478
  11.1 概述 ............................................................... 393           12.5 CTYPE.C 程序 .................................................. 478
  11.2 INCLUDE/目录下的文件 ................................. 393                             12.6 DUP.C 程序 ...................................................... 479
  11.3 A.OUT.H 文件................................................... 393                12.7 ERRNO.C 程序.................................................. 480
  11.4 CONST.H 文件 .................................................. 402                12.8 EXECVE.C 程序................................................ 480
  11.5 CTYPE.H 文件 .................................................. 402                12.9 MALLOC.C 程序 ............................................... 481
  11.6 ERRNO.H 文件 ................................................. 403                 12.10 OPEN.C 程序 .................................................. 489
  11.7 FCNTL.H 文件 .................................................. 405                12.11 SETSID.C 程序................................................ 490
  11.8 SIGNAL.H 文件................................................. 407                 12.12 STRING.C 程序 ............................................... 491
  11.9 STDARG.H 文件 ................................................ 409                 12.13 WAIT.C 程序................................................... 491
  11.10 STDDEF.H 文件 .............................................. 410                  12.14 WRITE.C 程序 ................................................ 492
  11.11 STRING.H 文件 ............................................... 410
                                                                                     第 13 章 建造工具(TOOLS) .................................... 493
  11.12 TERMIOS.H 文件 ............................................ 420
  11.13 TIME.H 文件................................................... 426                13.1 概述 ............................................................... 493
  11.14 UNISTD.H 文件............................................... 428                  13.2 BUILD.C 程序................................................... 493
  11.15 UTIME.H 文件 ................................................ 433
                                                                                     参考文献 .................................................................... 501
  11.16 INCLUDE/ASM/目录下的文件 ....................... 435
  11.17 IO.H 文件....................................................... 435           附录 ............................................................................ 502
  11.18 MEMORY.H 文件............................................. 436
                                                                                        附录 1 内核主要常数............................................ 502
  11.19 SEGMENT.H 文件 ........................................... 436                    附录 2 内核数据结构............................................ 505
  11.20 SYSTEM.H 文件.............................................. 439
                                                                                        附录 3 80X86 保护运行模式 ................................. 512
  11.21 INCLUDE/LINUX/目录下的文件 .................... 442
  11.22 CONFIG.H 文件............................................... 442               索引 ............................................................................ 520
  11.23 FDREG.H 头文件 ............................................ 444
  11.24 FS.H 文件....................................................... 447




                                                                            - II -
序言




                             序言

本书是一本有关 Linux 操作系统内核基本工作原理的入门读物。


本书的主要目标
    本书的主要目标是使用尽量少的篇幅或在有限的篇幅内,对完整的 Linux 内核源代码进行解剖,以期
对操作系统的基本功能和实际实现方式获得全方位的理解。做到对 linux 内核有一个完整而深刻的理解,
对 linux 操作系统的基本工作原理真正理解和入门。
    本书读者群的定位是一些知晓 Linux 系统的一般使用方法或具有一定的编程基础,但比较缺乏阅读目
前最新内核源代码的基础知识,又急切希望能够进一步理解 UNIX 类操作系统内核工作原理和实际代码实
现的爱好者。这部分读者的水平应该界于初级与中级水平之间。目前,这部分读者人数在 Linux 爱好者中
所占的比例是很高的,而面向这部分读者以比较易懂和有效的手段讲解内核的书籍资料不多。


现有书籍不足之处
  目前已有的描述 Linux 内核的书籍,均尽量选用最新 Linux 内核版本(例如 Redhat 7.0 使用的 2.2.16
稳定版等)进行描述,但由于目前 Linux 内核整个源代码的大小已经非常得大(例如 2.2.20 版具有 268 万
行代码!,因此这些书籍仅能对 Linux 内核源代码进行选择性地或原理性地说明,许多系统实现细节被忽
     )
略。因此并不能给予读者对实际 Linux 内核有清晰而完整的理解。
  陆丽娜等同志翻译的 Scott Maxwell 著的一书《Linux 内核源代码分析》基本上是面对 Linux 中级水平
的读者,需要较为全面的基础知识才能完全理解。而且可能是由于篇幅所限,该书并没有对所有 Linux 内
核代码进行注释,略去了很多内核实现细节,例如其中内核中使用的各个头文件(*.h)、生成内核代码映象
文件的工具程序、各个 make 文件的作用和实现等均没有涉及。因此对于处于初中级水平之间的读者来说
阅读该书有些困难。
  去年初浙江大学出版的《Linux 内核源代码情景分析》一书,也基本有这些不足之处。甚至对于一些
具有较高 Linux 系统应用水平的计算机本科高年级学生,由于该书篇幅问题以及仅仅选择性地讲解内核源
代码,也不能真正吃透内核的实际实现方式,因而往往刚开始阅读就放弃了。这在本人教学的学生中基本
都会出现这个问题。该书刚面市时,本人曾极力劝说学生购之阅读,并在二个月后调查阅读学习情况,基
本都存在看不下去或不能理解等问题,大多数人都放弃了。
  John Lions 著的《莱昂氏 UNIX 源代码分析》一书虽然是一本学习 UNIX 类操作系统内核源代码很好
的书籍,但是由于其采用的是 UNIX V6 版,其中系统调用等部分代码是用早已过时的 PDP-11 系列机的汇
编语言编制的,因此在阅读与硬件部分相关的源代码时就会遇到较大的困难。
  A.S.Tanenbaum 的书《操作系统:设计与实现》是一本有关操作系统内核实现很好的入门书籍,但该
书所叙述的 minix 系统是一种基于消息传递的内核实现机制,与 Linux 内核的实现有所区别。因此在学习
该书之后,并不能很顺利地即刻着手进一步学习较新的 Linux 内核源代码实现。

   在使用这些书籍进行学习时会有一种“盲人摸象”的感觉,不能真正理解 Linux 内核系统具体实现的
整体概念,尤其是对那些 Linux 系统初学者或刚学会如何使用 Linux 系统的人在使用那些书学习内核原理
时,内核的整体运作结构并不能清晰地在脑海中形成。    这在本人多年的 Linux 内核学习过程中也深有体会,
也是写作本书的动机之一。

  为了填补这个空缺,本书的主要目标是使用尽量少的篇幅或在有限的篇幅内,对完整的 Linux 内核源
代码进行全面解剖,以期对操作系统的基本功能和实际实现方式获得全方位的理解。做到对 linux 内核有
一个完整而深刻的理解,对 linux 操作系统的基本工作原理真正理解和入门。


                              - 1 -
序言



阅读早期内核其它的好处?
  目前,已经出现不少基于 Linux 早期内核而开发的专门用于嵌入式系统的内核版本,如 DJJ 的 x86 操
作系统、Uclinux 等(在 www.linux.org 上有专门目录),世界上也有许多人认识到通过早期 Linux 内核源代
码学习的好处,目前国内也已经有人正在组织人力注释出版类似本文的书籍。因此,通过阅读 Linux 早期
内核版本的源代码,的确是学习 Linux 系统的一种行之有效的途径,并且对研究和应用 Linux 嵌入式系统
也有很大的帮助。

    在对早期内核源代码的注释过程中,作者已经发现,早期内核源代码几乎就是目前所使用的较新内核
的一个精简版本。其中已经包括了目前新版本中几乎所有的基本功能原理的内容。正如《系统软件:系统
编程导论》一书的作者 Leland L. Bach 在介绍系统程序以及操作系统设计时,引入了一种极其简化的简单
指令计算机(SIC)系统来说明所有系统程序的设计和实现原理,从而既避免了实际计算机系统的复杂性,又
能透彻地说明问题。这里选择 Linux 的早期内核版本作为学习对象,其指导思想与 Leland 的一致。这对
Linux 内核学习的入门者来说,无非是一个最理想的选择之一。能够在尽可能短的时间内深入理解 Linux
内核的基本工作原理。

  当然,使用早期内核作为学习的对象也有不足之处,所选用的 Linux 早期内核版本不包含对虚拟文件
系统 VFS 的支持、对网络系统的支持、 仅支持 a.out 执行文件和对其它一些现有内核中复杂子系统的说明。
但由于本书是作为 Linux 内核工作机理实现的入门教材,因此这也正是选择早期内核版本的优点之一。通
过学习本书,可以为进一步学习这些高级内容打下扎实的基础。


阅读完整源代码的重要性和必要性
  正如 Linux 系统的创始人在一篇新闻组投稿上所说的,要理解一个软件系统的真正运行机制,一定要
阅读其源代码(RTFSC – Read The Fucking Source Code)。系统本身是一个完整的整体,具有很多看似不重
要的细节存在,但是若忽略这些细节,就会对整个系统的理解带来困难,并且不能真正了解一个实际系统
的实现方法和手段。
  虽然通过阅读一些操作系统原理经典书籍(例如 M.J.Bach 的《UNIX 操作系统设计》            )能够对 UNIX
类操作系统的工作原理有一些理论上的指导作用,但实际上对操作系统的真正组成和内部关系实现的理解
仍不是很清晰。正如 AST 所说的,      “许多操作系统教材都是重理论而轻实践”“多数书籍和课程为调度算
                                                     ,
法耗费大量的时间和篇幅而完全忽略 I/O,其实,前者通常不足一页代码,而后者往往要占到整个系统三
分之一的代码总量。    ”内核中大量的重要细节均未提到。因此并不能让读者理解一个真正的操作系统实现
的奥妙所在。只有在详细阅读过完整的内核源代码之后,才会对系统有一种豁然开朗的感觉,对整个系统
的运作过程有深刻的理解。以后再选择最新的或较新内核源代码进行学习时,也不会碰到大问题,基本上
都能顺利地理解新代码的内容。


如何选择要阅读的内核代码版本
    那么,如何选择既能达到上述要求,又不被太多的内容而搞乱头脑,选择一个适合的 Linux 内核
版本进行学习,提高学习的效率呢?作者通过对大量内核版本进行比较和选择后,   最终选择了与目前 Linux
内核基本功能较为相近,又非常短小的 0.11 版内核作为入门学习的最佳版本。下图是对一些主要 Linux 内
核版本行数的统计。




                                - 2 -
序言



     10000000




      1000000




       100000




        10000




         1000
                V0.01

                        V0.11

                                V0.12

                                        V0.95



                                                          V0.97

                                                                  V1.0
                                                V0.96a




                                                                         V1.1.52

                                                                                   V1.2.13

                                                                                             V1.3.95

                                                                                                       V2.0.38

                                                                                                                 V2.2.20

                                                                                                                           V2.4.17
  目前的 Linux 内核源代码量都在几百万行的数量上,极其庞大,对这些版本进行完全注释和说明是不
可能的,而 0.11 版内核不超过 2 万行代码量,因此完全可以在一本书中解释和注释清楚。麻雀虽小,五脏
俱全。
  另外,使用该版本可以避免使用现有较新内核版本中已经变得越来越复杂得各子系统部分的研究(如
虚拟文件系统 VFS、ext2 或 ext3 文件系统、网络子系统、新的复杂的内存管理机制等)
                                              。


阅读本书需具备的基础知识
    在阅读本书时,作者希望读者具有以下一些基础知识或有相关的参考书籍在手边。其一是有关 80x86
处理器结构和编程的知识或资料。例如可以从网上下载的 80x86 编程手册(INTEL 80386 Programmer's
Reference Manual)
                ;其二是有关 80x86 硬件体系结构和接口编程的知识或资料。有关这方面的资料很多;
其三还应具备初级使用 Linux 系统的简单技能。

  另外,由于 Linux 系统内核实现,最早是根据 M.J.Bach 的《UNIX 操作系统设计》一书的基本原理开
发的,源代码中许多变量或函数的名称都来自该书,因此在阅读本书时,若能适当参考该书,更易于对源
代码的理解。

    Linus 在最初开发 Linux 操作系统时,参照了 Minix 操作系统。例如,最初的 Linux 内核版本完全照搬
了 Minix 1.0 文件系统。因此,在阅读本书时,A.S.Tanenbaum 的书《操作系统:设计与实现》也具有较大
的参考价值。但 Tanenbaum 的书描述的是一种基于消息传递在内核各模块之间进行通信(信息交换)            ,这
与 Linux 内核的工作机制不一样。因此可以仅参考其中有关一般操作系统工作原理章节和文件系统实现的
内容。


使用早期版本是否过时?
  表面看来本书对 Linux 早期内核版本注释的内容犹如 Linux 操作系统刚公布时 Tanenbaum 就认为其已


                                                         - 3 -
序言


经过时的(Linux is obsolete)想法一样,但通过学习本书内容,你就会发现,利用本书学习 Linux 内核,
由于内核源代码量短小而精干,因此会有极高的学习效率,能够做到事半功倍,快速入门。并且对继续进
一步选择新内核部分源代码的学习打下坚实的基础。在学习完本书之后,你将对系统的运作原理有一个非
常完整而实际的概念,这种完整概念能使人很容易地进一步选择和学习新内核源代码中的任何部分,而不
需要再去啃读代码量巨大的新内核中完整的源代码。


Ext2 文件系统与 Minix 文件系统?
  目前 Linux 系统上所使用的 Ext2(或最新的 Ext3)文件系统,是在内核 1.x 之后开发的文件系统,其
功能详尽并且其性能也非常完整和稳固,       是目前 Linux 操作系统上默认的标准文件系统。  但是,
                                                      作为对 Linux
操作系统完整工作原理入门学习所使用的部分,原则上是越精简越好。为了达到对一个操作系统有完整的
理解,并且能不被其中各子系统中的复杂性和过多的细节喧宾夺主,在选择学习剖析用的内核版本时,只
要系统的部分代码内容能说明实际工作原理,就越简单越好。
  Linux 内核 0.11 版上当时仅包含最为简单的 minix 1.0 文件系统,对于理解一个操作系统中文件系统的
实际组成和工作原理已经足够。这也是选择 Linux 早期内核版本进行学习的主要原因之一。

  在完整阅读完本书之后,相信您定会发出这样的感叹:    “对于 Linux 内核系统,我现在终于入门了!。
                                                      ”
此时,您应该有十分的把握去进一步学习最新 Linux 内核中各部分的工作原理和过程了。


                                                  同济大学
                                                  赵炯 博士
                                                  2002.12




                             - 4 -
1.1 Linux 的诞生和发展




                              第1章 概述

  本章首先回顾了 Linux 操作系统的诞生、开发和成长过程,由此理解本书为什么会选择 Linux 系统早
期版本作为学习对象的一些原因。然后具体说明了选择早期 Linux 内核版本进行学习的优点和不足之处以
及如何开始进一步的学习。


1.1 Linux 的诞生和发展
    Linux 操作系统是 UNIX 操作系统的一种克隆系统。它诞生于 1991 年的 10 月 5 日(这是第一次正式
向外公布的时间)      。以后借助于 Internet 网络,并经过全世界各地计算机爱好者的共同努力下,现已成为今
天世界上使用最多的一种 UNIX 类操作系统,并且使用人数还在迅猛增长。
Linux 操作系统的诞生、发展和成长过程始终依赖着以下五个重要支柱:UNIX 操作系统、MINIX 操作系
统、GNU 计划、POSIX 标准和 Internet 网络。
    下面主要根据这五个基本线索来追寻一下 Linux 的开发历程,它的酝酿过程,最初的发展经历。首先
分别介绍其中的四个基本要素(UNIX、MINIX、GNU 和 POSIX,Internet 的重要性显而易见,所以不用对
其罗嗦),然后根据 Linux 的创始人 Linus Toravlds 从对计算机感兴趣而自学计算机知识,到心里开始酝酿
编制一个自己的操作系统,到最初 Linux 内核 0.01 版公布,以及从此如何艰难地一步一个脚印地在全世界
hacker 的帮助下最后推出比较完善的 1.0 版本这段时间的发展经过,也即对 Linux 的早期发展历史进行详
细介绍。
    当然,  目前 Linux 内核版本已经开发到了 2.5.52 版。而大多数 Linux 系统中所用到的内核是稳定的 2.4.20
版内核。   (其中第 2 个数字奇数表示是正在开发的版本,        不能保证系统的稳定性)    对于 Linux 的一般发展史,
许多文章和书籍都有介绍,这里就不重复。
1.1.1 UNIX 操作系统的诞生
   Linux 操作系统是 UNIX 操作系统的一个克隆版本。             UNIX 操作系统是美国贝尔实验室的 Ken.Thompson
和 Dennis Ritchie 于 1969 年夏在 DEC PDP-7 小型计算机上开发的一个分时操作系统。
当时 Ken Thompson 为了能在闲置不用的 PDP-7 计算机上运行他非常喜欢的星际旅行(Space travel)游戏,
在 1969 年夏天乘他夫人回家乡加利福尼亚渡假期间,在一个月内开发出了 unix 操作系统的原型。当时使
用的是 BCPL 语言(基本组合编程语言)             ,后经 Dennis Ritchie 于 1972 年用移植性很强的 C 语言进行了改
写,使得 UNIX 系统在大专院校得到了推广。
1.1.2 MINIX 操作系统
  MINIX 系统是由 Andrew S. Tanenbaum(AST)开发的。AST 是在荷兰 Amsterdam 的 Vrije 大学数学与
计算机科学系统工作, ACM 和 IEEE 的资深会员(全世界也只有很少人是两会的资深会员)。
               是                                                  共发表了 100
多篇文章,5 本计算机书籍。
  AST 虽出生在美国纽约,但是是荷兰侨民(1914 年他的祖辈来到美国)。他在纽约上的中学、M.I.T 上
的大学、加洲大学 Berkeley 分校念的博士学位。由于读博士后的缘故,他来到了家乡荷兰。从此就与家乡
一直有来往。后来就在 Vrije 大学开始教书、带研究生了。荷兰首都 Amsterdam 是个常年阴雨绵绵的城市,
而对于 AST 来说,这最好不过了,因为这样他就可以待在家里摆弄他的计算机了。
  MINIX 是他 1987 年编制的,主要用于学生学习操作系统原理。到 91 年时版本是 1.5。目前主要有两
个版本在使用: 1.5 版和 2.0 版,当时该操作系统在大学使用是免费的,但其它用途不是,当然目前都已
经是免费的,可以从许多 FTP 上下载。
  对于 Linux 系统,他表示对其开发者 Linus 的称赞。但他认为 Linux 的发展有很大原因是因为他为了
保持 minix 的小型化,能让学生在一个学期内就能学完,而没有接纳全世界许多人对 Minix 的扩展要求。
因此这激发了 Linus 编写 Linux。Linus 正好抓住了这个好时机。
作为一个操作系统,MINIX 并不是优秀者,但它同时提供了用 C 语言和汇编语言写的系统源代码。这是
第一次使得有抱负的程序员或 hacker 能够阅读操作系统的源代码,            在当时这种源代码是软件商一直小心地
守护着的。


                                    - 5 -
1.1 Linux 的诞生和发展


1.1.3 GNU 计划
     GNU 计划和自由软件基金会(the Free Software Foundation – FSF)是由 Richard M. Stallman 于 1984 年一
手创办的。旨在开发一个类似 Unix、并且是自由软件的完整操作系统:GNU 系统。 GNU 是"GNU's Not           (
Unix"的递归缩写,   它的发音为"guh-NEW"。 各种使用 linux 作为核心的 GNU 操作系统正在被广泛的使用。
                              )
虽然这些系统通常被称作"Linux",但是严格地说,它们应该被称为 GNU/Linux 系统。
到上世纪 90 年代初,   GNU 项目已经开发出许多高质量的免费软件,                 其中包括有名的 emacs 编辑系统、            bash
shell 程序、gcc 系列编译程序、gdb 调试程序等等。这些软件为 Linux 操作系统的开发创造了一个合适的
环境, Linux 能够诞生的基础之一。
       是                  以至于目前许多人都将 Linux 操作系统称为 GNU/Linux”操作系统。“
1.1.4 POSIX 标准
    POSIX(Portable Operating System Interface for Computing Systems)是由 IEEE 和 ISO/IEC 开发的一簇标
准。该标准是基于现有的 UNIX 实践和经验,描述了操作系统的调用服务接口,用于保证编制的应用程序
可以在源代码一级上在多种操作系统上移植运行。它是在 1980 年早期一个 UNIX 用户组(usr/group)的早期
工作的基础上取得的。该 UNIX 用户组原来试图将 AT&T 的系统 V 和 Berkeley CSRG 的 BSD 系统的调用
接口之间的区别重新调和集成,从而于 1984 年产生了/usr/group 标准。1985 年,IEEE 操作系统技术委员
会标准小组委员会(TCOS-SS)开始在 ANSI 的支持下责成 IEEE 标准委员会制定有关程序源代码可移植
性操作系统服务接口正式标准。到了 1986 年 4 月,IEEE 就制定出了试用标准。第一个正式标准是在 1988
年 9 月份批准的(IEEE 1003.1-1988)        ,也既以后经常提到的 POSIX.1 标准。
    1989 年 POSIX 的工作被转移至 ISO/IEC 社团,并由 15 工作组继续将其制定成 ISO 标准。到 1990 年,
POSIX.1 与已经通过的 C 语言标准联合,正式批准为 IEEE 1003.1-1990(也是 ANSI 标准)和 ISO/IEC
9945-1:1990 标准。
    POSIX.1 仅规定了系统服务应用程序编程接口(API)                       ,仅概括了基本的系统服务标准,因此期望对系
统的其它功能也制定出标准。这样 IEEE POSIX 的工作就开始展开了。在 1990 年,刚开始有十个批准的计
划在进行,有近 300 多人参加每季度为期一周的会议。着手的工作有命令与工具标准(POSIX.2)、测试方法
标准(POSIX.3)   、实时 API(POSIX.4)等。到了 1990 年上半年已经有 25 个计划在进行,并且有 16 个工
作组参与了进来。与此同时,还有一些组织也在制定类似的标准,如 X/Open,AT&T,OSF 等。
    在 90 年代初,POSIX 标准的制定正处在最后投票敲定的时候,那是 1991-1993 年间。此时正是 Linux
刚刚起步的时候,这个 UNIX 标准为 Linux 提供了极为重要的信息,使得 Linux 的能够在标准的指导下进
行开发,能够与绝大多数 UNIX 系统兼容。在最初的 Linux 内核代码中(0.01 版、0.11 版)就已经为 Linux
与 POSIX 标准的兼容做好了准备工作。在 0.01 版的内核/include/unistd.h 文件中就已经定义了几个有关
POSXI 标准要求的常数符号,并且在注释中就写到“ok,这也许是个玩笑,但我正在着手研究它呢”                                          。
    1991 年 7 月 3 日在 comp.os.minix 上发布的 post 上就已经提到了正在搜集 POSIX 的资料。(当然此时
还不存在 Linux 这个名称,当时 Linus 的脑子里想的可能是 FREAX ☺,FREAX 的英文含义是怪诞的、怪
物、异想天开等)。其中透露了他正在进行 Linux 系统的开发,并且在 Linux 最初的时候已经想到要实现与
POSIX(UNIX 的国际标准)的兼容问题了。
1.1.5 Linux 操作系统的诞生
    1981 年 IBM 公司推出享誉全球的微型计算机 IBM PC。在 1981-1991 年间,MS-DOS 操作系统一直是
微型计算机上操作系统的主宰。此时计算机硬件价格虽然逐年下降,但软件价格仍然是居高不下。当时
Apple 的 MACs 操作系统可以说是性能最好的,但是其天价没人能够轻易靠近。
    当时的另一个计算机技术阵营是 Unix 世界。但是 Unix 操作系统就不仅是价格贵的问题了。为了寻求
高利率,Unix 经销商将价格抬得极高,PC 小用户就根本不能靠近它。曾经一度受到 Bell Labs 的许可而可
以在大学中用于教学的 UNIX 源代码一直被小心地守卫着不需公开。对于广大的 PC 用户,软件行业的大
型供应商始终没有给出有效的解决该问题的手段。
    正在此时,出现了 MINIX 操作系统,并有一本详细的书本描述它的设计实现原理。由于 AST 的书写
的非常详细,并且叙述有条有理,几乎全世界的计算机爱好者都在看这本书以理解操作系统的工作原理。
其中也包括 Linux 系统的创始者 Linus Benedict Torvalds。
    当时(1991 年),Linus Benedict Torvalds 是赫尔辛基大学计算机科学系的二年级学生,也是一个自学
hacker。这个 21 岁的芬兰年轻人喜欢鼓捣计算机,测试计算机的能力和限制。但当时缺乏的是一个专业级
的操作系统。MINIX 虽然很好,但只是一个用于教学目的简单操作系统,而不是一个强有力的实用操作系
统。
    到 1991 年,GNU 计划已经开发出了许多工具软件。最受期盼的 Gnu C 编译器已经出现,但还没有开

                                           - 6 -
1.1 Linux 的诞生和发展


发出免费的 GNU 操作系统。即使是 MINIX 也开始有了版权,需要购买才能得到源代码。而 GNU 的操作
系统 HURD 一直在开发之中,但并不能在几年内完成。
     对于 Linus 来说,已经不能等待了。从 1991 年 4 月份起,他开始酝酿并着手编制自己的操作系统。刚
开始,他的目的很简单,只是为了学习 Intel 386 体系结构保护模式运行方式下的编程技术。但后来 Linux
的发展却完全改变了初衷。
     1991 年初,     Linux 开始在一台 386sx 兼容微机上学习 minix 操作系统。     通过学习,  他逐渐不能满足 minix
系统的现有性能,并开始酝酿开发一个新的免费操作系统。根据 Linus 在 comp.os.minix 新闻组上发布的消
息,我们可以知道他逐步从学习 minix 系统到开发自己的 Linux 的过程。
     Linus 第 1 次向 comp.os.minix 投递消息是在 1991 年 3 月 29 日。题目是“gcc on minix-386 doesn’t
optimize”,是有关 gcc 编译器在 minix-386 上运行的优化问题,由此可知,Linus 在 1991 年的初期已经开始
深入研究了 minix 系统,并在这段时间有了改进 minix 操作系统的思想,而且在进一步学习 minix 系统中,
逐步演变为想自己重新设计一个基于 Intel 80386 体系结构的新操作系统。
     他在回答有人提出 minix 上的一个问题时,                所说的第一句话是   “阅读源代码” “RTFSC (Read the F**ing
                                                               (
Source Code :-)”)  。他认为答案就在源程序中。这也说明了对于学习系统软件来说,你不光需要懂得系统的
工作基本原理,还需要结合实际系统,学习实际系统的实现方法。因为理论毕竟是理论,其中省略了许多
枝节,而这些枝节问题虽然没有太多的理论含量,但却是一个系统必要的组成部分,就象麻雀身上的一根
羽毛。
     从 1991 年的 4 月份开始,Linus 几乎花了全部时间研究 386-minix 系统(hack the kernel),并且尝试着
移植 GNU 的软件到该系统上(GNU gcc、bash、gdb 等)。并于 4 月 13 日在 comp.os.minix 上发布说自己已
经成功地将 bash 移植到了 minix 上,而且已经爱不释手、不能离开这个 shell 软件了。
     第一个与 Linux 有关的消息是在 1991 年 7 月 3 日在 comp.os.minix 上发布的(当然此时还不存在 Linux
这个名称,        当时 Linus 的脑子里想的可能是 FREAX ☺,        FREAX 的英文含义是怪诞的、     怪物、异想天开等)。
其中透露了他正在进行 Linux 系统的开发,并且在 Linux 最初的时候已经想到要实现与 POSIX(UNIX 的
国际标准)的兼容问题了。
     在 Linus 的下一发布的消息中(1991 年 8 月 25 日 comp.os.minix),   他向所有 minix 用户询问 What would
                                                                        “
you like to see in minix?”(“你最想在 minix 中见到什么?”),在该消息中他首次透露出正在开发一个(免费
的)386(486)操作系统,并且说只是兴趣而已,代码不会很大,也不会象 GNU 的那样专业。开发免费操作
系统这个想法从 4 月份就开始酝酿了,希望大家反馈一些对于 minix 系统中喜欢那些特色不喜欢什么等信
息,由于实际的和其它一些原因,新开发的系统刚开始与 minix 很象(并且使用了 minix 的文件系统)                             。并
且已经成功地将 bash(1.08 版)和 gcc(1.40 版)移植到了新系统上,而且在过几个月就可以实用了。
     最后,     Linus 申明他开发的操作系统没有使用一行 minix 的源代码;           而且由于使用了 386 的任务切换特
性,所以该操作系统不好移植(没有可移植性)                       ,并且只能使用 AT 硬盘。对于 Linux 的移植性问题,Linus
当时并没有考虑。但是目前 Linux 几乎可以运行在任何一种硬件体系结构上。
     到了 1991 年的 10 月 5 日,Linus 在 comp.os.minix 新闻组上发布消息,正式向外宣布 Linux 内核系统
的诞生(Free minix-like kernel sources for 386-AT)
                                             。这段消息可以称为 Linux 的诞生宣言,并且一直广为流
传。因此 10 月 5 日对 Linux 社区来说是一个特殊的日子,                许多后来 Linux 的新版本发布时都选择了这个日
子。所以 RedHat 公司选择这个日子发布它的新系统也不是偶然的。
1.1.6 Linux 操作系统版本的变迁
0.00 (1991.2-4) 两个进程分别显示 AAA BBB
0.01 (1991.9?)第一个正式向外公布的 Linux 内核版本。
0.02 (1991.10.5)该版本以及 0.03 版是内部版本,目前已经无法找到。
0.10 (1991.10)由 Ted Ts’o 发布的 Linux 内核版本。
0.11 (1991.12.8)基本可以正常运行的内核版本(也是本书着重注释的版本)  。
0.12 (1992.1.15)主要加入对数学协处理器的软件模拟程序。
0.95(0.13) (1992.3.8) 开始加入虚拟文件系统思想的内核版本。
0.96 (1992.5.12)开始加入网络支持和虚拟文件系统 VFS。
0.97 (1992.8.1)
0.98 (1992.9.29)
0.99 (1992.12.13)
1.0 (1994.3.14)
1.20 (1995.3.7)

                                        - 7 -
1.1 Linux 的诞生和发展


2.0 (1996.2.9)
2.20 (1999.1.26)
2.40 (2001.1.4)
到现在为止,最新版是

                          表1.1 表新内核源代码字节数
         内核版本号            发布日期                             代码总字节数
         2.4.20           2002.11.29                       26,200,000
         2.5.52           2002.12.16                       30,000,000

    将 Linux 系统 0.13 版内核直接改称 0.95 版,Linus 的意思是让大家不要觉得离 1.0 版还很遥远。同时,
从 0.95 版开始,对内核的许多改进之处(补丁程序的提供)均以其他人为主了,而 Linus 的主要任务开始变
成对内核的维护和决定是否采用某个补丁程序。
1.1.7 Linux 名称的来由
  Linux 操作系统刚开始时并没有被称作 Linux,        Linus 给他的操作系统取名为 FREAX,其英文含义是怪
诞的、怪物、异想天开等意思。在他将新的操作系统上载到 ftp.funet.fi 服务器上时,管理员 Ari Lemke 很
不喜欢这个名称。    他认为既然是 Linus 的操作系统就取其谐音 Linux 作为该操作系统的目录吧,         于是 Linux
这个名称就开始流传下来。
  在 Linus 的自传《Just for Fun》一书中,Linus 解释说:
  “坦白地说,    我从来没有想到过要用 Linux 这个名称发布这个操作系统,          因为这个名字有些太自负了。
而我为最终发布版准备的是什么名字呢?Freax。实际上,内核代码中某些早期的 Makefile - 用于描述如何
编译源代码的文件 - 文件中就已经包含有“Freax”这个名字了,大约存在了半年左右。但其实这也没什么
关系,在当时还不需要一个名字,因为我还没有向任何人发布过内核代码。                  ”
  “而 Ari Lemke,他坚持要用自己的方式将内核代码放到 ftp 站点上,         并且非常不喜欢 Freax 这个名字。
他坚持要用现在这个名字(Linux),我承认当时我并没有跟他多争论。但这都是他取的名字。所以我可以光
明正大地说我并不自负,或者部分坦白地说我并没有本位主义思想。但我想好吧,这也是个好名字,而且
以后为这事我总能说服别人,就象我现在做的这样。               ”

                                           -- Linus Torvalds《Just for fun》第 84-88 页。
1.1.8 早期 Linux 系统开发的主要贡献者
     从 Linux 的早期源代码中可知,         Linux 系统的早期主要开发人员除了 Linus 本人以外,    最著名的人员之
一就是 Theodore Ts'o (Ted Ts'o)。他 1990 年毕业于 MIT 计算机科学专业。在大学时代他就积极参加学校中
举办的各种学生活动。他喜欢烹饪、骑自行车,当然还有就是 hacking on Linux,后来他开始喜欢起业余无
线电报运动。目前他在 IBM 工作从事系统编程及其它重要事务。他还是国际网络设计、操作、销售和研究
者开放团体 IETF 成员。
     Linux 在世界范围内的流行也有他很大的功劳。早在 Linux 操作系统刚问世时,他就怀着极大的热情
为 linux 的发展提供了 maillist,几乎是在 Linux 刚开始发布起(1991 年开始)就一直为 Linux 做出贡献的人,
也是最早向 Linux 内核添加程序的人(Linux 内核 0.10 版中的虚拟盘驱动程序 ramdisk.c 和内核内存分配程
序 kmalloc.c)。直到目前仍然从事着与 Linux 有关的工作。他当时在北美洲地区最早设立了 linux 的 ftp 站
点(tsx-11.mit.edu),而且至今仍然为广大 linux 用户提供服务。他对 linux 作出的最大贡献之一是提出并实
现了 ext2 文件系统。该文件系统已成为 linux 世界中事实上的文件系统标准。最近他又推出了 ext3 文件系
统,大大提高了文件系统的稳定性和访问效率。作为对他的推崇,第 97 期(2002 年 5 月)的 linuxjournal
期刊将他作为了封面人物,并对他进行了采访。目前,他为 IBM linux 技术中心工作,并从事着有关
LSB(Linux Standard Base)等方面的工作。
     Linux 社区中另一位著名人物是 Alan Cox。他原工作于英国威尔士斯旺西大学(Swansea University
College)。刚开始他特别喜欢玩电脑游戏,尤其是 MUD(Multi-User Dungeon or Dimension,多用户网络游
戏) 。在 90 年代早期 games.mud 新闻组的 posts 中你可以找到他发表的大量 posts。他甚至为此还写了一偏
MUD 的发展史(rec.games.mud 新闻组,1992 年 3 月 9 日,A history of MUD)。由于 MUD 游戏与网络密切
相关,慢慢的他对计算机网络开始感兴趣。为了玩游戏并提高电脑运行游戏的速度以及网络传输的速度,
他开始接触各种类型的操作系统,为他的游戏选择一个最为满意的平台。由于没钱,即使 Minix 他都买不
起,当 Linux 0.11 和 386BSD 发布时,他考虑良久总算买了一台 386SX 电脑。由于 386BSD 需要数学协处

                                   - 8 -
1.2 内容综述


理器的支持,而 386SX 中是不带的,所以他安装了 Linux 系统。于是他开始学习带有免费源代码的 Linux。
开始对 Linux 产生了兴趣,尤其是有关网络方面的实现。在关于 Linux 的单用户运行模式问题的讨论中,
他甚至赞叹 Linux 实现的巧妙(beautifully)。
    Linux 0.95 版发布之后, 他开始为 Linux 系统编写补丁程序     (修改程序)(记得他最早的两个补丁程序,
都没有被 Linus 采纳)    ,成为 Linux 上 TCP/IP 网络代码的最早使用人之一。后逐渐加入 Linux 的开发队伍,
并开始成为维护 Linux 内核源代码的主要负责人之一,也可以说成为 Linux 社团中在 Linus 之后最为重要
的人物。以后 Microsoft 公司曾经邀请他加盟,但他却干脆地拒绝了。从 2001 年开始他负责维护 Linux 内
核 2.4.x 的代码(Linus 主要负责开发最新开发版内核的研制(奇数版,比如 2.5.x 版)。
    《内核骇客手册》一书的作者 Michael K. Johnson 也是最早接触 Linux 操作系统的人之一(从 0.97 版)。
他还是著名 Linux 文档计划(Linux Document Project - LDP)的发起者之一。曾经在 Linux Journel 工作,
现在 RedHat 公司工作。
    Linux 系统并不是仅有这些中坚力量就能发展成今天这个样子的,还有许多计算机高手对 Linux 做出
了极大的贡献,这里就不一一列举了。主要贡献者的具体名单可参见 Linux 内核中的 CREDITS 文件,其
中以字母顺序列出了对 Linux 做出较大贡献的近 400 人的名单列表,包括他们的 email 地址和通信地址、
主页以及主要贡献事迹等信息。

通过上述说明,我们可以对上述 Linux 的五大支柱归纳如下:
    UNIX 操作系统 -- UNIX 于 1969 年诞生在 Bell 实验室。Linux 就是 UNIX 的一种克隆系统。UNIX 的
重要性就不用多说了。
    MINIX 操作系统 -- Minix 操作系统也是 UNIX 的一种克隆系统,它于 1987 年由著名计算机教授
Andrew S. Tanenbaum 开发完成。由于 MINIX 系统的出现并且提供源代码(只能免费用于大学内)在全世界
的大学中刮起了学习 UNIX 系统旋风。Linux 刚开始就是参照 Minix 系统于 1991 年才开始开发。
    GNU 计划-- 开发 Linux 操作系统,以及 Linux 上所用大多数软件基本上都出自 GNU 计划。Linux 只
是操作系统的一个内核,没有 GNU 软件环境(比如说 bash shell),则 Linux 将寸步难行。
    POSIX 标准 -- 该标准在推动 Linux 操作系统以后朝着正规路上发展起着重要的作用。是 Linux 前进
的灯塔。
    INTERNET -- 如果没有 Intenet 网,没有遍布全世界的无数计算机骇客的无私奉献,那么 Linux 最多
只能发展到 0.13(0.95)版的水平。


1.2 内容综述
  本文将主要对 Linux 的早期内核 0.11 版进行详细描述和注释。Linux-0.11 版本是在 1991 年 12 月 8 日
发布的。在发布时包括以下文件:

   bootimage.Z          - 具有美国键盘代码的压缩启动映像文件;
   rootimage.Z          - 以 1200kB 压缩的根文件系统映像文件;
   linux-0.11.tar.Z- 内核源代码文件;
   as86.tar.Z     - linux bruce evans'二进制执行文件;
                          是 16 位的汇编程序和装入程序;
   INSTALL-0.11         - 更新过的安装信息文件。

   目前除了原来的 rootimage.Z 文件,其它四个文件均能找到。

   本文主要详细分析 linux-0.11 内核中的所有源代码程序,对每个源程序文件都进行了详细注释,包括
对 Makefile 文件的注释。分析过程主要是按照计算机启动过程进行的。因此分析的连贯性到初始化结束内
核开始调用 shell 程序为止。其余的各个程序均针对其自身进行分析,没有连贯性,因此可以根据自己的需
要进行阅读。但在分析时还是提供了一些应用实例。

  所有的程序在分析过程中如果遇到作者认为是较难理解的语句时,        将给出相关知识的详细介绍。比如,
在阅读代码头一次遇到 C 语言内嵌汇编码时,将对 gnu C 语言的内嵌汇编语言进行较为详细的介绍;在遇
到对中断控制器进行输入/输出操作时,将对 Intel 中断控制器(8259A)芯片给出详细的说明,并列出使用

                                    - 9 -
1.2 内容综述


的命令和方法。这样做有助于加深对代码的理解,又能更好的了解所用硬件的使用方法,作者认为这种解
读方法要比单独列出一章内容来总体介绍硬件或其它知识要效率高得多。

     拿 Linux 0.11 版内核来“开刀”是为了提高我们认识 Linux 运行机理的效率。Linux-0.11 版整个内核
源代码只有 325K 字节左右,其中包括的内容基本上都是 Linux 的精髓。而目前最新的 2.5.XX 版内核非常
大,将近有 188 兆字节,即使你花一生的经历来阅读也未必能全部都看完。也许你要问“既然要从简入手,
为什么不分析更小的 Linux 0.01 版内核源代码呢?它只有 240K 字节左右”主要原因是因为 0.01 版的内核
代码有太多的不足之处,甚至还没有包括对软盘的驱动程序,也没有很好地涉及数学协处理器的使用以及
对登陆程序的说明。并且其引导启动程序的结构也与目前的版本不太一样,而 0.11 版的引导启动程序结构
则与现在的基本上是一样的。另外一个原因是可以找到 0.11 版早期的已经编译制作好的内核映像文件
(bootimage),可以用来进行引导演示。如果再配上简单的根文件系统映像文件(rootimage),那么它就可以
进行正常的运行了。

    拿 Linux 0.11 版进行学习也有不足之处,比如该内核版本中尚不包括有关进程等待队列、TCP/IP 网络
等方面的一些当前非常重要的代码,但好在 Linux 中的网络代码基本上是自成一体的,与内核机制关系不
是非常大,因此可以在了解了 Linux 工作的基本原理之后再去分析这些代码。
    本文对 Linux 内核中所有的代码都进行了说明。为了保持结构的完整性,对代码的说明是以内核中源
代码的组成结构来进行的,基本上是以每个源代码中的目录为一章内容进行介绍。介绍的源程序文件的次
序可参见前面的文件列表索引。整个 Linux 内核源代码的目录结构如下列表 1.1 所示。所有目录结构均是
以 linux 为当前目录。

                                列表 1.1 Linux/目录

                     名称         大小           最后修改日期(GMT)           说明
                     boot/                   1991-12-05 22:48:49
                     fs/                     1991-12-08 14:08:27
                     include/                1991-09-22 19:58:04
                     init/                   1991-12-05 19:59:04
                     kernel/                 1991-12-08 14:08:00
                     lib/                    1991-12-08 14:10:00
                     mm/                     1991-12-08 14:08:21
                     tools/                  1991-12-04 13:11:56

                     Makefile   2887 bytes   1991-12-06 03:12:46



  本书内容可以分为三个部分。第 1 章至第 4 章是描述内核引导启动和 32 位运行方式的准备阶段,作
为学习内核的初学者应该全部进行阅读。第二部分从第 5 章到第 10 章是内核代码的主要部分。其中第 5
章内容可以作为阅读本部分后续章节的索引来进行。第 11 章到第 13 章是第三部分内容,可以作为阅读第
二部分代码的参考。

    第 2 章概要地描述了 Linux 操作系统的体系结构,内核源代码文件放置的组织结构以及每个文件大致
功能。还介绍了 Linux 对物理内存的使用分配方式以及对虚拟线性地址的使用分配,以及如何在 RedHat 9
操作系统上编译本书所讨论的 linux 内核,   对内核代码需要修改的地方。    最后开始注释内核程序包中 Linux/
目录下的所看到的第一个文件,也即内核代码的总体 Makefile 文件的内容。该文件是所有内核源程序的编
译管理配置文件,供编译管理工具软件 make 使用。
    第 3 章将详细注释 boot/目录下的三个汇编程序,    其中包括磁盘引导程序 bootsect.s、获取 BIOS 中参数
的 setup.s 汇编程序和 32 位运行启动代码程序 head.s。这三个汇编程序完成了内核从块设备上引导加载到
内存,对系统配置参数的进行探测,完成了进入 32 位保护模式运行之前的所有工作。为内核系统进一步
的初始化工作作好了准备。


                                       - 10 -
1.2 内容综述


    第 4 章主要介绍 init/目录中内核系统的初始化程序 main.c。它是内核完成所有初始化工作并进入正常
运行的关键地方。在完成了系统所有的初始化工作后,创建了用于 shell 的进程。在介绍该程序时将需要查
看其所调用的其它程序,因此对后续章节的阅读可以按照这里调用的顺序进行。由于内存管理程序的函数
在内核中被广泛使用,因此该章内容应该最先选读。当你能真正看懂直到 main.c 程序为止的所有程序时,
你应该已经对 Linux 内核有了一定的了解,可以说已经有一半入门了☺,但你还需要对文件系统、系统调
用、各种驱动程序等进行更深一步的阅读。
    第 5 章主要介绍 kenel/目录中的所有程序。其中最重要的部分是进程调度函数 schedule()、sleep_on()
函数和有关系统调用的程序。此时你应该已经对其中的一些重要程序有所了解。
    第 6 章对 kernel/dev_blk/目录中的块设备程序进行了注释说明。该章主要含有硬盘、软盘等块设备的
驱动程序,主要与文件系统和高速缓冲区打交道。因此,在阅读这章内容时需首先浏览一下文件系统的章
节。
    第 7 章对 kernel/dev_chr/目录中的字符设备驱动程序进行注释说明。这一章中主要涉及到串行线路驱
动程序,键盘驱动程序和显示器驱动程序,因此含有较多与硬件有关的内容。在阅读时需要参考一下相关
硬件的书籍。
    第 8 章介绍 kernel/math/目录中的数学协处理器的仿真程序。     由于本书所注释的内核版本,   还没有真正
开始支持协处理器,因此本章的内容较少,也比较简单。只需有一般性的了解即可。
    第 9 章介绍内核源代码 fs/目录中的文件系统程序,在看这章内容时建议你能够暂停一下而去阅读
Andrew S. Tanenbaum 的《操作系统设计与实现》一书中有关 minix 文件系统的章节,因为最初的 Linux 系
统是只支持 minix 一种文件系统,Linux 0.11 版也不例外。
    第 10 章解说 mm/目录中的内存管理程序。要透彻地理解这方面的内容,需要对 Intel 80X86 微处理器
的保护模式运行方式有足够的理解,             因此本章在适当的地方包含有较为完整的有关 80X86 保护模式运行方
式的说明,这些知识基本上都可以参考 Intel 80386 程序员编程手册(Intel 80386 Programmer's Reference
Manual)。但在此章中,以源代码中的运用实例为对象进行解说,应该可以更好地理解它的工作原理。
    现有的 Linux 内核分析书籍都缺乏对内核头文件的描述,因此对于一个初学者来讲,在阅读内核程序
时会碰到许多障碍。         本书的第 11 章对 include/目录中的所有头文件进行了详细说明,基本上对每一个定义、
每一个常量或数据结构都进行了详细注释。为了便于在阅读时参考查阅,本书在附录中还对一些经常要用
到的重要的数据结构和变量进行了归纳注释,但这些内容实际上都能在这一章中找到。虽然该章内容主要
是为阅读其它章节中的程序作参考使用的,但是若想彻底理解内核的运行机制,仍然需要了解这些头文件
中的许多细节。
    第 12 章介绍了 Linux 0.11 版内核源代码 lib/目录中的所有文件。这些库函数文件主要向编译系统等系
统程序提供了接口函数,对以后理解系统软件会有较大的帮助。由于这个版本较低,所以这里的内容并不
是很多,可以很快地看完。这也是我们为什么选择 0.11 版的原因之一。
    第 13 章介绍 tools/目录下的 build.c 程序。这个程序并不会包括在编译生成的内核映像(image)文件中,
它仅用于将内核中的磁盘引导程序块与其它主要内核模块连接成一个完整了内核映像 kernel image)   (           文件。
    最后是附录和索引。附录中给出了 Linux 内核中的一些常数定义和基本数据结构定义,以及保护模式
运行机制的简明描述。

  为了便于查阅,在本书的附录中还单独列出了内核中要用到的有关 PC 机硬件方面的信息。在参考文
献中,我们仅给出了在阅读源代码时可以参考的书籍、文章等信息,并没有包罗万象地给出一大堆的繁杂
凌乱的文献列表。比如在引用 Linux 文档项目 LDP(Linux Document Project)中的文件时,我们会明确地
列出具体需要参考哪一篇 HOWTO 文章,而并不是仅仅给出 LDP 的网站地址了事。
  Linus 在最初开发 Linux 操作系统内核时,主要参考了 3 本书。一本是 M. J. Bach 著的《UNIX 操作系
统设计》 (文献[11]),该书描述了 UNIX 系统 V 内核的工作原理和数据结构。Linus 使用了该书中很多函数
的算法,Linux 内核源代码中很多重要函数的名称都取自该书。因此,在阅读本书时,这是一本必不可少
的内核工作原理方面的参考书籍。       另一本是 John H. Crawford 等编著的 Programming the 80386》文献[21])
                                               《                      (      ,
是讲解 80x86 下保护模式编程方法的好书。还有一本就是 Andrew S.Tanenbaum 著的《MINIX 操作系统设
计与实现》一书的第 1 版(文献[22]) Linus 主要使用了该书中描述的 MINIX 文件系统 1.0 版,而且在早
                        。
期的 Linux 内核中也仅支持该文件系统,所以在阅读本书有关文件系统一章内容时,文件系统的工作原理
方面的知识完全可以从 Tanenbaum 的书中获得。

   在对每个程序进行解说时,我们首先简单说明程序的主要用途和目的、输入输出参数以及与其它程序

                                    - 11 -
1.3 本章小结


的关系,然后列出程序的完整代码并在其中对代码进行详细注释,注释时对原程序代码或文字不作任何方
面的改动或删除,因为 C 语言是一种英语类语言,程序中原有的少量英文注释对常数符号、变量名等也提
供了不少有用的信息。在代码之后是对程序更为深入的解剖,并对代码中出现的一些语言或硬件方面的相
关知识进行说明。如果在看完这些信息后回头再浏览一遍程序,你会有更深一层的体会。
   对于阅读本书所需要的一些基本概念知识的介绍都散布在各个章节相应的地方,这样做主要是为了能
够方便的找到,而且在结合源代码阅读时,对一些基本概念能有更深的理解。
   最后要说明的是当你已经完全理解了本文说解说的一切时,并不代表你已经成为一个 Linux 行家了,
你只是刚刚踏上 Linux 的征途,具有了一定的成为一个 Linux GURU 的初步知识。这时你应该去阅读更多
的源代码,最好是循序渐进地从 1.0 版本开始直到最新的正在开发中的奇数编号的版本。在撰写这本书时
最新的 Linux 内核是 2.5.44 版。当你能快速理解这些开发中的最新版本甚至能提出自己的建议和补丁
(patch)程序时,我也甘拜下风了☺。


1.3 本章小结
   首先阐述了 Linux 诞生和发展不可缺少的五个支柱:UNIX 最初的开放原代码版本为 Linux 提供了实
现的基本原理和算法、Rechard Stallman 的 GNU 计划为 Linux 系统提供了丰富且免费的各种实用工具、
POSIX 标准的出现为 Linux 提供了实现与标准兼容系统的参考指南、A.S.T 的 MINIX 操作系统为 Linux 的
诞生起到了不可忽缺的参考、Internet 是 Linux 成长和壮大的必要环境。最后本章概述了书中的基本内容。

  最后祝你征途愉快!




                              - 12 -
2.1 Linux 内核模式




                第2章 linux 内核体系结构

  本章首先概要介绍了早期 Linux 内核的编制模式和体系结构,然后详细描述了 linux 内核源代码目录
中组织形式以及子目录中各个代码文件的主要功能以及基本调用的层次关系。接下来就直接切入正题,从
内核源文件 linux/目录下的第一个文件 Makefile 开始,对每一行代码进行详细注释说明。

  一个完整可用的操作系统主要由 4 部分组成:硬件、操作系统内核、操作系统服务和用户应用程序,
见图 2.1 所示。用户应用程序是指那些字处理程序、Internet 浏览器程序或用户自行编制的各种应用程序;
操作系统服务程序是指那些向用户所提供的服务被看作是操作系统的部分功能的程序。在 Linux 操作系统
上,这些程序包括 X 窗口系统、shell 命令解释系统以及那些内核编程接口等系统程序;操作系统内核程序
即是本书所感兴趣的部分,它主要用于对硬件资源的抽象和访问调度。



                          用户应用程序

                          操作系统服务

                          操作系统内核

                           硬件系统


                       图2.1 操作系统组成部分。

  Linux 内核的主要用途就是为了与计算机硬件进行交互,实现对硬件部件的编程控制和接口操作,调
度对硬件资源的访问,并为计算机上的用户程序提供一个高级的执行环境和对硬件的虚拟接口。
在本章内容中,我们首先基于 Linux 0.11 版的内核源代码,简明地描述 Linux 内核的基本体系结构、主要
构成模块。然后对源代码中出现的几个重要数据结构进行说明。最后描述了构建 Linux 0.11 内核编译实验
环境的方法。


2.1 Linux 内核模式
  目前,操作系统内核的结构模式主要可分为整体式的单内核模式和层次式的微内核模式。而本书所注
释的 Linux 0.11 内核,则是采用了单内核模式。单内核模式的主要优点是内核代码结构紧凑、执行速度快,
不足之处主要是层次结构性不强。
  在单内核模式的系统中,操作系统所提供服务的流程为:应用主程序使用指定的参数值执行系统调用
指令(int x80),使 CPU 从用户态(User Mode)切换到核心态(Kernel Model)
                                                    ,然后操作系统根据具体的
参数值调用特定的系统调用服务程序,而这些服务程序则根据需要再底层的一些支持函数以完成特定的功
能。在完成了应用程序所要求的服务后,操作系统又从核心态切换回用户态,返回到应用程序中继续执行
后面的指令。因此概要地讲,单内核模式的内核也可粗略地分为三个层次:调用服务的主程序层、执行系
统调用的服务层和支持系统调用的底层函数。见图 2.2 所示。




                              - 13 -
2.2 Linux 内核系统体系结构




                                                     主程序



                                                     系统服务




                                                     支持函数



                      图2.2 单内核模式的简单结构模型


2.2 Linux 内核系统体系结构
    Linux 内核主要由 5 个模块构成,它们分别是:进程调度模块、内存管理模块、文件系统模块、进程
间通信模块和网络接口模块。
    进程调度模块用来负责控制进程对 CPU 资源的使用。所采取的调度策略是各进程能够公平合理地访
问 CPU,同时保证内核能及时地执行硬件操作。内存管理模块用于确保所有进程能够安全地共享机器主内
存区,同时,内存管理模块还支持虚拟内存管理方式,使得 Linux 支持进程使用比实际内存空间更多大的
内存容量。并可以利用文件系统把暂时不用的内存数据块会被交换到外部存储设备上去,当需要时再交换
回来。文件系统模块用于支持对外部设备的驱动和存储。虚拟文件系统模块通过向所有的外部存储设备提
供一个通用的文件接口,隐藏了各种硬件设备的不同细节。从而提供并支持与其它操作系统兼容的多种文
件系统格式。进程间通信模块子系统用于支持多种进程间的信息交换方式。网络接口模块提供对多种网络
通信标准的访问并支持许多网络硬件。
    这几个模块之间的依赖关系见图 2.3 所示。其中的连线代表它们之间的依赖关系,虚线和虚框部分表
示 Linux 0.11 中还未实现的部分(从 Linux 0.95 版才开始逐步实现虚拟文件系统,而网络接口的支持到 0.96
版才有)   。



                            内存管理



              虚拟文件系统
                            进程调度             进程间通信
               文件系统




                            网络接口


                 图2.3 Linux 内核系统模块结构及相互依赖关系

  由图可以看出,所有的模块都与进程调度模块存在依赖关系。因为它们都需要依靠进程调度程序来挂
起(暂停)或重新运行它们的进程。通常,一个模块会在等待硬件操作期间被挂起,而在操作完成后才可
继续运行。例如,当一个进程试图将一数据块写到软盘上去时,软盘驱动程序就可能在启动软盘旋转期间

                              - 14 -
2.3 Linux 内核进程控制


将该进程置为挂起等待状态,而在软盘进入到正常转速后再使得该进程能继续运行。另外 3 个模块也是由
于类似的原因而与进程调度模块存在依赖关系。
    其它几个依赖关系有些不太明显,但同样也很重要。进程调度子系统需要使用内存管理器来调整一特
定进程所使用的物理内存空间。进程间通信子系统则需要依靠内存管理器来支持共享内存通信机制。这种
通信机制允许两个进程访问内存的同一个区域以进行进程间信息的交换。虚拟文件系统也会使用网络接口
来支持网络文件系统(NFS) ,同样也能使用内存管理子系统来提供内存虚拟盘(ramdisk)设备。而内存
管理子系统也会使用文件系统来支持内存数据块的交换操作。
    若从单内核模式结构模型出发,我们还可以根据 linux 0.11 内核源代码的结构将内核主要模块绘制成
图 2.4 所示的框图结构。



                               用户程序


                                            函   数   库
                                                               用户级

                                                               内核级
                            系统调用接口



                                                    进程间通信
               文件子系统
                                       进程控制
                    高速缓冲               子系统              调度程序


            字符设备    块设备
                                                        内存管理
               设备驱动程序



                           硬   件   控   制
                                                               内核级


                           硬            件                      硬件级


                           图2.4 内核结构框图

  其中内核级中的几个方框,除了硬件控制方框以外,其它粗线方框分别对应内核源代码的目录组织结
构。
  除了这些图中已经给出的依赖关系以外,所有这些模块还会依赖于内核中的通用资源。这些资源包括
内核所有子系统都会调用的内存分配和收回函数、打印警告或出错信息函数以及一些系统调试函数。


2.3 Linux 内核进程控制
  程序是一个可执行的文件,而进程(process)是一个执行中的程序实例。在 Linux 操作系统上同时可
以执行多个进程。对于 linux 0.11 内核来讲,系统最多可有 64 个进程同时存在。  系统除了第一个进程是    “手
工”建立以外,其余的都是进程使用系统调用 fork 创建的新进程,      被创建的进程称为子进程 child process)
                                                      (         ,
创建者,则称为父进程(parent process)
                         。内核程序使用进程标识号(process ID,pid)来标识每个进程。
进程由可执行的指令代码、数据和堆栈区组成。进程中的代码和数据部分分别对应一个执行文件中的代码
段、数据段。每个进程只能执行自己的代码和访问自己的数据及堆栈区。进程之间相互之间的通信需要通
过系统调用了进行。对于只有一个 CPU 的系统,在某一时刻只能有一个进程正在运行。内核通过调度程
序分时调度各个进程运行。


                                   - 15 -
2.4 Linux 内核对内存的使用方法


  Linux 系统中,一个进程可以在内核态(kernel mode)或用户态(user mode)下执行,因此,linux 内
核栈和用户栈是分开的。用户栈用于进程在用户态下临时保存调用函数的参数、局部变量等数据。内核栈
则含有内核程序执行函数调用时的信息。
  内核程序是通过进程表对进程进行管理的,每个进程在进程表中占有一项。在 linux 系统中,进程表
项是一个 task 结构。
  当一个进程在执行时,CPU 的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下
文。当内核需要切换(switch)至另一个进程时,它就需要保存当前进程的所有状态,也即保存当前进程
的上下文,以便在再次执行该进程时,能够恢复到切换时的状态执行下去。在发生中断时,内核就在被中
断进程的上下文中,在内核态下执行中断服务例程。但同时会保留所有需要用到的资源,以便中断服务结
束时能恢复被中断进程的执行。
  一个进程在其生存期内,可处于一组不同的状态下,称为进程状态。见下图所示。


                              用户运行态
                               running     0


                 内核运行态       系统调用或中断           返回
                  running                                中断,中断返回



       可中断睡眠状态                 睡眠                   终止        僵死状态
                         1                 0              3
       interruptible                                          zombie
                                睡眠
                                                    终止

                                               调度
                                唤醒
       不可中断睡眠状态                                               停止状态
                         2                 0              4
       uninterruptible                                        stopped
                               唤醒
                                          就绪态
                                         running


                             图2.5 进程状态及转换关系

  当进程正在被 CPU 执行时,被称为处于执行状态(running)
                                  。当进程正在等待系统中的资源而处于等
待状态时,则称其处于睡眠等待状态。在 linux 系统中,还分为可中断的和不可中断的等待状态。当系统
资源已经可用时,进程就被唤醒而进入准备运行状态,该状态称为就绪态。当进程已停止运行,但其父进
程还没有询问其状态时,则称该进程处于僵死状态。当进程被终止时,称其处于停止状态。
  只有当进程从“内核运行态”转移到“睡眠状态”时,内核才会进行进程切换操作。在内核态下运行
的进程不能被其它进程抢占,而且一个进程不能改变另一个进程的状态。为了避免进程切换时造成内核数
据错误,内核在执行临界区代码时会禁止一切中断。


2.4 Linux 内核对内存的使用方法
  在 linux 0.11 内核中,为了有效地使用系统的物理内存,内存被划分成几个功能区域,见下图 2.6 所示。




                                     - 16 -
2.4 Linux 内核对内存的使用方法




   内核模块               高速缓冲区                虚拟盘               主内存区




   0           640K    1M

                     显存和 BIOS ROM




                               图2.6 物理内存使用的功能分布图

   其中,   linux 内核程序占据在物理内存的开始部分,  接下来是用于供硬盘或软盘等块设备使用的高速缓
冲区部分。当一个进程需要读取块设备中的数据时,系统会首先将数据读到高速缓冲区中;当有数据需要
写到块设备上去时,系统也是先将数据放到高速缓冲区中,然后由块设备驱动程序写到设备上。最后部分
是供所有程序可以随时申请使用的主内存区部分。内核程序在使用主内存区时,也同样要首先向内核的内
存管理模块提出申请,在申请成功后方能使用。对于含有 RAM 虚拟盘的系统,主内存区头部还要划去一
部分,共虚拟盘存放数据。
   由于计算机系统中所含的实际物理内存容量是有限制的。为了能有效地使用这些物理内存,Linux 采
用了 Intel CPU 的内存分页管理机制,使用虚拟线性地址与实际物理内存地址映射的方法让所有同时执行
的程序共同使用有限的内存。内存分页管理的基本原理是将整个主内存区域划分成 4096 字节为一页的内
存页面。程序申请使用内存时,就以内存页为单位进行分配。
   在使用这种内存分页管理方法时,每个执行中的进程(任务)可以使用比实际内存容量大得多的线性
地址空间。对于 Intel 80386 系统,其 CPU 可以提供多达 4G 的线性地址空间。对于 linux 0.11 内核,系统
设置全局描述符表 GDT 中的段描述符项数最大为 256,      其中 2 项空闲、 项系统使用,
                                            2        每个进程使用两项。
因此,此时系统可以最多容纳(256-4)/2 + 1=127 个任务,并且虚拟地址范围是 ((256-4)/2)* 64MB 约等于
8G。 0.11 内核中人工定义最大任务数 NR_TASKS = 64 个,
   但                                  每个进程虚拟地址 (或线性地址)     范围是 64M,
并且各个进程的虚拟地址起始位置是(任务号-1)*64MB。      因此所使用的虚拟地址空间范围是 64MB*64 =4G,
见图 2.7 所示。4G 正好与 CPU 的线性地址空间范围或物理地址空间范围相同,因此在 0.11 内核中比较容
易混淆三种地址概念。



           进程(任务)0                  进程 1                   进程 2




       0                64M                      128M               192M       4G




                              - 执行代码                - 数据            - 参数和堆栈区



                        图2.7 linux 0.11 虚拟地址空间的使用示意图

  linux 0.11 中,在进行地址映射时,我们需要分清 3 种地址之间的变换:a. 进程虚拟地址,是从虚拟
地址 0 开始计,最大 64M;b. CPU 的线性地址空间(0--4G);c. 实际物理内存地址。
  进程的虚拟地址需要首先通过其局部段描述符变换为 CPU 整个线性地址空间中的地址,然后再使用

                                             - 17 -
2.5 Linux 内核源代码的目录结构


页目录表 PDT(一级页表)和页表 PT(二级页表)映射到实际物理地址页上。因此两种变换不能混淆。
  为了使用实际物理内存,每个进程的线性地址通过二级内存页表动态地映射到主内存区域的不同内存
页上。因此每个进程最大可用的虚拟内存空间是 64MB。每个进程的逻辑地址通过加上任务号*64M,即可
转换为线性地址。不过在注释中,我们通常将进程中的地址简单地称为线性地址。
  有关内存分页管理的详细信息,请参见第 10 章开始部分的有关说明,或参见附录。


2.5 Linux 内核源代码的目录结构
  由于 Linux 内核是一种单内核模式的系统,因此,内核中所有的程序几乎都有紧密的联系,它们之间
的依赖和调用关系非常密切。所以在阅读一个源代码文件时往往需要参阅其它相关的文件。因此有必要在
开始阅读内核源代码之前,先熟悉一下源代码文件的目录结构和安排。
  这里我们首先列出 Linux 内核完整的源代码目录,包括其中的子目录。然后逐一介绍各个目录中所包
含程序的主要功能,使得整个内核源代码的安排形式能在我们的头脑中建立起一个大概的框架,以便于下
一章开始的源代码阅读工作。
  当我们使用 tar 命令将 linux-0.11.tar.gz 解开时,内核源代码文件被放到了 linux 目录中。其中的目录结
构为:


        linux
        ├─      boot            系统引导汇编程序
        ├─      fs              文件系统
        ├─      include      头文件(*.h)
        │       ├─ asm       与 CPU 体系结构相关的部分
        │       ├─ linux        Linux 内核专用部分
        │       └─ sys       系统数据结构部分
        ├─      init         内核初始化程序
        ├─      kernel          内核进程调度、信号处理、系统调用等程序
        │       ├─ blk_drv   块设备驱动程序
        │       ├─ chr_drv   字符设备驱动程序
        │       └─ math         数学协处理器仿真处理程序
        ├─      lib          内核库函数
        ├─      mm                  内存管理程序
        └─      tools           生成内核 Image 文件的工具程序

                             图2.8 Linux 内核源代码目录结构

  该内核版本的源代码目录中含有 14 个子目录,总共包括 102 个代码文件。下面逐个对这些子目录中
的内容进行描述。
2.5.1 内核主目录 linux
     linux 目录是源代码的主目录,在该主目录中除了包括所有的 14 个子目录以外,还含有唯一的一个
makefile 文件。该文件是编译辅助工具软件 make 的参数配置文件。make 工具软件的主要用途是通过识别
哪些文件已被修改过,从而自动地决定在一个含有多个源程序文件的程序系统中哪些文件需要被重新编
译。因此,make 工具软件是程序项目的管理软件。
linux 目录下的这个 makefile 文件还嵌套地调用了所有子目录中包含的 makefile 文件,这样,当 linux 目录
(包括子目录)下的任何文件被修改过时,make 都会对其进行重新编译。因此为了编译整个内核所有的
源代码文件,只要在 linux 目录下运行一次 make 软件即可。
2.5.2 引导启动程序目录 boot
  boot 目录中含有 3 个汇编语言文件,是内核源代码文件中最先被编译的程序。这 3 个程序完成的主要
功能是当计算机加电时引导内核启动,将内核代码加载到内存中,并做一些进入 32 位保护运行方式前的
系统初始化工作。   其中 bootsect.s 和 setup.s 程序需要使用 as86 软件来编译,使用的是 as86 的汇编语言格式


                                     - 18 -
2.5 Linux 内核源代码的目录结构


(与微软的类似)       ,而 head.s 需要用 GNU as 来编译,使用的是 AT&T 格式的汇编语言。这两种汇编语言在
下一章的代码注释里以及代码列表后面的说明中会有简单的介绍。
   bootsect.s 程序是磁盘引导块程序,编译后会驻留在磁盘的第一个扇区中(引导扇区,0 磁道(柱面)             ,
0 磁头,第 1 个扇区)    。在 PC 机加电 ROM BIOS 自检后,将被 BIOS 加载到内存 0x7C00 处进行执行。
   setup.s 程序主要用于读取机器的硬件配置参数,并把内核模块 system 移动到适当的内存位置处。
   head.s 程序会被编译连接在 system 模块的最前部分,主要进行硬件设备的探测设置和内存管理页面的
初始设置工作。
2.5.3 文件系统目录 fs
  是文件系统实现程序的目录,共包含 17 个 C 语言程序。这些程序之间的主要引用关系见图 2.9 所示
图中每个方框代表一个文件,从上到下按基本按引用关系放置。其中各文件名均略去了后缀.c,虚框中是
的程序文件不属于文件系统,带箭头的线条表示引用关系,粗线条表示有相互引用关系。


                                                                                    高层函数
                    file_table
                                                       exec     fcntl     ioctl
         数据访问
                                                                 open stat
                         read_write

       char_dev   pipe     block_dev    file_dev                                         低层操作函数
                                                                        namei

                                                              inode
                                                                              truncate

                                                                 bitmap

                                                                        super
                             高速缓冲管理
                                              buffer


                                          ll_rw_block


                          图2.9 fs 目录中各程序中函数之间的引用关系。

     由图可以看出,该目录中的程序可以划分成四个部分:高速缓冲区管理、低层文件操作、文件数据访
问和文件高层函数,在对本目录中文件进行注释说明时,我们也将分成这四个部分来描述。
     对于文件系统,我们可以将它看成是内存高速缓冲区的扩展部分。所有对文件系统中数据的访问,都
需要首先读取到高速缓冲区中。本目录中的程序主要用来管理高速缓冲区中缓冲块的使用分配和块设备上
的文件系统。管理高速缓冲区的程序是 buffer.c,而其它程序则主要都是用于文件系统管理。
     在 file_table.c 文件中,目前仅定义了一个文件句柄(描述符)结构数组。 ioctl.c 文件将引用
kernel/chr_dev/tty.c 中的函数,实现字符设备的 io 控制功能。 exec.c 程序主要包含一个执行程序函数
do_execve(),它是所有 exec()函数簇中的主要函数。fcntl.c 程序用于实现文件 i/o 控制的系统调用函数。
read_write.c 程序用于实现文件读/写和定位三个系统调用函数。stat.c 程序中实现了两个获取文件状态的系
统调用函数。open.c 程序主要包含实现修改文件属性和创建与关闭文件的系统调用函数。
     char_dev.c 主要包含字符设备读写函数 rw_char()。pipe.c 程序中包含管道读写函数和创建管道的系统
调用。    file_dev.c 程序中包含基于 i 节点和描述符结构的文件读写函数。      namei.c 程序主要包括文件系统中目
录名和文件名的操作函数和系统调用函数。block_dev.c 程序包含块数据读和写函数。inode.c 程序中包含针
对文件系统 i 节点操作的函数。          truncate.c 程序用于在删除文件时释放文件所占用的设备数据空间。     bitmap.c

                                                   - 19 -
2.5 Linux 内核源代码的目录结构


程序用于处理文件系统中 i 节点和逻辑数据块的位图。super.c 程序中包含对文件系统超级块的处理函数。
buffer.c 程序主要用于对内存高速缓冲区进行处理。虚框中的 ll_rw_block 是块设备的底层读函数,它并不
在 fs 目录中,而是 kernel/blk_dev/ll_rw_block.c 中的块设备读写驱动函数。放在这里只是让我们清楚的看
到,文件系统对于块设备中数据的读写,都需要通过高速缓冲区与块设备的驱动程序(ll_rw_block())来
操作来进行,文件系统程序集本身并不直接与块设备的驱动程序打交道。
     在对程序进行注释过程中,我们将另外给出这些文件中各个主要函数之间的调用层次关系。
2.5.4 头文件主目录 include
     头文件目录中总共有 32 个.h 头文件。其中主目录下有 13 个,asm 子目录中有 4 个,linux 子目录中有
10 个,sys 子目录中有 5 个。这些头文件各自的功能见如下简述,具体的作用和所包含的信息请参见对头
文件的注释一章。
<a.out.h>        a.out 头文件,定义了 a.out 执行文件格式和一些宏。
<const.h>        常数符号头文件,目前仅定义了 i 节点中 i_mode 字段的各标志位。
<ctype.h>        字符类型头文件。定义了一些有关字符类型判断和转换的宏。
<errno.h>        错误号头文件。包含系统中各种出错号。(Linus 从 minix 中引进的)。
<fcntl.h>        文件控制头文件。用于文件及其描述符的操作控制常数符号的定义。
<signal.h>       信号头文件。定义信号符号常量,信号结构以及信号操作函数原型。
<stdarg.h>       标准参数头文件。以宏的形式定义变量参数列表。主要说明了-个类型(va_list)和三个
宏(va_start, va_arg 和 va_end)
                           ,用于 vsprintf、vprintf、vfprintf 函数。
<stddef.h>       标准定义头文件。定义了 NULL, offsetof(TYPE, MEMBER)。
<string.h>      字符串头文件。主要定义了一些有关字符串操作的嵌入函数。
<termios.h>      终端输入输出函数头文件。主要定义控制异步通信口的终端接口。
<time.h>          时间类型头文件。其中最主要定义了 tm 结构和一些有关时间的函数原形。
<unistd.h>       Linux 标准头文件。定义了各种符号常数和类型,并申明了各种函数。如定义了
__LIBRARY__,则还包括系统调用号和内嵌汇编_syscall0()等。
<utime.h>         用户时间头文件。定义了访问和修改时间结构以及 utime()原型。
2.5.4.1 体系结构相关头文件子目录 include/asm
    这些头文件主要定义了一些与 CPU 体系结构密切相关的数据结构、宏函数和变量。共 4 个文件。
<asm/io.h>      io 头文件。以宏的嵌入汇编程序形式定义对 io 端口操作的函数。
<asm/memory.h>   内存拷贝头文件。含有 memcpy()嵌入式汇编宏函数。
<asm/segment.h> 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
<asm/system.h> 系统头文件。定义了设置或修改描述符/中断门等的嵌入式汇编宏。
2.5.4.2 Linux 内核专用头文件子目录 include/linux
<linux/config.h>   内核配置头文件。定义键盘语言和硬盘类型(HD_TYPE)可选项。
<linux/fdreg.h>    软驱头文件。含有软盘控制器参数的一些定义。
<linux/fs.h>        文件系统头文件。定义文件表结构(file,buffer_head,m_inode 等)
                                                              。
<linux/hdreg.h>     硬盘参数头文件。定义访问硬盘寄存器端口,状态码,分区表等信息。
<linux/head.h>      head 头文件,定义了段描述符的简单结构,和几个选择符常量。
<linux/kernel.h>   内核头文件。含有一些内核常用函数的原形定义。
<linux/mm.h>          内存管理头文件。含有页面大小定义和一些页面释放函数原型。
<linux/sched.h>     调度程序头文件,定义了任务结构 task_struct、初始任务 0 的数据,
                       还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
<linux/sys.h>       系统调用头文件。含有 72 个系统调用 C 函数处理程序,以'sys_'开头。
<linux/tty.h>      tty 头文件,定义了有关 tty_io,串行通信方面的参数、常数。
2.5.4.3 系统专用数据结构子目录 include/sys
<sys/stat.h>       文件状态头文件。含有文件或文件系统状态结构 stat{}和常量。
<sys/times.h>      定义了进程中运行时间结构 tms 以及 times()函数原型。
<sys/types.h>      类型头文件。定义了基本的系统数据类型。
<sys/utsname.h>    系统名称结构头文件。
<sys/wait.h>       等待调用头文件。定义系统调用 wait()核 waitpid()及相关常数符号。



                                       - 20 -
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code
Linux011 Heavily Documented Source Code

More Related Content

Similar to Linux011 Heavily Documented Source Code

Adobe premiere cs4
Adobe premiere cs4Adobe premiere cs4
Adobe premiere cs4cl17
 
After effects cs4_help
After effects cs4_helpAfter effects cs4_help
After effects cs4_helpestivenpg13
 
云计算培训材料
云计算培训材料云计算培训材料
云计算培训材料koolea
 
Ajax api(flash地图展现)用户手册—企业版—v2.3.4
Ajax api(flash地图展现)用户手册—企业版—v2.3.4Ajax api(flash地图展现)用户手册—企业版—v2.3.4
Ajax api(flash地图展现)用户手册—企业版—v2.3.4aethersg
 
F5-Big_IP_LTM-Zh
F5-Big_IP_LTM-ZhF5-Big_IP_LTM-Zh
F5-Big_IP_LTM-Zhceed100043
 
Protocollo informatico manuale_usp_mantova
Protocollo informatico manuale_usp_mantovaProtocollo informatico manuale_usp_mantova
Protocollo informatico manuale_usp_mantovajamboo
 
Oracle 经验操作
Oracle 经验操作Oracle 经验操作
Oracle 经验操作Bob Huang
 
PHP-Debug-Manual-public
PHP-Debug-Manual-publicPHP-Debug-Manual-public
PHP-Debug-Manual-publicwensheng wei
 
12 версия m03 - введение в aveva pdms
12 версия   m03 - введение в aveva pdms12 версия   m03 - введение в aveva pdms
12 версия m03 - введение в aveva pdmsmarvinantonio
 
Memoria ap
Memoria apMemoria ap
Memoria apazorojo1
 
Protocollo informatico manuale_usr_lombardia
Protocollo informatico manuale_usr_lombardiaProtocollo informatico manuale_usr_lombardia
Protocollo informatico manuale_usr_lombardiajamboo
 
Bizikidetasun plana eusk
Bizikidetasun plana euskBizikidetasun plana eusk
Bizikidetasun plana euskIrekia - EJGV
 
Pla d'acció per al desenvolupament del contract manufacturing en el sector de...
Pla d'acció per al desenvolupament del contract manufacturing en el sector de...Pla d'acció per al desenvolupament del contract manufacturing en el sector de...
Pla d'acció per al desenvolupament del contract manufacturing en el sector de...Biocat, BioRegion of Catalonia
 
codi Html osanroma
codi Html osanromacodi Html osanroma
codi Html osanromaPedro Vilas
 

Similar to Linux011 Heavily Documented Source Code (20)

Adobe premiere cs4
Adobe premiere cs4Adobe premiere cs4
Adobe premiere cs4
 
After effects cs4_help
After effects cs4_helpAfter effects cs4_help
After effects cs4_help
 
云计算培训材料
云计算培训材料云计算培训材料
云计算培训材料
 
Curso
CursoCurso
Curso
 
Ajax api(flash地图展现)用户手册—企业版—v2.3.4
Ajax api(flash地图展现)用户手册—企业版—v2.3.4Ajax api(flash地图展现)用户手册—企业版—v2.3.4
Ajax api(flash地图展现)用户手册—企业版—v2.3.4
 
F5-Big_IP_LTM-Zh
F5-Big_IP_LTM-ZhF5-Big_IP_LTM-Zh
F5-Big_IP_LTM-Zh
 
Protocollo informatico manuale_usp_mantova
Protocollo informatico manuale_usp_mantovaProtocollo informatico manuale_usp_mantova
Protocollo informatico manuale_usp_mantova
 
Oracle 经验操作
Oracle 经验操作Oracle 经验操作
Oracle 经验操作
 
PHP-Debug-Manual-public
PHP-Debug-Manual-publicPHP-Debug-Manual-public
PHP-Debug-Manual-public
 
12 версия m03 - введение в aveva pdms
12 версия   m03 - введение в aveva pdms12 версия   m03 - введение в aveva pdms
12 версия m03 - введение в aveva pdms
 
Memoria ap
Memoria apMemoria ap
Memoria ap
 
Protocollo informatico manuale_usr_lombardia
Protocollo informatico manuale_usr_lombardiaProtocollo informatico manuale_usr_lombardia
Protocollo informatico manuale_usr_lombardia
 
NTE E.060 CONCRETO ARMADO 2009 - PERU
NTE E.060 CONCRETO ARMADO 2009 - PERUNTE E.060 CONCRETO ARMADO 2009 - PERU
NTE E.060 CONCRETO ARMADO 2009 - PERU
 
Bizikidetasun plana eusk
Bizikidetasun plana euskBizikidetasun plana eusk
Bizikidetasun plana eusk
 
Pla d'acció per al desenvolupament del contract manufacturing en el sector de...
Pla d'acció per al desenvolupament del contract manufacturing en el sector de...Pla d'acció per al desenvolupament del contract manufacturing en el sector de...
Pla d'acció per al desenvolupament del contract manufacturing en el sector de...
 
Tiling
Tiling Tiling
Tiling
 
5175
51755175
5175
 
Cloud computing
Cloud computingCloud computing
Cloud computing
 
Otamotz aldizkariaren irakurleen azterketa
Otamotz aldizkariaren irakurleen azterketaOtamotz aldizkariaren irakurleen azterketa
Otamotz aldizkariaren irakurleen azterketa
 
codi Html osanroma
codi Html osanromacodi Html osanroma
codi Html osanroma
 

Linux011 Heavily Documented Source Code

  • 1. 0 10 5 . (. ) 1 9
  • 2. Linux 内核 0.11 完全注释 A Heavily Commented Linux Kernel Source Code Linux Version 0.11 修正版 1.2.2 (Revision 1.2.2) 赵炯 Zhao Jiong gohigh@sh163.net www.plinux.org www.oldlinux.org 2003-11-26
  • 3. 内容简介 本书对 Linux 早期操作系统内核(v0.11)全部代码文件进行了详细全面的注释和说明,旨在使读者能够在尽量短的时间 内对 Linux 的工作机理获得全面而深刻的理解,为进一步学习和研究 Linux 系统打下坚实的基础。虽然所选择的版本较低, 但该内核已能够正常编译运行,其中已经包括了 LINUX 工作原理的精髓,通过阅读其源代码能快速地完全理解内核的运作 机制。书中首先以 Linux 源代码版本的变迁历史为主线,详细介绍了 Linux 系统的发展历史,着重说明了各个内核版本之间 的重要区别和改进方面,给出了选择 0.11(0.95)版作为研究的对象的原因。另外介绍了内核源代码的组织结构及相互关系, 同时还说明了编译和运行该版本内核的方法。 然后本书依据内核源代码的组织结构对所有内核程序和文件进行了注释和详细 说明。每章的安排基本上分为具体研究对象的概述、每个文件的功能介绍、代码内注释、代码中难点及相关资料介绍、与当 前版本的主要区别等部分。最后一章内容总结性地介绍了继续研究 Linux 系统的方法和着手点。 版权说明 作者保留本电子书籍的修改和正式出版的所有权利.读者可以自由传播本书全部和部分章节的内容,但需要注明出处.由 于目前本书尚为草稿阶段,因此存在许多错误和不足之处,希望读者能踊跃给予批评指正或建议.可以通过电子邮件给我发信 息:gohigh@sh163.net, 或直接来信至:上海同济大学 机械电子工程研究所(上海四平路 1239 号,邮编:200092). © 2002,2003 by Zhao Jiong © 2002,2003 赵炯 版权所有.
  • 4. “RTFSC – Read The F**king Source Code ☺!” –Linus Benedict Torvalds
  • 5. 目录 目录 5.7 SCHED.C 程序 .................................................... 104 序言 ................................................................................ 1 5.8 SIGNAL.C 程序................................................... 116 本书的主要目标 ........................................................ 1 5.9 EXIT.C 程序 ....................................................... 122 现有书籍不足之处 .................................................... 1 5.10 FORK.C 程序.................................................... 127 阅读早期内核其它的好处? .................................... 2 5.11 SYS.C 程序....................................................... 132 阅读完整源代码的重要性和必要性 ........................ 2 5.12 VSPRINTF.C 程序.............................................. 138 如何选择要阅读的内核代码版本 ............................ 2 5.13 PRINTK.C 程序 ................................................. 146 阅读本书需具备的基础知识 .................................... 3 5.14 PANIC.C 程序 ................................................... 147 使用早期版本是否过时? ........................................ 3 5.15 本章小结........................................................ 148 EXT2 文件系统与 MINIX 文件系统? ...................... 4 第 6 章 块设备驱动程序(BLOCK DRIVER)......... 149 第 1 章 概述 .................................................................. 5 6.1 概述 ................................................................. 149 1.1 LINUX 的诞生和发展 ........................................... 5 6.2 总体功能.......................................................... 149 1.2 内容综述 ............................................................. 9 6.3 MAKEFILE 文件................................................. 149 1.3 本章小结 ........................................................... 12 6.4 BLK.H 文件........................................................ 151 6.5 HD.C 程序.......................................................... 154 第 2 章 LINUX 内核体系结构 .................................. 13 6.6 LL_RW_BLK.C 程序 ........................................... 167 2.1 LINUX 内核模式 ................................................. 13 6.7 RAMDISK.C 程序................................................ 171 2.2 LINUX 内核系统体系结构 ................................. 14 6.8 FLOPPY.C 程序 ................................................... 175 2.3 LINUX 内核进程控制 ......................................... 15 第 7 章 字符设备驱动程序(CHAR DRIVER) ....... 189 2.4 LINUX 内核对内存的使用方法 ......................... 16 2.5 LINUX 内核源代码的目录结构 ......................... 18 7.1 概述 ................................................................. 189 2.6 内核系统与用户程序的关系............................ 23 7.2 总体功能描述.................................................. 189 2.7 LINUX 内核的编译实验环境 ............................. 23 7.3 MAKEFILE 文件................................................. 192 2.8 LINUX/MAKEFILE 文件........................................ 25 7.4 KEYBOARD.S 程序 ............................................. 194 2.9 本章小结 ........................................................... 33 7.5 CONSOLE.C 程序................................................ 211 7.6 SERIAL.C 程序 ................................................... 234 第 3 章 引导启动程序(BOOT)............................. 35 7.7 RS_IO.S 程序 ..................................................... 237 3.1 概述 ................................................................... 35 7.8 TTY_IO.C 程序 ................................................... 240 3.2 总体功能 ........................................................... 35 7.9 TTY_IOCTL.C 程序 ............................................. 250 3.3 BOOTSECT.S 程序 ................................................ 36 第 8 章 数学协处理器(MATH)................................ 257 3.4 SETUP.S 程序 ....................................................... 43 3.5 HEAD.S 程序........................................................ 55 8.1 概述 ................................................................. 257 3.6 本章小结 ........................................................... 63 8.2 MAKEFILE 文件................................................. 257 8.3 MATH-EMULATION.C 程序.................................. 258 第 4 章 初始化程序(INIT) ......................................... 65 第 9 章 文件系统(FS) ............................................... 261 4.1 概述 ................................................................... 65 4.2 MAIN.C 程序........................................................ 65 9.1 概述 ................................................................. 261 4.3 本章小结 ........................................................... 73 9.2 总体功能描述.................................................. 261 9.3 MAKEFILE 文件................................................. 267 第 5 章 内核代码(KERNEL)..................................... 75 9.4 BUFFER.C 程序 .................................................. 269 5.1 概述 ................................................................... 75 9.5 BITMAP.C 程序................................................... 283 5.2 MAKEFILE 文件................................................... 78 9.6 INODE.C 程序 .................................................... 288 5.3 ASM.S 程序.......................................................... 80 9.7 SUPER.C 程序 .................................................... 298 5.4 TRAPS.C 程序 ...................................................... 87 9.8 NAMEI.C 程序 .................................................... 306 5.5 SYSTEM_CALL.S 程序.......................................... 94 9.9 FILE_TABLE.C 程序............................................ 328 5.6 MKTIME.C 程序 ................................................. 102 9.10 BLOCK_DEV.C 程序.......................................... 328 - I -
  • 6. 目录 9.11 FILE_DEV.C 程序.............................................. 331 11.25 HDREG.H 文件................................................ 452 9.12 PIPE.C 程序...................................................... 333 11.26 HEAD.H 文件.................................................. 454 9.13 CHAR_DEV.C 程序 ........................................... 337 11.27 KERNEL.H 文件.............................................. 455 9.14 READ_WRITE.C 程序........................................ 340 11.28 MM.H 文件..................................................... 456 9.15 TRUNCATE.C 程序............................................ 343 11.29 SCHED.H 文件 ................................................ 456 9.16 OPEN.C 程序.................................................... 346 11.30 SYS.H 文件..................................................... 464 9.17 EXEC.C 程序.................................................... 352 11.31 TTY.H 文件..................................................... 466 9.18 STAT.C 程序 ..................................................... 366 11.32 INCLUDE/SYS/目录中的文件......................... 469 9.19 FCNTL.C 程序 .................................................. 367 11.33 STAT.H 文件 ................................................... 469 9.20 IOCTL.C 程序................................................... 369 11.34 TIMES.H 文件................................................. 470 11.35 TYPES.H 文件................................................. 471 第 10 章 内存管理(MM) .......................................... 371 11.36 UTSNAME.H 文件 ........................................... 472 10.1 概述 ............................................................... 371 11.37 WAIT.H 文件................................................... 472 10.2 总体功能描述 ............................................... 371 第 12 章 库文件(LIB)............................................... 475 10.3 MAKEFILE 文件............................................... 375 10.4 MEMORY.C 程序............................................... 377 12.1 概述 ............................................................... 475 10.5 PAGE.S 程序..................................................... 390 12.2 MAKEFILE 文件............................................... 475 12.3 _EXIT.C 程序 ................................................... 477 第 11 章 包含文件(INCLUDE) ............................... 393 12.4 CLOSE.C 程序 .................................................. 478 11.1 概述 ............................................................... 393 12.5 CTYPE.C 程序 .................................................. 478 11.2 INCLUDE/目录下的文件 ................................. 393 12.6 DUP.C 程序 ...................................................... 479 11.3 A.OUT.H 文件................................................... 393 12.7 ERRNO.C 程序.................................................. 480 11.4 CONST.H 文件 .................................................. 402 12.8 EXECVE.C 程序................................................ 480 11.5 CTYPE.H 文件 .................................................. 402 12.9 MALLOC.C 程序 ............................................... 481 11.6 ERRNO.H 文件 ................................................. 403 12.10 OPEN.C 程序 .................................................. 489 11.7 FCNTL.H 文件 .................................................. 405 12.11 SETSID.C 程序................................................ 490 11.8 SIGNAL.H 文件................................................. 407 12.12 STRING.C 程序 ............................................... 491 11.9 STDARG.H 文件 ................................................ 409 12.13 WAIT.C 程序................................................... 491 11.10 STDDEF.H 文件 .............................................. 410 12.14 WRITE.C 程序 ................................................ 492 11.11 STRING.H 文件 ............................................... 410 第 13 章 建造工具(TOOLS) .................................... 493 11.12 TERMIOS.H 文件 ............................................ 420 11.13 TIME.H 文件................................................... 426 13.1 概述 ............................................................... 493 11.14 UNISTD.H 文件............................................... 428 13.2 BUILD.C 程序................................................... 493 11.15 UTIME.H 文件 ................................................ 433 参考文献 .................................................................... 501 11.16 INCLUDE/ASM/目录下的文件 ....................... 435 11.17 IO.H 文件....................................................... 435 附录 ............................................................................ 502 11.18 MEMORY.H 文件............................................. 436 附录 1 内核主要常数............................................ 502 11.19 SEGMENT.H 文件 ........................................... 436 附录 2 内核数据结构............................................ 505 11.20 SYSTEM.H 文件.............................................. 439 附录 3 80X86 保护运行模式 ................................. 512 11.21 INCLUDE/LINUX/目录下的文件 .................... 442 11.22 CONFIG.H 文件............................................... 442 索引 ............................................................................ 520 11.23 FDREG.H 头文件 ............................................ 444 11.24 FS.H 文件....................................................... 447 - II -
  • 7. 序言 序言 本书是一本有关 Linux 操作系统内核基本工作原理的入门读物。 本书的主要目标 本书的主要目标是使用尽量少的篇幅或在有限的篇幅内,对完整的 Linux 内核源代码进行解剖,以期 对操作系统的基本功能和实际实现方式获得全方位的理解。做到对 linux 内核有一个完整而深刻的理解, 对 linux 操作系统的基本工作原理真正理解和入门。 本书读者群的定位是一些知晓 Linux 系统的一般使用方法或具有一定的编程基础,但比较缺乏阅读目 前最新内核源代码的基础知识,又急切希望能够进一步理解 UNIX 类操作系统内核工作原理和实际代码实 现的爱好者。这部分读者的水平应该界于初级与中级水平之间。目前,这部分读者人数在 Linux 爱好者中 所占的比例是很高的,而面向这部分读者以比较易懂和有效的手段讲解内核的书籍资料不多。 现有书籍不足之处 目前已有的描述 Linux 内核的书籍,均尽量选用最新 Linux 内核版本(例如 Redhat 7.0 使用的 2.2.16 稳定版等)进行描述,但由于目前 Linux 内核整个源代码的大小已经非常得大(例如 2.2.20 版具有 268 万 行代码!,因此这些书籍仅能对 Linux 内核源代码进行选择性地或原理性地说明,许多系统实现细节被忽 ) 略。因此并不能给予读者对实际 Linux 内核有清晰而完整的理解。 陆丽娜等同志翻译的 Scott Maxwell 著的一书《Linux 内核源代码分析》基本上是面对 Linux 中级水平 的读者,需要较为全面的基础知识才能完全理解。而且可能是由于篇幅所限,该书并没有对所有 Linux 内 核代码进行注释,略去了很多内核实现细节,例如其中内核中使用的各个头文件(*.h)、生成内核代码映象 文件的工具程序、各个 make 文件的作用和实现等均没有涉及。因此对于处于初中级水平之间的读者来说 阅读该书有些困难。 去年初浙江大学出版的《Linux 内核源代码情景分析》一书,也基本有这些不足之处。甚至对于一些 具有较高 Linux 系统应用水平的计算机本科高年级学生,由于该书篇幅问题以及仅仅选择性地讲解内核源 代码,也不能真正吃透内核的实际实现方式,因而往往刚开始阅读就放弃了。这在本人教学的学生中基本 都会出现这个问题。该书刚面市时,本人曾极力劝说学生购之阅读,并在二个月后调查阅读学习情况,基 本都存在看不下去或不能理解等问题,大多数人都放弃了。 John Lions 著的《莱昂氏 UNIX 源代码分析》一书虽然是一本学习 UNIX 类操作系统内核源代码很好 的书籍,但是由于其采用的是 UNIX V6 版,其中系统调用等部分代码是用早已过时的 PDP-11 系列机的汇 编语言编制的,因此在阅读与硬件部分相关的源代码时就会遇到较大的困难。 A.S.Tanenbaum 的书《操作系统:设计与实现》是一本有关操作系统内核实现很好的入门书籍,但该 书所叙述的 minix 系统是一种基于消息传递的内核实现机制,与 Linux 内核的实现有所区别。因此在学习 该书之后,并不能很顺利地即刻着手进一步学习较新的 Linux 内核源代码实现。 在使用这些书籍进行学习时会有一种“盲人摸象”的感觉,不能真正理解 Linux 内核系统具体实现的 整体概念,尤其是对那些 Linux 系统初学者或刚学会如何使用 Linux 系统的人在使用那些书学习内核原理 时,内核的整体运作结构并不能清晰地在脑海中形成。 这在本人多年的 Linux 内核学习过程中也深有体会, 也是写作本书的动机之一。 为了填补这个空缺,本书的主要目标是使用尽量少的篇幅或在有限的篇幅内,对完整的 Linux 内核源 代码进行全面解剖,以期对操作系统的基本功能和实际实现方式获得全方位的理解。做到对 linux 内核有 一个完整而深刻的理解,对 linux 操作系统的基本工作原理真正理解和入门。 - 1 -
  • 8. 序言 阅读早期内核其它的好处? 目前,已经出现不少基于 Linux 早期内核而开发的专门用于嵌入式系统的内核版本,如 DJJ 的 x86 操 作系统、Uclinux 等(在 www.linux.org 上有专门目录),世界上也有许多人认识到通过早期 Linux 内核源代 码学习的好处,目前国内也已经有人正在组织人力注释出版类似本文的书籍。因此,通过阅读 Linux 早期 内核版本的源代码,的确是学习 Linux 系统的一种行之有效的途径,并且对研究和应用 Linux 嵌入式系统 也有很大的帮助。 在对早期内核源代码的注释过程中,作者已经发现,早期内核源代码几乎就是目前所使用的较新内核 的一个精简版本。其中已经包括了目前新版本中几乎所有的基本功能原理的内容。正如《系统软件:系统 编程导论》一书的作者 Leland L. Bach 在介绍系统程序以及操作系统设计时,引入了一种极其简化的简单 指令计算机(SIC)系统来说明所有系统程序的设计和实现原理,从而既避免了实际计算机系统的复杂性,又 能透彻地说明问题。这里选择 Linux 的早期内核版本作为学习对象,其指导思想与 Leland 的一致。这对 Linux 内核学习的入门者来说,无非是一个最理想的选择之一。能够在尽可能短的时间内深入理解 Linux 内核的基本工作原理。 当然,使用早期内核作为学习的对象也有不足之处,所选用的 Linux 早期内核版本不包含对虚拟文件 系统 VFS 的支持、对网络系统的支持、 仅支持 a.out 执行文件和对其它一些现有内核中复杂子系统的说明。 但由于本书是作为 Linux 内核工作机理实现的入门教材,因此这也正是选择早期内核版本的优点之一。通 过学习本书,可以为进一步学习这些高级内容打下扎实的基础。 阅读完整源代码的重要性和必要性 正如 Linux 系统的创始人在一篇新闻组投稿上所说的,要理解一个软件系统的真正运行机制,一定要 阅读其源代码(RTFSC – Read The Fucking Source Code)。系统本身是一个完整的整体,具有很多看似不重 要的细节存在,但是若忽略这些细节,就会对整个系统的理解带来困难,并且不能真正了解一个实际系统 的实现方法和手段。 虽然通过阅读一些操作系统原理经典书籍(例如 M.J.Bach 的《UNIX 操作系统设计》 )能够对 UNIX 类操作系统的工作原理有一些理论上的指导作用,但实际上对操作系统的真正组成和内部关系实现的理解 仍不是很清晰。正如 AST 所说的, “许多操作系统教材都是重理论而轻实践”“多数书籍和课程为调度算 , 法耗费大量的时间和篇幅而完全忽略 I/O,其实,前者通常不足一页代码,而后者往往要占到整个系统三 分之一的代码总量。 ”内核中大量的重要细节均未提到。因此并不能让读者理解一个真正的操作系统实现 的奥妙所在。只有在详细阅读过完整的内核源代码之后,才会对系统有一种豁然开朗的感觉,对整个系统 的运作过程有深刻的理解。以后再选择最新的或较新内核源代码进行学习时,也不会碰到大问题,基本上 都能顺利地理解新代码的内容。 如何选择要阅读的内核代码版本 那么,如何选择既能达到上述要求,又不被太多的内容而搞乱头脑,选择一个适合的 Linux 内核 版本进行学习,提高学习的效率呢?作者通过对大量内核版本进行比较和选择后, 最终选择了与目前 Linux 内核基本功能较为相近,又非常短小的 0.11 版内核作为入门学习的最佳版本。下图是对一些主要 Linux 内 核版本行数的统计。 - 2 -
  • 9. 序言 10000000 1000000 100000 10000 1000 V0.01 V0.11 V0.12 V0.95 V0.97 V1.0 V0.96a V1.1.52 V1.2.13 V1.3.95 V2.0.38 V2.2.20 V2.4.17 目前的 Linux 内核源代码量都在几百万行的数量上,极其庞大,对这些版本进行完全注释和说明是不 可能的,而 0.11 版内核不超过 2 万行代码量,因此完全可以在一本书中解释和注释清楚。麻雀虽小,五脏 俱全。 另外,使用该版本可以避免使用现有较新内核版本中已经变得越来越复杂得各子系统部分的研究(如 虚拟文件系统 VFS、ext2 或 ext3 文件系统、网络子系统、新的复杂的内存管理机制等) 。 阅读本书需具备的基础知识 在阅读本书时,作者希望读者具有以下一些基础知识或有相关的参考书籍在手边。其一是有关 80x86 处理器结构和编程的知识或资料。例如可以从网上下载的 80x86 编程手册(INTEL 80386 Programmer's Reference Manual) ;其二是有关 80x86 硬件体系结构和接口编程的知识或资料。有关这方面的资料很多; 其三还应具备初级使用 Linux 系统的简单技能。 另外,由于 Linux 系统内核实现,最早是根据 M.J.Bach 的《UNIX 操作系统设计》一书的基本原理开 发的,源代码中许多变量或函数的名称都来自该书,因此在阅读本书时,若能适当参考该书,更易于对源 代码的理解。 Linus 在最初开发 Linux 操作系统时,参照了 Minix 操作系统。例如,最初的 Linux 内核版本完全照搬 了 Minix 1.0 文件系统。因此,在阅读本书时,A.S.Tanenbaum 的书《操作系统:设计与实现》也具有较大 的参考价值。但 Tanenbaum 的书描述的是一种基于消息传递在内核各模块之间进行通信(信息交换) ,这 与 Linux 内核的工作机制不一样。因此可以仅参考其中有关一般操作系统工作原理章节和文件系统实现的 内容。 使用早期版本是否过时? 表面看来本书对 Linux 早期内核版本注释的内容犹如 Linux 操作系统刚公布时 Tanenbaum 就认为其已 - 3 -
  • 10. 序言 经过时的(Linux is obsolete)想法一样,但通过学习本书内容,你就会发现,利用本书学习 Linux 内核, 由于内核源代码量短小而精干,因此会有极高的学习效率,能够做到事半功倍,快速入门。并且对继续进 一步选择新内核部分源代码的学习打下坚实的基础。在学习完本书之后,你将对系统的运作原理有一个非 常完整而实际的概念,这种完整概念能使人很容易地进一步选择和学习新内核源代码中的任何部分,而不 需要再去啃读代码量巨大的新内核中完整的源代码。 Ext2 文件系统与 Minix 文件系统? 目前 Linux 系统上所使用的 Ext2(或最新的 Ext3)文件系统,是在内核 1.x 之后开发的文件系统,其 功能详尽并且其性能也非常完整和稳固, 是目前 Linux 操作系统上默认的标准文件系统。 但是, 作为对 Linux 操作系统完整工作原理入门学习所使用的部分,原则上是越精简越好。为了达到对一个操作系统有完整的 理解,并且能不被其中各子系统中的复杂性和过多的细节喧宾夺主,在选择学习剖析用的内核版本时,只 要系统的部分代码内容能说明实际工作原理,就越简单越好。 Linux 内核 0.11 版上当时仅包含最为简单的 minix 1.0 文件系统,对于理解一个操作系统中文件系统的 实际组成和工作原理已经足够。这也是选择 Linux 早期内核版本进行学习的主要原因之一。 在完整阅读完本书之后,相信您定会发出这样的感叹: “对于 Linux 内核系统,我现在终于入门了!。 ” 此时,您应该有十分的把握去进一步学习最新 Linux 内核中各部分的工作原理和过程了。 同济大学 赵炯 博士 2002.12 - 4 -
  • 11. 1.1 Linux 的诞生和发展 第1章 概述 本章首先回顾了 Linux 操作系统的诞生、开发和成长过程,由此理解本书为什么会选择 Linux 系统早 期版本作为学习对象的一些原因。然后具体说明了选择早期 Linux 内核版本进行学习的优点和不足之处以 及如何开始进一步的学习。 1.1 Linux 的诞生和发展 Linux 操作系统是 UNIX 操作系统的一种克隆系统。它诞生于 1991 年的 10 月 5 日(这是第一次正式 向外公布的时间) 。以后借助于 Internet 网络,并经过全世界各地计算机爱好者的共同努力下,现已成为今 天世界上使用最多的一种 UNIX 类操作系统,并且使用人数还在迅猛增长。 Linux 操作系统的诞生、发展和成长过程始终依赖着以下五个重要支柱:UNIX 操作系统、MINIX 操作系 统、GNU 计划、POSIX 标准和 Internet 网络。 下面主要根据这五个基本线索来追寻一下 Linux 的开发历程,它的酝酿过程,最初的发展经历。首先 分别介绍其中的四个基本要素(UNIX、MINIX、GNU 和 POSIX,Internet 的重要性显而易见,所以不用对 其罗嗦),然后根据 Linux 的创始人 Linus Toravlds 从对计算机感兴趣而自学计算机知识,到心里开始酝酿 编制一个自己的操作系统,到最初 Linux 内核 0.01 版公布,以及从此如何艰难地一步一个脚印地在全世界 hacker 的帮助下最后推出比较完善的 1.0 版本这段时间的发展经过,也即对 Linux 的早期发展历史进行详 细介绍。 当然, 目前 Linux 内核版本已经开发到了 2.5.52 版。而大多数 Linux 系统中所用到的内核是稳定的 2.4.20 版内核。 (其中第 2 个数字奇数表示是正在开发的版本, 不能保证系统的稳定性) 对于 Linux 的一般发展史, 许多文章和书籍都有介绍,这里就不重复。 1.1.1 UNIX 操作系统的诞生 Linux 操作系统是 UNIX 操作系统的一个克隆版本。 UNIX 操作系统是美国贝尔实验室的 Ken.Thompson 和 Dennis Ritchie 于 1969 年夏在 DEC PDP-7 小型计算机上开发的一个分时操作系统。 当时 Ken Thompson 为了能在闲置不用的 PDP-7 计算机上运行他非常喜欢的星际旅行(Space travel)游戏, 在 1969 年夏天乘他夫人回家乡加利福尼亚渡假期间,在一个月内开发出了 unix 操作系统的原型。当时使 用的是 BCPL 语言(基本组合编程语言) ,后经 Dennis Ritchie 于 1972 年用移植性很强的 C 语言进行了改 写,使得 UNIX 系统在大专院校得到了推广。 1.1.2 MINIX 操作系统 MINIX 系统是由 Andrew S. Tanenbaum(AST)开发的。AST 是在荷兰 Amsterdam 的 Vrije 大学数学与 计算机科学系统工作, ACM 和 IEEE 的资深会员(全世界也只有很少人是两会的资深会员)。 是 共发表了 100 多篇文章,5 本计算机书籍。 AST 虽出生在美国纽约,但是是荷兰侨民(1914 年他的祖辈来到美国)。他在纽约上的中学、M.I.T 上 的大学、加洲大学 Berkeley 分校念的博士学位。由于读博士后的缘故,他来到了家乡荷兰。从此就与家乡 一直有来往。后来就在 Vrije 大学开始教书、带研究生了。荷兰首都 Amsterdam 是个常年阴雨绵绵的城市, 而对于 AST 来说,这最好不过了,因为这样他就可以待在家里摆弄他的计算机了。 MINIX 是他 1987 年编制的,主要用于学生学习操作系统原理。到 91 年时版本是 1.5。目前主要有两 个版本在使用: 1.5 版和 2.0 版,当时该操作系统在大学使用是免费的,但其它用途不是,当然目前都已 经是免费的,可以从许多 FTP 上下载。 对于 Linux 系统,他表示对其开发者 Linus 的称赞。但他认为 Linux 的发展有很大原因是因为他为了 保持 minix 的小型化,能让学生在一个学期内就能学完,而没有接纳全世界许多人对 Minix 的扩展要求。 因此这激发了 Linus 编写 Linux。Linus 正好抓住了这个好时机。 作为一个操作系统,MINIX 并不是优秀者,但它同时提供了用 C 语言和汇编语言写的系统源代码。这是 第一次使得有抱负的程序员或 hacker 能够阅读操作系统的源代码, 在当时这种源代码是软件商一直小心地 守护着的。 - 5 -
  • 12. 1.1 Linux 的诞生和发展 1.1.3 GNU 计划 GNU 计划和自由软件基金会(the Free Software Foundation – FSF)是由 Richard M. Stallman 于 1984 年一 手创办的。旨在开发一个类似 Unix、并且是自由软件的完整操作系统:GNU 系统。 GNU 是"GNU's Not ( Unix"的递归缩写, 它的发音为"guh-NEW"。 各种使用 linux 作为核心的 GNU 操作系统正在被广泛的使用。 ) 虽然这些系统通常被称作"Linux",但是严格地说,它们应该被称为 GNU/Linux 系统。 到上世纪 90 年代初, GNU 项目已经开发出许多高质量的免费软件, 其中包括有名的 emacs 编辑系统、 bash shell 程序、gcc 系列编译程序、gdb 调试程序等等。这些软件为 Linux 操作系统的开发创造了一个合适的 环境, Linux 能够诞生的基础之一。 是 以至于目前许多人都将 Linux 操作系统称为 GNU/Linux”操作系统。“ 1.1.4 POSIX 标准 POSIX(Portable Operating System Interface for Computing Systems)是由 IEEE 和 ISO/IEC 开发的一簇标 准。该标准是基于现有的 UNIX 实践和经验,描述了操作系统的调用服务接口,用于保证编制的应用程序 可以在源代码一级上在多种操作系统上移植运行。它是在 1980 年早期一个 UNIX 用户组(usr/group)的早期 工作的基础上取得的。该 UNIX 用户组原来试图将 AT&T 的系统 V 和 Berkeley CSRG 的 BSD 系统的调用 接口之间的区别重新调和集成,从而于 1984 年产生了/usr/group 标准。1985 年,IEEE 操作系统技术委员 会标准小组委员会(TCOS-SS)开始在 ANSI 的支持下责成 IEEE 标准委员会制定有关程序源代码可移植 性操作系统服务接口正式标准。到了 1986 年 4 月,IEEE 就制定出了试用标准。第一个正式标准是在 1988 年 9 月份批准的(IEEE 1003.1-1988) ,也既以后经常提到的 POSIX.1 标准。 1989 年 POSIX 的工作被转移至 ISO/IEC 社团,并由 15 工作组继续将其制定成 ISO 标准。到 1990 年, POSIX.1 与已经通过的 C 语言标准联合,正式批准为 IEEE 1003.1-1990(也是 ANSI 标准)和 ISO/IEC 9945-1:1990 标准。 POSIX.1 仅规定了系统服务应用程序编程接口(API) ,仅概括了基本的系统服务标准,因此期望对系 统的其它功能也制定出标准。这样 IEEE POSIX 的工作就开始展开了。在 1990 年,刚开始有十个批准的计 划在进行,有近 300 多人参加每季度为期一周的会议。着手的工作有命令与工具标准(POSIX.2)、测试方法 标准(POSIX.3) 、实时 API(POSIX.4)等。到了 1990 年上半年已经有 25 个计划在进行,并且有 16 个工 作组参与了进来。与此同时,还有一些组织也在制定类似的标准,如 X/Open,AT&T,OSF 等。 在 90 年代初,POSIX 标准的制定正处在最后投票敲定的时候,那是 1991-1993 年间。此时正是 Linux 刚刚起步的时候,这个 UNIX 标准为 Linux 提供了极为重要的信息,使得 Linux 的能够在标准的指导下进 行开发,能够与绝大多数 UNIX 系统兼容。在最初的 Linux 内核代码中(0.01 版、0.11 版)就已经为 Linux 与 POSIX 标准的兼容做好了准备工作。在 0.01 版的内核/include/unistd.h 文件中就已经定义了几个有关 POSXI 标准要求的常数符号,并且在注释中就写到“ok,这也许是个玩笑,但我正在着手研究它呢” 。 1991 年 7 月 3 日在 comp.os.minix 上发布的 post 上就已经提到了正在搜集 POSIX 的资料。(当然此时 还不存在 Linux 这个名称,当时 Linus 的脑子里想的可能是 FREAX ☺,FREAX 的英文含义是怪诞的、怪 物、异想天开等)。其中透露了他正在进行 Linux 系统的开发,并且在 Linux 最初的时候已经想到要实现与 POSIX(UNIX 的国际标准)的兼容问题了。 1.1.5 Linux 操作系统的诞生 1981 年 IBM 公司推出享誉全球的微型计算机 IBM PC。在 1981-1991 年间,MS-DOS 操作系统一直是 微型计算机上操作系统的主宰。此时计算机硬件价格虽然逐年下降,但软件价格仍然是居高不下。当时 Apple 的 MACs 操作系统可以说是性能最好的,但是其天价没人能够轻易靠近。 当时的另一个计算机技术阵营是 Unix 世界。但是 Unix 操作系统就不仅是价格贵的问题了。为了寻求 高利率,Unix 经销商将价格抬得极高,PC 小用户就根本不能靠近它。曾经一度受到 Bell Labs 的许可而可 以在大学中用于教学的 UNIX 源代码一直被小心地守卫着不需公开。对于广大的 PC 用户,软件行业的大 型供应商始终没有给出有效的解决该问题的手段。 正在此时,出现了 MINIX 操作系统,并有一本详细的书本描述它的设计实现原理。由于 AST 的书写 的非常详细,并且叙述有条有理,几乎全世界的计算机爱好者都在看这本书以理解操作系统的工作原理。 其中也包括 Linux 系统的创始者 Linus Benedict Torvalds。 当时(1991 年),Linus Benedict Torvalds 是赫尔辛基大学计算机科学系的二年级学生,也是一个自学 hacker。这个 21 岁的芬兰年轻人喜欢鼓捣计算机,测试计算机的能力和限制。但当时缺乏的是一个专业级 的操作系统。MINIX 虽然很好,但只是一个用于教学目的简单操作系统,而不是一个强有力的实用操作系 统。 到 1991 年,GNU 计划已经开发出了许多工具软件。最受期盼的 Gnu C 编译器已经出现,但还没有开 - 6 -
  • 13. 1.1 Linux 的诞生和发展 发出免费的 GNU 操作系统。即使是 MINIX 也开始有了版权,需要购买才能得到源代码。而 GNU 的操作 系统 HURD 一直在开发之中,但并不能在几年内完成。 对于 Linus 来说,已经不能等待了。从 1991 年 4 月份起,他开始酝酿并着手编制自己的操作系统。刚 开始,他的目的很简单,只是为了学习 Intel 386 体系结构保护模式运行方式下的编程技术。但后来 Linux 的发展却完全改变了初衷。 1991 年初, Linux 开始在一台 386sx 兼容微机上学习 minix 操作系统。 通过学习, 他逐渐不能满足 minix 系统的现有性能,并开始酝酿开发一个新的免费操作系统。根据 Linus 在 comp.os.minix 新闻组上发布的消 息,我们可以知道他逐步从学习 minix 系统到开发自己的 Linux 的过程。 Linus 第 1 次向 comp.os.minix 投递消息是在 1991 年 3 月 29 日。题目是“gcc on minix-386 doesn’t optimize”,是有关 gcc 编译器在 minix-386 上运行的优化问题,由此可知,Linus 在 1991 年的初期已经开始 深入研究了 minix 系统,并在这段时间有了改进 minix 操作系统的思想,而且在进一步学习 minix 系统中, 逐步演变为想自己重新设计一个基于 Intel 80386 体系结构的新操作系统。 他在回答有人提出 minix 上的一个问题时, 所说的第一句话是 “阅读源代码” “RTFSC (Read the F**ing ( Source Code :-)”) 。他认为答案就在源程序中。这也说明了对于学习系统软件来说,你不光需要懂得系统的 工作基本原理,还需要结合实际系统,学习实际系统的实现方法。因为理论毕竟是理论,其中省略了许多 枝节,而这些枝节问题虽然没有太多的理论含量,但却是一个系统必要的组成部分,就象麻雀身上的一根 羽毛。 从 1991 年的 4 月份开始,Linus 几乎花了全部时间研究 386-minix 系统(hack the kernel),并且尝试着 移植 GNU 的软件到该系统上(GNU gcc、bash、gdb 等)。并于 4 月 13 日在 comp.os.minix 上发布说自己已 经成功地将 bash 移植到了 minix 上,而且已经爱不释手、不能离开这个 shell 软件了。 第一个与 Linux 有关的消息是在 1991 年 7 月 3 日在 comp.os.minix 上发布的(当然此时还不存在 Linux 这个名称, 当时 Linus 的脑子里想的可能是 FREAX ☺, FREAX 的英文含义是怪诞的、 怪物、异想天开等)。 其中透露了他正在进行 Linux 系统的开发,并且在 Linux 最初的时候已经想到要实现与 POSIX(UNIX 的 国际标准)的兼容问题了。 在 Linus 的下一发布的消息中(1991 年 8 月 25 日 comp.os.minix), 他向所有 minix 用户询问 What would “ you like to see in minix?”(“你最想在 minix 中见到什么?”),在该消息中他首次透露出正在开发一个(免费 的)386(486)操作系统,并且说只是兴趣而已,代码不会很大,也不会象 GNU 的那样专业。开发免费操作 系统这个想法从 4 月份就开始酝酿了,希望大家反馈一些对于 minix 系统中喜欢那些特色不喜欢什么等信 息,由于实际的和其它一些原因,新开发的系统刚开始与 minix 很象(并且使用了 minix 的文件系统) 。并 且已经成功地将 bash(1.08 版)和 gcc(1.40 版)移植到了新系统上,而且在过几个月就可以实用了。 最后, Linus 申明他开发的操作系统没有使用一行 minix 的源代码; 而且由于使用了 386 的任务切换特 性,所以该操作系统不好移植(没有可移植性) ,并且只能使用 AT 硬盘。对于 Linux 的移植性问题,Linus 当时并没有考虑。但是目前 Linux 几乎可以运行在任何一种硬件体系结构上。 到了 1991 年的 10 月 5 日,Linus 在 comp.os.minix 新闻组上发布消息,正式向外宣布 Linux 内核系统 的诞生(Free minix-like kernel sources for 386-AT) 。这段消息可以称为 Linux 的诞生宣言,并且一直广为流 传。因此 10 月 5 日对 Linux 社区来说是一个特殊的日子, 许多后来 Linux 的新版本发布时都选择了这个日 子。所以 RedHat 公司选择这个日子发布它的新系统也不是偶然的。 1.1.6 Linux 操作系统版本的变迁 0.00 (1991.2-4) 两个进程分别显示 AAA BBB 0.01 (1991.9?)第一个正式向外公布的 Linux 内核版本。 0.02 (1991.10.5)该版本以及 0.03 版是内部版本,目前已经无法找到。 0.10 (1991.10)由 Ted Ts’o 发布的 Linux 内核版本。 0.11 (1991.12.8)基本可以正常运行的内核版本(也是本书着重注释的版本) 。 0.12 (1992.1.15)主要加入对数学协处理器的软件模拟程序。 0.95(0.13) (1992.3.8) 开始加入虚拟文件系统思想的内核版本。 0.96 (1992.5.12)开始加入网络支持和虚拟文件系统 VFS。 0.97 (1992.8.1) 0.98 (1992.9.29) 0.99 (1992.12.13) 1.0 (1994.3.14) 1.20 (1995.3.7) - 7 -
  • 14. 1.1 Linux 的诞生和发展 2.0 (1996.2.9) 2.20 (1999.1.26) 2.40 (2001.1.4) 到现在为止,最新版是 表1.1 表新内核源代码字节数 内核版本号 发布日期 代码总字节数 2.4.20 2002.11.29 26,200,000 2.5.52 2002.12.16 30,000,000 将 Linux 系统 0.13 版内核直接改称 0.95 版,Linus 的意思是让大家不要觉得离 1.0 版还很遥远。同时, 从 0.95 版开始,对内核的许多改进之处(补丁程序的提供)均以其他人为主了,而 Linus 的主要任务开始变 成对内核的维护和决定是否采用某个补丁程序。 1.1.7 Linux 名称的来由 Linux 操作系统刚开始时并没有被称作 Linux, Linus 给他的操作系统取名为 FREAX,其英文含义是怪 诞的、怪物、异想天开等意思。在他将新的操作系统上载到 ftp.funet.fi 服务器上时,管理员 Ari Lemke 很 不喜欢这个名称。 他认为既然是 Linus 的操作系统就取其谐音 Linux 作为该操作系统的目录吧, 于是 Linux 这个名称就开始流传下来。 在 Linus 的自传《Just for Fun》一书中,Linus 解释说: “坦白地说, 我从来没有想到过要用 Linux 这个名称发布这个操作系统, 因为这个名字有些太自负了。 而我为最终发布版准备的是什么名字呢?Freax。实际上,内核代码中某些早期的 Makefile - 用于描述如何 编译源代码的文件 - 文件中就已经包含有“Freax”这个名字了,大约存在了半年左右。但其实这也没什么 关系,在当时还不需要一个名字,因为我还没有向任何人发布过内核代码。 ” “而 Ari Lemke,他坚持要用自己的方式将内核代码放到 ftp 站点上, 并且非常不喜欢 Freax 这个名字。 他坚持要用现在这个名字(Linux),我承认当时我并没有跟他多争论。但这都是他取的名字。所以我可以光 明正大地说我并不自负,或者部分坦白地说我并没有本位主义思想。但我想好吧,这也是个好名字,而且 以后为这事我总能说服别人,就象我现在做的这样。 ” -- Linus Torvalds《Just for fun》第 84-88 页。 1.1.8 早期 Linux 系统开发的主要贡献者 从 Linux 的早期源代码中可知, Linux 系统的早期主要开发人员除了 Linus 本人以外, 最著名的人员之 一就是 Theodore Ts'o (Ted Ts'o)。他 1990 年毕业于 MIT 计算机科学专业。在大学时代他就积极参加学校中 举办的各种学生活动。他喜欢烹饪、骑自行车,当然还有就是 hacking on Linux,后来他开始喜欢起业余无 线电报运动。目前他在 IBM 工作从事系统编程及其它重要事务。他还是国际网络设计、操作、销售和研究 者开放团体 IETF 成员。 Linux 在世界范围内的流行也有他很大的功劳。早在 Linux 操作系统刚问世时,他就怀着极大的热情 为 linux 的发展提供了 maillist,几乎是在 Linux 刚开始发布起(1991 年开始)就一直为 Linux 做出贡献的人, 也是最早向 Linux 内核添加程序的人(Linux 内核 0.10 版中的虚拟盘驱动程序 ramdisk.c 和内核内存分配程 序 kmalloc.c)。直到目前仍然从事着与 Linux 有关的工作。他当时在北美洲地区最早设立了 linux 的 ftp 站 点(tsx-11.mit.edu),而且至今仍然为广大 linux 用户提供服务。他对 linux 作出的最大贡献之一是提出并实 现了 ext2 文件系统。该文件系统已成为 linux 世界中事实上的文件系统标准。最近他又推出了 ext3 文件系 统,大大提高了文件系统的稳定性和访问效率。作为对他的推崇,第 97 期(2002 年 5 月)的 linuxjournal 期刊将他作为了封面人物,并对他进行了采访。目前,他为 IBM linux 技术中心工作,并从事着有关 LSB(Linux Standard Base)等方面的工作。 Linux 社区中另一位著名人物是 Alan Cox。他原工作于英国威尔士斯旺西大学(Swansea University College)。刚开始他特别喜欢玩电脑游戏,尤其是 MUD(Multi-User Dungeon or Dimension,多用户网络游 戏) 。在 90 年代早期 games.mud 新闻组的 posts 中你可以找到他发表的大量 posts。他甚至为此还写了一偏 MUD 的发展史(rec.games.mud 新闻组,1992 年 3 月 9 日,A history of MUD)。由于 MUD 游戏与网络密切 相关,慢慢的他对计算机网络开始感兴趣。为了玩游戏并提高电脑运行游戏的速度以及网络传输的速度, 他开始接触各种类型的操作系统,为他的游戏选择一个最为满意的平台。由于没钱,即使 Minix 他都买不 起,当 Linux 0.11 和 386BSD 发布时,他考虑良久总算买了一台 386SX 电脑。由于 386BSD 需要数学协处 - 8 -
  • 15. 1.2 内容综述 理器的支持,而 386SX 中是不带的,所以他安装了 Linux 系统。于是他开始学习带有免费源代码的 Linux。 开始对 Linux 产生了兴趣,尤其是有关网络方面的实现。在关于 Linux 的单用户运行模式问题的讨论中, 他甚至赞叹 Linux 实现的巧妙(beautifully)。 Linux 0.95 版发布之后, 他开始为 Linux 系统编写补丁程序 (修改程序)(记得他最早的两个补丁程序, 都没有被 Linus 采纳) ,成为 Linux 上 TCP/IP 网络代码的最早使用人之一。后逐渐加入 Linux 的开发队伍, 并开始成为维护 Linux 内核源代码的主要负责人之一,也可以说成为 Linux 社团中在 Linus 之后最为重要 的人物。以后 Microsoft 公司曾经邀请他加盟,但他却干脆地拒绝了。从 2001 年开始他负责维护 Linux 内 核 2.4.x 的代码(Linus 主要负责开发最新开发版内核的研制(奇数版,比如 2.5.x 版)。 《内核骇客手册》一书的作者 Michael K. Johnson 也是最早接触 Linux 操作系统的人之一(从 0.97 版)。 他还是著名 Linux 文档计划(Linux Document Project - LDP)的发起者之一。曾经在 Linux Journel 工作, 现在 RedHat 公司工作。 Linux 系统并不是仅有这些中坚力量就能发展成今天这个样子的,还有许多计算机高手对 Linux 做出 了极大的贡献,这里就不一一列举了。主要贡献者的具体名单可参见 Linux 内核中的 CREDITS 文件,其 中以字母顺序列出了对 Linux 做出较大贡献的近 400 人的名单列表,包括他们的 email 地址和通信地址、 主页以及主要贡献事迹等信息。 通过上述说明,我们可以对上述 Linux 的五大支柱归纳如下: UNIX 操作系统 -- UNIX 于 1969 年诞生在 Bell 实验室。Linux 就是 UNIX 的一种克隆系统。UNIX 的 重要性就不用多说了。 MINIX 操作系统 -- Minix 操作系统也是 UNIX 的一种克隆系统,它于 1987 年由著名计算机教授 Andrew S. Tanenbaum 开发完成。由于 MINIX 系统的出现并且提供源代码(只能免费用于大学内)在全世界 的大学中刮起了学习 UNIX 系统旋风。Linux 刚开始就是参照 Minix 系统于 1991 年才开始开发。 GNU 计划-- 开发 Linux 操作系统,以及 Linux 上所用大多数软件基本上都出自 GNU 计划。Linux 只 是操作系统的一个内核,没有 GNU 软件环境(比如说 bash shell),则 Linux 将寸步难行。 POSIX 标准 -- 该标准在推动 Linux 操作系统以后朝着正规路上发展起着重要的作用。是 Linux 前进 的灯塔。 INTERNET -- 如果没有 Intenet 网,没有遍布全世界的无数计算机骇客的无私奉献,那么 Linux 最多 只能发展到 0.13(0.95)版的水平。 1.2 内容综述 本文将主要对 Linux 的早期内核 0.11 版进行详细描述和注释。Linux-0.11 版本是在 1991 年 12 月 8 日 发布的。在发布时包括以下文件: bootimage.Z - 具有美国键盘代码的压缩启动映像文件; rootimage.Z - 以 1200kB 压缩的根文件系统映像文件; linux-0.11.tar.Z- 内核源代码文件; as86.tar.Z - linux bruce evans'二进制执行文件; 是 16 位的汇编程序和装入程序; INSTALL-0.11 - 更新过的安装信息文件。 目前除了原来的 rootimage.Z 文件,其它四个文件均能找到。 本文主要详细分析 linux-0.11 内核中的所有源代码程序,对每个源程序文件都进行了详细注释,包括 对 Makefile 文件的注释。分析过程主要是按照计算机启动过程进行的。因此分析的连贯性到初始化结束内 核开始调用 shell 程序为止。其余的各个程序均针对其自身进行分析,没有连贯性,因此可以根据自己的需 要进行阅读。但在分析时还是提供了一些应用实例。 所有的程序在分析过程中如果遇到作者认为是较难理解的语句时, 将给出相关知识的详细介绍。比如, 在阅读代码头一次遇到 C 语言内嵌汇编码时,将对 gnu C 语言的内嵌汇编语言进行较为详细的介绍;在遇 到对中断控制器进行输入/输出操作时,将对 Intel 中断控制器(8259A)芯片给出详细的说明,并列出使用 - 9 -
  • 16. 1.2 内容综述 的命令和方法。这样做有助于加深对代码的理解,又能更好的了解所用硬件的使用方法,作者认为这种解 读方法要比单独列出一章内容来总体介绍硬件或其它知识要效率高得多。 拿 Linux 0.11 版内核来“开刀”是为了提高我们认识 Linux 运行机理的效率。Linux-0.11 版整个内核 源代码只有 325K 字节左右,其中包括的内容基本上都是 Linux 的精髓。而目前最新的 2.5.XX 版内核非常 大,将近有 188 兆字节,即使你花一生的经历来阅读也未必能全部都看完。也许你要问“既然要从简入手, 为什么不分析更小的 Linux 0.01 版内核源代码呢?它只有 240K 字节左右”主要原因是因为 0.01 版的内核 代码有太多的不足之处,甚至还没有包括对软盘的驱动程序,也没有很好地涉及数学协处理器的使用以及 对登陆程序的说明。并且其引导启动程序的结构也与目前的版本不太一样,而 0.11 版的引导启动程序结构 则与现在的基本上是一样的。另外一个原因是可以找到 0.11 版早期的已经编译制作好的内核映像文件 (bootimage),可以用来进行引导演示。如果再配上简单的根文件系统映像文件(rootimage),那么它就可以 进行正常的运行了。 拿 Linux 0.11 版进行学习也有不足之处,比如该内核版本中尚不包括有关进程等待队列、TCP/IP 网络 等方面的一些当前非常重要的代码,但好在 Linux 中的网络代码基本上是自成一体的,与内核机制关系不 是非常大,因此可以在了解了 Linux 工作的基本原理之后再去分析这些代码。 本文对 Linux 内核中所有的代码都进行了说明。为了保持结构的完整性,对代码的说明是以内核中源 代码的组成结构来进行的,基本上是以每个源代码中的目录为一章内容进行介绍。介绍的源程序文件的次 序可参见前面的文件列表索引。整个 Linux 内核源代码的目录结构如下列表 1.1 所示。所有目录结构均是 以 linux 为当前目录。 列表 1.1 Linux/目录 名称 大小 最后修改日期(GMT) 说明 boot/ 1991-12-05 22:48:49 fs/ 1991-12-08 14:08:27 include/ 1991-09-22 19:58:04 init/ 1991-12-05 19:59:04 kernel/ 1991-12-08 14:08:00 lib/ 1991-12-08 14:10:00 mm/ 1991-12-08 14:08:21 tools/ 1991-12-04 13:11:56 Makefile 2887 bytes 1991-12-06 03:12:46 本书内容可以分为三个部分。第 1 章至第 4 章是描述内核引导启动和 32 位运行方式的准备阶段,作 为学习内核的初学者应该全部进行阅读。第二部分从第 5 章到第 10 章是内核代码的主要部分。其中第 5 章内容可以作为阅读本部分后续章节的索引来进行。第 11 章到第 13 章是第三部分内容,可以作为阅读第 二部分代码的参考。 第 2 章概要地描述了 Linux 操作系统的体系结构,内核源代码文件放置的组织结构以及每个文件大致 功能。还介绍了 Linux 对物理内存的使用分配方式以及对虚拟线性地址的使用分配,以及如何在 RedHat 9 操作系统上编译本书所讨论的 linux 内核, 对内核代码需要修改的地方。 最后开始注释内核程序包中 Linux/ 目录下的所看到的第一个文件,也即内核代码的总体 Makefile 文件的内容。该文件是所有内核源程序的编 译管理配置文件,供编译管理工具软件 make 使用。 第 3 章将详细注释 boot/目录下的三个汇编程序, 其中包括磁盘引导程序 bootsect.s、获取 BIOS 中参数 的 setup.s 汇编程序和 32 位运行启动代码程序 head.s。这三个汇编程序完成了内核从块设备上引导加载到 内存,对系统配置参数的进行探测,完成了进入 32 位保护模式运行之前的所有工作。为内核系统进一步 的初始化工作作好了准备。 - 10 -
  • 17. 1.2 内容综述 第 4 章主要介绍 init/目录中内核系统的初始化程序 main.c。它是内核完成所有初始化工作并进入正常 运行的关键地方。在完成了系统所有的初始化工作后,创建了用于 shell 的进程。在介绍该程序时将需要查 看其所调用的其它程序,因此对后续章节的阅读可以按照这里调用的顺序进行。由于内存管理程序的函数 在内核中被广泛使用,因此该章内容应该最先选读。当你能真正看懂直到 main.c 程序为止的所有程序时, 你应该已经对 Linux 内核有了一定的了解,可以说已经有一半入门了☺,但你还需要对文件系统、系统调 用、各种驱动程序等进行更深一步的阅读。 第 5 章主要介绍 kenel/目录中的所有程序。其中最重要的部分是进程调度函数 schedule()、sleep_on() 函数和有关系统调用的程序。此时你应该已经对其中的一些重要程序有所了解。 第 6 章对 kernel/dev_blk/目录中的块设备程序进行了注释说明。该章主要含有硬盘、软盘等块设备的 驱动程序,主要与文件系统和高速缓冲区打交道。因此,在阅读这章内容时需首先浏览一下文件系统的章 节。 第 7 章对 kernel/dev_chr/目录中的字符设备驱动程序进行注释说明。这一章中主要涉及到串行线路驱 动程序,键盘驱动程序和显示器驱动程序,因此含有较多与硬件有关的内容。在阅读时需要参考一下相关 硬件的书籍。 第 8 章介绍 kernel/math/目录中的数学协处理器的仿真程序。 由于本书所注释的内核版本, 还没有真正 开始支持协处理器,因此本章的内容较少,也比较简单。只需有一般性的了解即可。 第 9 章介绍内核源代码 fs/目录中的文件系统程序,在看这章内容时建议你能够暂停一下而去阅读 Andrew S. Tanenbaum 的《操作系统设计与实现》一书中有关 minix 文件系统的章节,因为最初的 Linux 系 统是只支持 minix 一种文件系统,Linux 0.11 版也不例外。 第 10 章解说 mm/目录中的内存管理程序。要透彻地理解这方面的内容,需要对 Intel 80X86 微处理器 的保护模式运行方式有足够的理解, 因此本章在适当的地方包含有较为完整的有关 80X86 保护模式运行方 式的说明,这些知识基本上都可以参考 Intel 80386 程序员编程手册(Intel 80386 Programmer's Reference Manual)。但在此章中,以源代码中的运用实例为对象进行解说,应该可以更好地理解它的工作原理。 现有的 Linux 内核分析书籍都缺乏对内核头文件的描述,因此对于一个初学者来讲,在阅读内核程序 时会碰到许多障碍。 本书的第 11 章对 include/目录中的所有头文件进行了详细说明,基本上对每一个定义、 每一个常量或数据结构都进行了详细注释。为了便于在阅读时参考查阅,本书在附录中还对一些经常要用 到的重要的数据结构和变量进行了归纳注释,但这些内容实际上都能在这一章中找到。虽然该章内容主要 是为阅读其它章节中的程序作参考使用的,但是若想彻底理解内核的运行机制,仍然需要了解这些头文件 中的许多细节。 第 12 章介绍了 Linux 0.11 版内核源代码 lib/目录中的所有文件。这些库函数文件主要向编译系统等系 统程序提供了接口函数,对以后理解系统软件会有较大的帮助。由于这个版本较低,所以这里的内容并不 是很多,可以很快地看完。这也是我们为什么选择 0.11 版的原因之一。 第 13 章介绍 tools/目录下的 build.c 程序。这个程序并不会包括在编译生成的内核映像(image)文件中, 它仅用于将内核中的磁盘引导程序块与其它主要内核模块连接成一个完整了内核映像 kernel image) ( 文件。 最后是附录和索引。附录中给出了 Linux 内核中的一些常数定义和基本数据结构定义,以及保护模式 运行机制的简明描述。 为了便于查阅,在本书的附录中还单独列出了内核中要用到的有关 PC 机硬件方面的信息。在参考文 献中,我们仅给出了在阅读源代码时可以参考的书籍、文章等信息,并没有包罗万象地给出一大堆的繁杂 凌乱的文献列表。比如在引用 Linux 文档项目 LDP(Linux Document Project)中的文件时,我们会明确地 列出具体需要参考哪一篇 HOWTO 文章,而并不是仅仅给出 LDP 的网站地址了事。 Linus 在最初开发 Linux 操作系统内核时,主要参考了 3 本书。一本是 M. J. Bach 著的《UNIX 操作系 统设计》 (文献[11]),该书描述了 UNIX 系统 V 内核的工作原理和数据结构。Linus 使用了该书中很多函数 的算法,Linux 内核源代码中很多重要函数的名称都取自该书。因此,在阅读本书时,这是一本必不可少 的内核工作原理方面的参考书籍。 另一本是 John H. Crawford 等编著的 Programming the 80386》文献[21]) 《 ( , 是讲解 80x86 下保护模式编程方法的好书。还有一本就是 Andrew S.Tanenbaum 著的《MINIX 操作系统设 计与实现》一书的第 1 版(文献[22]) Linus 主要使用了该书中描述的 MINIX 文件系统 1.0 版,而且在早 。 期的 Linux 内核中也仅支持该文件系统,所以在阅读本书有关文件系统一章内容时,文件系统的工作原理 方面的知识完全可以从 Tanenbaum 的书中获得。 在对每个程序进行解说时,我们首先简单说明程序的主要用途和目的、输入输出参数以及与其它程序 - 11 -
  • 18. 1.3 本章小结 的关系,然后列出程序的完整代码并在其中对代码进行详细注释,注释时对原程序代码或文字不作任何方 面的改动或删除,因为 C 语言是一种英语类语言,程序中原有的少量英文注释对常数符号、变量名等也提 供了不少有用的信息。在代码之后是对程序更为深入的解剖,并对代码中出现的一些语言或硬件方面的相 关知识进行说明。如果在看完这些信息后回头再浏览一遍程序,你会有更深一层的体会。 对于阅读本书所需要的一些基本概念知识的介绍都散布在各个章节相应的地方,这样做主要是为了能 够方便的找到,而且在结合源代码阅读时,对一些基本概念能有更深的理解。 最后要说明的是当你已经完全理解了本文说解说的一切时,并不代表你已经成为一个 Linux 行家了, 你只是刚刚踏上 Linux 的征途,具有了一定的成为一个 Linux GURU 的初步知识。这时你应该去阅读更多 的源代码,最好是循序渐进地从 1.0 版本开始直到最新的正在开发中的奇数编号的版本。在撰写这本书时 最新的 Linux 内核是 2.5.44 版。当你能快速理解这些开发中的最新版本甚至能提出自己的建议和补丁 (patch)程序时,我也甘拜下风了☺。 1.3 本章小结 首先阐述了 Linux 诞生和发展不可缺少的五个支柱:UNIX 最初的开放原代码版本为 Linux 提供了实 现的基本原理和算法、Rechard Stallman 的 GNU 计划为 Linux 系统提供了丰富且免费的各种实用工具、 POSIX 标准的出现为 Linux 提供了实现与标准兼容系统的参考指南、A.S.T 的 MINIX 操作系统为 Linux 的 诞生起到了不可忽缺的参考、Internet 是 Linux 成长和壮大的必要环境。最后本章概述了书中的基本内容。 最后祝你征途愉快! - 12 -
  • 19. 2.1 Linux 内核模式 第2章 linux 内核体系结构 本章首先概要介绍了早期 Linux 内核的编制模式和体系结构,然后详细描述了 linux 内核源代码目录 中组织形式以及子目录中各个代码文件的主要功能以及基本调用的层次关系。接下来就直接切入正题,从 内核源文件 linux/目录下的第一个文件 Makefile 开始,对每一行代码进行详细注释说明。 一个完整可用的操作系统主要由 4 部分组成:硬件、操作系统内核、操作系统服务和用户应用程序, 见图 2.1 所示。用户应用程序是指那些字处理程序、Internet 浏览器程序或用户自行编制的各种应用程序; 操作系统服务程序是指那些向用户所提供的服务被看作是操作系统的部分功能的程序。在 Linux 操作系统 上,这些程序包括 X 窗口系统、shell 命令解释系统以及那些内核编程接口等系统程序;操作系统内核程序 即是本书所感兴趣的部分,它主要用于对硬件资源的抽象和访问调度。 用户应用程序 操作系统服务 操作系统内核 硬件系统 图2.1 操作系统组成部分。 Linux 内核的主要用途就是为了与计算机硬件进行交互,实现对硬件部件的编程控制和接口操作,调 度对硬件资源的访问,并为计算机上的用户程序提供一个高级的执行环境和对硬件的虚拟接口。 在本章内容中,我们首先基于 Linux 0.11 版的内核源代码,简明地描述 Linux 内核的基本体系结构、主要 构成模块。然后对源代码中出现的几个重要数据结构进行说明。最后描述了构建 Linux 0.11 内核编译实验 环境的方法。 2.1 Linux 内核模式 目前,操作系统内核的结构模式主要可分为整体式的单内核模式和层次式的微内核模式。而本书所注 释的 Linux 0.11 内核,则是采用了单内核模式。单内核模式的主要优点是内核代码结构紧凑、执行速度快, 不足之处主要是层次结构性不强。 在单内核模式的系统中,操作系统所提供服务的流程为:应用主程序使用指定的参数值执行系统调用 指令(int x80),使 CPU 从用户态(User Mode)切换到核心态(Kernel Model) ,然后操作系统根据具体的 参数值调用特定的系统调用服务程序,而这些服务程序则根据需要再底层的一些支持函数以完成特定的功 能。在完成了应用程序所要求的服务后,操作系统又从核心态切换回用户态,返回到应用程序中继续执行 后面的指令。因此概要地讲,单内核模式的内核也可粗略地分为三个层次:调用服务的主程序层、执行系 统调用的服务层和支持系统调用的底层函数。见图 2.2 所示。 - 13 -
  • 20. 2.2 Linux 内核系统体系结构 主程序 系统服务 支持函数 图2.2 单内核模式的简单结构模型 2.2 Linux 内核系统体系结构 Linux 内核主要由 5 个模块构成,它们分别是:进程调度模块、内存管理模块、文件系统模块、进程 间通信模块和网络接口模块。 进程调度模块用来负责控制进程对 CPU 资源的使用。所采取的调度策略是各进程能够公平合理地访 问 CPU,同时保证内核能及时地执行硬件操作。内存管理模块用于确保所有进程能够安全地共享机器主内 存区,同时,内存管理模块还支持虚拟内存管理方式,使得 Linux 支持进程使用比实际内存空间更多大的 内存容量。并可以利用文件系统把暂时不用的内存数据块会被交换到外部存储设备上去,当需要时再交换 回来。文件系统模块用于支持对外部设备的驱动和存储。虚拟文件系统模块通过向所有的外部存储设备提 供一个通用的文件接口,隐藏了各种硬件设备的不同细节。从而提供并支持与其它操作系统兼容的多种文 件系统格式。进程间通信模块子系统用于支持多种进程间的信息交换方式。网络接口模块提供对多种网络 通信标准的访问并支持许多网络硬件。 这几个模块之间的依赖关系见图 2.3 所示。其中的连线代表它们之间的依赖关系,虚线和虚框部分表 示 Linux 0.11 中还未实现的部分(从 Linux 0.95 版才开始逐步实现虚拟文件系统,而网络接口的支持到 0.96 版才有) 。 内存管理 虚拟文件系统 进程调度 进程间通信 文件系统 网络接口 图2.3 Linux 内核系统模块结构及相互依赖关系 由图可以看出,所有的模块都与进程调度模块存在依赖关系。因为它们都需要依靠进程调度程序来挂 起(暂停)或重新运行它们的进程。通常,一个模块会在等待硬件操作期间被挂起,而在操作完成后才可 继续运行。例如,当一个进程试图将一数据块写到软盘上去时,软盘驱动程序就可能在启动软盘旋转期间 - 14 -
  • 21. 2.3 Linux 内核进程控制 将该进程置为挂起等待状态,而在软盘进入到正常转速后再使得该进程能继续运行。另外 3 个模块也是由 于类似的原因而与进程调度模块存在依赖关系。 其它几个依赖关系有些不太明显,但同样也很重要。进程调度子系统需要使用内存管理器来调整一特 定进程所使用的物理内存空间。进程间通信子系统则需要依靠内存管理器来支持共享内存通信机制。这种 通信机制允许两个进程访问内存的同一个区域以进行进程间信息的交换。虚拟文件系统也会使用网络接口 来支持网络文件系统(NFS) ,同样也能使用内存管理子系统来提供内存虚拟盘(ramdisk)设备。而内存 管理子系统也会使用文件系统来支持内存数据块的交换操作。 若从单内核模式结构模型出发,我们还可以根据 linux 0.11 内核源代码的结构将内核主要模块绘制成 图 2.4 所示的框图结构。 用户程序 函 数 库 用户级 内核级 系统调用接口 进程间通信 文件子系统 进程控制 高速缓冲 子系统 调度程序 字符设备 块设备 内存管理 设备驱动程序 硬 件 控 制 内核级 硬 件 硬件级 图2.4 内核结构框图 其中内核级中的几个方框,除了硬件控制方框以外,其它粗线方框分别对应内核源代码的目录组织结 构。 除了这些图中已经给出的依赖关系以外,所有这些模块还会依赖于内核中的通用资源。这些资源包括 内核所有子系统都会调用的内存分配和收回函数、打印警告或出错信息函数以及一些系统调试函数。 2.3 Linux 内核进程控制 程序是一个可执行的文件,而进程(process)是一个执行中的程序实例。在 Linux 操作系统上同时可 以执行多个进程。对于 linux 0.11 内核来讲,系统最多可有 64 个进程同时存在。 系统除了第一个进程是 “手 工”建立以外,其余的都是进程使用系统调用 fork 创建的新进程, 被创建的进程称为子进程 child process) ( , 创建者,则称为父进程(parent process) 。内核程序使用进程标识号(process ID,pid)来标识每个进程。 进程由可执行的指令代码、数据和堆栈区组成。进程中的代码和数据部分分别对应一个执行文件中的代码 段、数据段。每个进程只能执行自己的代码和访问自己的数据及堆栈区。进程之间相互之间的通信需要通 过系统调用了进行。对于只有一个 CPU 的系统,在某一时刻只能有一个进程正在运行。内核通过调度程 序分时调度各个进程运行。 - 15 -
  • 22. 2.4 Linux 内核对内存的使用方法 Linux 系统中,一个进程可以在内核态(kernel mode)或用户态(user mode)下执行,因此,linux 内 核栈和用户栈是分开的。用户栈用于进程在用户态下临时保存调用函数的参数、局部变量等数据。内核栈 则含有内核程序执行函数调用时的信息。 内核程序是通过进程表对进程进行管理的,每个进程在进程表中占有一项。在 linux 系统中,进程表 项是一个 task 结构。 当一个进程在执行时,CPU 的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下 文。当内核需要切换(switch)至另一个进程时,它就需要保存当前进程的所有状态,也即保存当前进程 的上下文,以便在再次执行该进程时,能够恢复到切换时的状态执行下去。在发生中断时,内核就在被中 断进程的上下文中,在内核态下执行中断服务例程。但同时会保留所有需要用到的资源,以便中断服务结 束时能恢复被中断进程的执行。 一个进程在其生存期内,可处于一组不同的状态下,称为进程状态。见下图所示。 用户运行态 running 0 内核运行态 系统调用或中断 返回 running 中断,中断返回 可中断睡眠状态 睡眠 终止 僵死状态 1 0 3 interruptible zombie 睡眠 终止 调度 唤醒 不可中断睡眠状态 停止状态 2 0 4 uninterruptible stopped 唤醒 就绪态 running 图2.5 进程状态及转换关系 当进程正在被 CPU 执行时,被称为处于执行状态(running) 。当进程正在等待系统中的资源而处于等 待状态时,则称其处于睡眠等待状态。在 linux 系统中,还分为可中断的和不可中断的等待状态。当系统 资源已经可用时,进程就被唤醒而进入准备运行状态,该状态称为就绪态。当进程已停止运行,但其父进 程还没有询问其状态时,则称该进程处于僵死状态。当进程被终止时,称其处于停止状态。 只有当进程从“内核运行态”转移到“睡眠状态”时,内核才会进行进程切换操作。在内核态下运行 的进程不能被其它进程抢占,而且一个进程不能改变另一个进程的状态。为了避免进程切换时造成内核数 据错误,内核在执行临界区代码时会禁止一切中断。 2.4 Linux 内核对内存的使用方法 在 linux 0.11 内核中,为了有效地使用系统的物理内存,内存被划分成几个功能区域,见下图 2.6 所示。 - 16 -
  • 23. 2.4 Linux 内核对内存的使用方法 内核模块 高速缓冲区 虚拟盘 主内存区 0 640K 1M 显存和 BIOS ROM 图2.6 物理内存使用的功能分布图 其中, linux 内核程序占据在物理内存的开始部分, 接下来是用于供硬盘或软盘等块设备使用的高速缓 冲区部分。当一个进程需要读取块设备中的数据时,系统会首先将数据读到高速缓冲区中;当有数据需要 写到块设备上去时,系统也是先将数据放到高速缓冲区中,然后由块设备驱动程序写到设备上。最后部分 是供所有程序可以随时申请使用的主内存区部分。内核程序在使用主内存区时,也同样要首先向内核的内 存管理模块提出申请,在申请成功后方能使用。对于含有 RAM 虚拟盘的系统,主内存区头部还要划去一 部分,共虚拟盘存放数据。 由于计算机系统中所含的实际物理内存容量是有限制的。为了能有效地使用这些物理内存,Linux 采 用了 Intel CPU 的内存分页管理机制,使用虚拟线性地址与实际物理内存地址映射的方法让所有同时执行 的程序共同使用有限的内存。内存分页管理的基本原理是将整个主内存区域划分成 4096 字节为一页的内 存页面。程序申请使用内存时,就以内存页为单位进行分配。 在使用这种内存分页管理方法时,每个执行中的进程(任务)可以使用比实际内存容量大得多的线性 地址空间。对于 Intel 80386 系统,其 CPU 可以提供多达 4G 的线性地址空间。对于 linux 0.11 内核,系统 设置全局描述符表 GDT 中的段描述符项数最大为 256, 其中 2 项空闲、 项系统使用, 2 每个进程使用两项。 因此,此时系统可以最多容纳(256-4)/2 + 1=127 个任务,并且虚拟地址范围是 ((256-4)/2)* 64MB 约等于 8G。 0.11 内核中人工定义最大任务数 NR_TASKS = 64 个, 但 每个进程虚拟地址 (或线性地址) 范围是 64M, 并且各个进程的虚拟地址起始位置是(任务号-1)*64MB。 因此所使用的虚拟地址空间范围是 64MB*64 =4G, 见图 2.7 所示。4G 正好与 CPU 的线性地址空间范围或物理地址空间范围相同,因此在 0.11 内核中比较容 易混淆三种地址概念。 进程(任务)0 进程 1 进程 2 0 64M 128M 192M 4G - 执行代码 - 数据 - 参数和堆栈区 图2.7 linux 0.11 虚拟地址空间的使用示意图 linux 0.11 中,在进行地址映射时,我们需要分清 3 种地址之间的变换:a. 进程虚拟地址,是从虚拟 地址 0 开始计,最大 64M;b. CPU 的线性地址空间(0--4G);c. 实际物理内存地址。 进程的虚拟地址需要首先通过其局部段描述符变换为 CPU 整个线性地址空间中的地址,然后再使用 - 17 -
  • 24. 2.5 Linux 内核源代码的目录结构 页目录表 PDT(一级页表)和页表 PT(二级页表)映射到实际物理地址页上。因此两种变换不能混淆。 为了使用实际物理内存,每个进程的线性地址通过二级内存页表动态地映射到主内存区域的不同内存 页上。因此每个进程最大可用的虚拟内存空间是 64MB。每个进程的逻辑地址通过加上任务号*64M,即可 转换为线性地址。不过在注释中,我们通常将进程中的地址简单地称为线性地址。 有关内存分页管理的详细信息,请参见第 10 章开始部分的有关说明,或参见附录。 2.5 Linux 内核源代码的目录结构 由于 Linux 内核是一种单内核模式的系统,因此,内核中所有的程序几乎都有紧密的联系,它们之间 的依赖和调用关系非常密切。所以在阅读一个源代码文件时往往需要参阅其它相关的文件。因此有必要在 开始阅读内核源代码之前,先熟悉一下源代码文件的目录结构和安排。 这里我们首先列出 Linux 内核完整的源代码目录,包括其中的子目录。然后逐一介绍各个目录中所包 含程序的主要功能,使得整个内核源代码的安排形式能在我们的头脑中建立起一个大概的框架,以便于下 一章开始的源代码阅读工作。 当我们使用 tar 命令将 linux-0.11.tar.gz 解开时,内核源代码文件被放到了 linux 目录中。其中的目录结 构为: linux ├─ boot 系统引导汇编程序 ├─ fs 文件系统 ├─ include 头文件(*.h) │ ├─ asm 与 CPU 体系结构相关的部分 │ ├─ linux Linux 内核专用部分 │ └─ sys 系统数据结构部分 ├─ init 内核初始化程序 ├─ kernel 内核进程调度、信号处理、系统调用等程序 │ ├─ blk_drv 块设备驱动程序 │ ├─ chr_drv 字符设备驱动程序 │ └─ math 数学协处理器仿真处理程序 ├─ lib 内核库函数 ├─ mm 内存管理程序 └─ tools 生成内核 Image 文件的工具程序 图2.8 Linux 内核源代码目录结构 该内核版本的源代码目录中含有 14 个子目录,总共包括 102 个代码文件。下面逐个对这些子目录中 的内容进行描述。 2.5.1 内核主目录 linux linux 目录是源代码的主目录,在该主目录中除了包括所有的 14 个子目录以外,还含有唯一的一个 makefile 文件。该文件是编译辅助工具软件 make 的参数配置文件。make 工具软件的主要用途是通过识别 哪些文件已被修改过,从而自动地决定在一个含有多个源程序文件的程序系统中哪些文件需要被重新编 译。因此,make 工具软件是程序项目的管理软件。 linux 目录下的这个 makefile 文件还嵌套地调用了所有子目录中包含的 makefile 文件,这样,当 linux 目录 (包括子目录)下的任何文件被修改过时,make 都会对其进行重新编译。因此为了编译整个内核所有的 源代码文件,只要在 linux 目录下运行一次 make 软件即可。 2.5.2 引导启动程序目录 boot boot 目录中含有 3 个汇编语言文件,是内核源代码文件中最先被编译的程序。这 3 个程序完成的主要 功能是当计算机加电时引导内核启动,将内核代码加载到内存中,并做一些进入 32 位保护运行方式前的 系统初始化工作。 其中 bootsect.s 和 setup.s 程序需要使用 as86 软件来编译,使用的是 as86 的汇编语言格式 - 18 -
  • 25. 2.5 Linux 内核源代码的目录结构 (与微软的类似) ,而 head.s 需要用 GNU as 来编译,使用的是 AT&T 格式的汇编语言。这两种汇编语言在 下一章的代码注释里以及代码列表后面的说明中会有简单的介绍。 bootsect.s 程序是磁盘引导块程序,编译后会驻留在磁盘的第一个扇区中(引导扇区,0 磁道(柱面) , 0 磁头,第 1 个扇区) 。在 PC 机加电 ROM BIOS 自检后,将被 BIOS 加载到内存 0x7C00 处进行执行。 setup.s 程序主要用于读取机器的硬件配置参数,并把内核模块 system 移动到适当的内存位置处。 head.s 程序会被编译连接在 system 模块的最前部分,主要进行硬件设备的探测设置和内存管理页面的 初始设置工作。 2.5.3 文件系统目录 fs 是文件系统实现程序的目录,共包含 17 个 C 语言程序。这些程序之间的主要引用关系见图 2.9 所示 图中每个方框代表一个文件,从上到下按基本按引用关系放置。其中各文件名均略去了后缀.c,虚框中是 的程序文件不属于文件系统,带箭头的线条表示引用关系,粗线条表示有相互引用关系。 高层函数 file_table exec fcntl ioctl 数据访问 open stat read_write char_dev pipe block_dev file_dev 低层操作函数 namei inode truncate bitmap super 高速缓冲管理 buffer ll_rw_block 图2.9 fs 目录中各程序中函数之间的引用关系。 由图可以看出,该目录中的程序可以划分成四个部分:高速缓冲区管理、低层文件操作、文件数据访 问和文件高层函数,在对本目录中文件进行注释说明时,我们也将分成这四个部分来描述。 对于文件系统,我们可以将它看成是内存高速缓冲区的扩展部分。所有对文件系统中数据的访问,都 需要首先读取到高速缓冲区中。本目录中的程序主要用来管理高速缓冲区中缓冲块的使用分配和块设备上 的文件系统。管理高速缓冲区的程序是 buffer.c,而其它程序则主要都是用于文件系统管理。 在 file_table.c 文件中,目前仅定义了一个文件句柄(描述符)结构数组。 ioctl.c 文件将引用 kernel/chr_dev/tty.c 中的函数,实现字符设备的 io 控制功能。 exec.c 程序主要包含一个执行程序函数 do_execve(),它是所有 exec()函数簇中的主要函数。fcntl.c 程序用于实现文件 i/o 控制的系统调用函数。 read_write.c 程序用于实现文件读/写和定位三个系统调用函数。stat.c 程序中实现了两个获取文件状态的系 统调用函数。open.c 程序主要包含实现修改文件属性和创建与关闭文件的系统调用函数。 char_dev.c 主要包含字符设备读写函数 rw_char()。pipe.c 程序中包含管道读写函数和创建管道的系统 调用。 file_dev.c 程序中包含基于 i 节点和描述符结构的文件读写函数。 namei.c 程序主要包括文件系统中目 录名和文件名的操作函数和系统调用函数。block_dev.c 程序包含块数据读和写函数。inode.c 程序中包含针 对文件系统 i 节点操作的函数。 truncate.c 程序用于在删除文件时释放文件所占用的设备数据空间。 bitmap.c - 19 -
  • 26. 2.5 Linux 内核源代码的目录结构 程序用于处理文件系统中 i 节点和逻辑数据块的位图。super.c 程序中包含对文件系统超级块的处理函数。 buffer.c 程序主要用于对内存高速缓冲区进行处理。虚框中的 ll_rw_block 是块设备的底层读函数,它并不 在 fs 目录中,而是 kernel/blk_dev/ll_rw_block.c 中的块设备读写驱动函数。放在这里只是让我们清楚的看 到,文件系统对于块设备中数据的读写,都需要通过高速缓冲区与块设备的驱动程序(ll_rw_block())来 操作来进行,文件系统程序集本身并不直接与块设备的驱动程序打交道。 在对程序进行注释过程中,我们将另外给出这些文件中各个主要函数之间的调用层次关系。 2.5.4 头文件主目录 include 头文件目录中总共有 32 个.h 头文件。其中主目录下有 13 个,asm 子目录中有 4 个,linux 子目录中有 10 个,sys 子目录中有 5 个。这些头文件各自的功能见如下简述,具体的作用和所包含的信息请参见对头 文件的注释一章。 <a.out.h> a.out 头文件,定义了 a.out 执行文件格式和一些宏。 <const.h> 常数符号头文件,目前仅定义了 i 节点中 i_mode 字段的各标志位。 <ctype.h> 字符类型头文件。定义了一些有关字符类型判断和转换的宏。 <errno.h> 错误号头文件。包含系统中各种出错号。(Linus 从 minix 中引进的)。 <fcntl.h> 文件控制头文件。用于文件及其描述符的操作控制常数符号的定义。 <signal.h> 信号头文件。定义信号符号常量,信号结构以及信号操作函数原型。 <stdarg.h> 标准参数头文件。以宏的形式定义变量参数列表。主要说明了-个类型(va_list)和三个 宏(va_start, va_arg 和 va_end) ,用于 vsprintf、vprintf、vfprintf 函数。 <stddef.h> 标准定义头文件。定义了 NULL, offsetof(TYPE, MEMBER)。 <string.h> 字符串头文件。主要定义了一些有关字符串操作的嵌入函数。 <termios.h> 终端输入输出函数头文件。主要定义控制异步通信口的终端接口。 <time.h> 时间类型头文件。其中最主要定义了 tm 结构和一些有关时间的函数原形。 <unistd.h> Linux 标准头文件。定义了各种符号常数和类型,并申明了各种函数。如定义了 __LIBRARY__,则还包括系统调用号和内嵌汇编_syscall0()等。 <utime.h> 用户时间头文件。定义了访问和修改时间结构以及 utime()原型。 2.5.4.1 体系结构相关头文件子目录 include/asm 这些头文件主要定义了一些与 CPU 体系结构密切相关的数据结构、宏函数和变量。共 4 个文件。 <asm/io.h> io 头文件。以宏的嵌入汇编程序形式定义对 io 端口操作的函数。 <asm/memory.h> 内存拷贝头文件。含有 memcpy()嵌入式汇编宏函数。 <asm/segment.h> 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。 <asm/system.h> 系统头文件。定义了设置或修改描述符/中断门等的嵌入式汇编宏。 2.5.4.2 Linux 内核专用头文件子目录 include/linux <linux/config.h> 内核配置头文件。定义键盘语言和硬盘类型(HD_TYPE)可选项。 <linux/fdreg.h> 软驱头文件。含有软盘控制器参数的一些定义。 <linux/fs.h> 文件系统头文件。定义文件表结构(file,buffer_head,m_inode 等) 。 <linux/hdreg.h> 硬盘参数头文件。定义访问硬盘寄存器端口,状态码,分区表等信息。 <linux/head.h> head 头文件,定义了段描述符的简单结构,和几个选择符常量。 <linux/kernel.h> 内核头文件。含有一些内核常用函数的原形定义。 <linux/mm.h> 内存管理头文件。含有页面大小定义和一些页面释放函数原型。 <linux/sched.h> 调度程序头文件,定义了任务结构 task_struct、初始任务 0 的数据, 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。 <linux/sys.h> 系统调用头文件。含有 72 个系统调用 C 函数处理程序,以'sys_'开头。 <linux/tty.h> tty 头文件,定义了有关 tty_io,串行通信方面的参数、常数。 2.5.4.3 系统专用数据结构子目录 include/sys <sys/stat.h> 文件状态头文件。含有文件或文件系统状态结构 stat{}和常量。 <sys/times.h> 定义了进程中运行时间结构 tms 以及 times()函数原型。 <sys/types.h> 类型头文件。定义了基本的系统数据类型。 <sys/utsname.h> 系统名称结构头文件。 <sys/wait.h> 等待调用头文件。定义系统调用 wait()核 waitpid()及相关常数符号。 - 20 -