a heavily commented linux kernel source code...linux内核0.11完全注释 a heavily commented linux...

652

Upload: others

Post on 24-Oct-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

  • Linux内核完全注释内核版本0.11(0.95)

    赵 炯 著

    www.oldlinux.org

    修正版 V1.9.5

    [email protected]

  • Linux 内核 0.11 完全注释 A Heavily Commented Linux Kernel Source Code

    Linux Version 0.11

    修正版 1.9.5

    (Revision 1.9.5)

    赵炯

    Zhao Jiong

    [email protected]

    www.plinux.org

    www.oldlinux.org

    2004-05-21

  • 内容简介 本书对 Linux 早期操作系统内核(v0.11)全部代码文件进行了详细全面的注释和说明,旨在使读者能够在尽量短的时间

    内对 Linux 的工作机理获得全面而深刻的理解,为进一步学习和研究 Linux 系统打下坚实的基础。虽然所选择的版本较低,但该内核已能够正常编译运行,其中已经包括了 LINUX 工作原理的精髓,通过阅读其源代码能快速地完全理解内核的运作机制。书中首先以 Linux 源代码版本的变迁历史为主线,详细介绍了 Linux 系统的发展历史,着重说明了各个内核版本之间的重要区别和改进方面,给出了选择 0.11(0.95)版作为研究的对象的原因。另外介绍了内核源代码的组织结构及相互关系,同时还说明了编译和运行该版本内核的方法。然后本书依据内核源代码的组织结构对所有内核程序和文件进行了注释和详细

    说明。每章的安排基本上分为具体研究对象的概述、每个文件的功能介绍、代码内注释、代码中难点及相关资料介绍、与当

    前版本的主要区别等部分。最后一章内容总结性地介绍了继续研究 Linux 系统的方法和着手点。 版权说明

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

    © 2002,2003,2004 by Zhao Jiong © 2002,2003,2004 赵炯 版权所有.

  • “RTFSC – Read The F**king Source Code ☺!”

    –Linus Benedict Torvalds

  • 目录

    - I -

    目录

    序言 ................................................................................ 1

    本书的主要目标 ........................................................ 1 现有书籍不足之处 .................................................... 1 阅读早期内核的其它好处 ........................................ 2 阅读完整源代码的重要性和必要性 ........................ 2 如何选择要阅读的内核代码版本 ............................ 3 阅读本书需具备的基础知识 .................................... 3 使用早期版本是否过时? ........................................ 4 EXT2 文件系统与 MINIX 文件系统......................... 4

    第 1 章 概述 .................................................................. 5

    1.1 LINUX 的诞生和发展 ........................................... 5 1.2 内容综述 ........................................................... 10 1.3 本章小结 ........................................................... 14

    第 2 章 LINUX 内核体系结构 .................................. 15

    2.1 LINUX 内核模式 ................................................. 15 2.2 LINUX 内核系统体系结构 ................................. 16 2.3 中断机制 ........................................................... 18 2.4 系统定时 ........................................................... 19 2.5 LINUX 进程控制 ................................................. 20 2.6 LINUX 内核对内存的使用方法 ......................... 26 2.7 LINUX 系统中堆栈的使用方法 ......................... 29 2.8 LINUX 内核源代码的目录结构 ......................... 32 2.9 内核系统与用户程序的关系............................ 38 2.10 LINUX/MAKEFILE 文件...................................... 39 2.11 本章小结.......................................................... 47

    第 3 章 引导启动程序(BOOT)............................. 49

    3.1 概述 ................................................................... 49 3.2 总体功能 ........................................................... 49 3.3 BOOTSECT.S 程序 ................................................ 50 3.4 SETUP.S 程序 ....................................................... 58 3.5 HEAD.S 程序........................................................ 71 3.6 本章小结 ........................................................... 80

    第 4 章 初始化程序(INIT)......................................... 81

    4.1 概述 ................................................................... 81 4.2 MAIN.C 程序........................................................ 81 4.3 环境初始化工作 ............................................... 91 4.4 本章小结 ........................................................... 92

    第 5 章 内核代码(KERNEL)..................................... 95

    5.1 概述 ................................................................... 95 5.2 总体功能描述 ................................................... 95

    5.3 MAKEFILE 文件................................................... 98 5.4 ASM.S 程序 ........................................................ 100 5.5 TRAPS.C 程序..................................................... 106 5.6 SYSTEM_CALL.S 程序 ........................................ 113 5.7 MKTIME.C 程序 ................................................. 123 5.8 SCHED.C 程序 .................................................... 125 5.9 SIGNAL.C 程序................................................... 138 5.10 EXIT.C 程序 ..................................................... 147 5.11 FORK.C 程序 .................................................... 153 5.12 SYS.C 程序....................................................... 160 5.13 VSPRINTF.C 程序.............................................. 166 5.14 PRINTK.C 程序 ................................................. 174 5.15 PANIC.C 程序 ................................................... 175 5.16 本章小结........................................................ 176

    第 6 章 块设备驱动程序(BLOCK DRIVER)......... 177

    6.1 概述 ................................................................. 177 6.2 总体功能.......................................................... 177 6.3 MAKEFILE 文件................................................. 180 6.4 BLK.H 文件 ........................................................ 182 6.5 HD.C 程序.......................................................... 186 6.6 LL_RW_BLK.C 程序 ........................................... 202 6.7 RAMDISK.C 程序................................................ 207 6.8 FLOPPY.C 程序 ................................................... 212

    第 7 章 字符设备驱动程序(CHAR DRIVER) ....... 239

    7.1 概述 ................................................................. 239 7.2 总体功能描述.................................................. 239 7.3 MAKEFILE 文件................................................. 247 7.4 KEYBOARD.S 程序 ............................................. 249 7.5 CONSOLE.C 程序................................................ 267 7.6 SERIAL.C 程序 ................................................... 290 7.7 RS_IO.S 程序 ..................................................... 293 7.8 TTY_IO.C 程序 ................................................... 297 7.9 TTY_IOCTL.C 程序 ............................................. 308

    第 8 章 数学协处理器(MATH)................................ 317

    8.1 概述 ................................................................. 317 8.2 MAKEFILE 文件................................................. 317 8.3 MATH-EMULATION.C 程序.................................. 319

    第 9 章 文件系统(FS) ............................................... 321

    9.1 概述 ................................................................. 321 9.2 总体功能描述.................................................. 321 9.3 MAKEFILE 文件................................................. 327

  • 目录

    - II -

    9.4 BUFFER.C 程序 .................................................. 330 9.5 BITMAP.C 程序................................................... 345 9.6 INODE.C 程序 .................................................... 349 9.7 SUPER.C 程序 .................................................... 360 9.8 NAMEI.C 程序 .................................................... 369 9.9 FILE_TABLE.C 程序............................................ 390 9.10 BLOCK_DEV.C 程序 ......................................... 390 9.11 FILE_DEV.C 程序.............................................. 394 9.12 PIPE.C 程序...................................................... 397 9.13 CHAR_DEV.C 程序 ........................................... 400 9.14 READ_WRITE.C 程序........................................ 403 9.15 TRUNCATE.C 程序............................................ 407 9.16 OPEN.C 程序 .................................................... 409 9.17 EXEC.C 程序 .................................................... 415 9.18 STAT.C 程序 ..................................................... 430 9.19 FCNTL.C 程序 .................................................. 431 9.20 IOCTL.C 程序 ................................................... 434

    第 10 章 内存管理(MM).......................................... 437

    10.1 概述 ............................................................... 437 10.2 总体功能描述 ............................................... 437 10.3 MAKEFILE 文件............................................... 442 10.4 MEMORY.C 程序............................................... 443 10.5 PAGE.S 程序..................................................... 457

    第 11 章 头文件(INCLUDE) ................................... 459

    11.1 概述 ............................................................... 459 11.2 INCLUDE/目录下的文件 ................................. 459 11.3 A.OUT.H 文件 ................................................... 460 11.4 CONST.H 文件 .................................................. 470 11.5 CTYPE.H 文件 .................................................. 471 11.6 ERRNO.H 文件 ................................................. 472 11.7 FCNTL.H 文件 .................................................. 474 11.8 SIGNAL.H 文件................................................. 476 11.9 STDARG.H 文件 ................................................ 478 11.10 STDDEF.H 文件 .............................................. 479 11.11 STRING.H 文件 ............................................... 480 11.12 TERMIOS.H 文件 ............................................ 490 11.13 TIME.H 文件................................................... 497 11.14 UNISTD.H 文件............................................... 498 11.15 UTIME.H 文件 ................................................ 504 11.16 INCLUDE/ASM/目录下的文件 ....................... 505 11.17 IO.H 文件 ....................................................... 505 11.18 MEMORY.H 文件............................................. 506 11.19 SEGMENT.H 文件 ........................................... 507 11.20 SYSTEM.H 文件.............................................. 509 11.21 INCLUDE/LINUX/目录下的文件 .................... 512 11.22 CONFIG.H 文件............................................... 512 11.23 FDREG.H 头文件 ............................................ 514 11.24 FS.H 文件....................................................... 517 11.25 HDREG.H 文件................................................ 523 11.26 HEAD.H 文件 ................................................. 525

    11.27 KERNEL.H 文件 .............................................. 526 11.28 MM.H 文件..................................................... 527 11.29 SCHED.H 文件 ................................................ 527 11.30 SYS.H 文件..................................................... 535 11.31 TTY.H 文件..................................................... 537 11.32 INCLUDE/SYS/目录中的文件......................... 540 11.33 STAT.H 文件 ................................................... 540 11.34 TIMES.H 文件 ................................................. 541 11.35 TYPES.H 文件................................................. 542 11.36 UTSNAME.H 文件 ........................................... 543 11.37 WAIT.H 文件................................................... 544

    第 12 章 库文件(LIB)............................................... 547

    12.1 概述 ............................................................... 547 12.2 MAKEFILE 文件............................................... 548 12.3 _EXIT.C 程序 ................................................... 550 12.4 CLOSE.C 程序 .................................................. 550 12.5 CTYPE.C 程序 .................................................. 551 12.6 DUP.C 程序 ...................................................... 552 12.7 ERRNO.C 程序.................................................. 553 12.8 EXECVE.C 程序 ................................................ 553 12.9 MALLOC.C 程序 ............................................... 554 12.10 OPEN.C 程序 .................................................. 562 12.11 SETSID.C 程序................................................ 563 12.12 STRING.C 程序 ............................................... 564 12.13 WAIT.C 程序................................................... 564 12.14 WRITE.C 程序 ................................................ 565

    第 13 章 建造工具(TOOLS) .................................... 567

    13.1 概述 ............................................................... 567 13.2 BUILD.C 程序................................................... 567

    第 14 章 实验环境设置与使用方法 ........................ 574

    14.1 概述 ............................................................... 574 14.2 BOCHS 仿真系统 ............................................ 574 14.3 创建磁盘映象文件........................................ 578 14.4 访问磁盘映象文件中的信息........................ 581 14.5 制作根文件系统............................................ 584 14.6 在 LINUX 0.11 系统上编译 0.11 内核 ........... 590 14.7 在 REDHAT 9 系统下编译 LINUX 0.11 内核 .. 591 14.8 利用 BOCHS 调试内核 ................................... 594

    参考文献 .................................................................... 595

    附录 ............................................................................ 596

    附录 1 内核主要常数............................................ 596 附录 2 内核数据结构............................................ 599 附录 3 80X86 保护运行模式 ................................. 607 附录 4 ASCII 码表 ................................................. 617

    索引 ............................................................................ 618

  • 序言

    - 1 -

    序言 本书是一本有关 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 系统的人在使用那些书学习内核

  • 序言

    - 2 -

    原理时,内核的整体运作结构并不能清晰地在脑海中形成。这在本人多年的 Linux 内核学习过程中也深有体会。在 1991 年 10 月份,Linux 的创始人 Linus Toravlds 在开发出 Linux 0.03 版后写的一篇文章中也提到了同样的问题。在这篇题为“LINUX--a free unix-386 kernel”的文章中,他说:“开发 Linux 是为了那些操作系统爱好者和计算机科学系的学生使用、学习和娱乐。...自由软件基金会的 GNU Hurd 系统如果开发出来就已经显得太庞大而不适合学习和理解。”而现今的 Linux 系统要比 GNU 的 Hurd 系统更为庞大和复杂,因此同样也已经不适合作为操作系统初学者的入门学习起点。这也是写作本书的动机之一。 为了填补这个空缺,本书的主要目标是使用尽量少的篇幅或在有限的篇幅内,对完整的 Linux 内核源代码进行全面解剖,以期对操作系统的基本功能和实际实现方式获得全方位的理解。做到对 Linux 内核有一个完整而深刻的理解,对 Linux 操作系统的基本工作原理真正理解和入门。

    阅读早期内核的其它好处

    目前,已经出现不少基于 Linux 早期内核而开发的专门用于嵌入式系统的内核版本,如 DJJ 的 x86操作系统、Uclinux 等(在 www.linux.org 上有专门目录),世界上也有许多人认识到通过早期 Linux 内核源代码学习的好处,目前国内也已经有人正在组织人力注释出版类似本文的书籍。因此,通过阅读 Linux早期内核版本的源代码,的确是学习 Linux 系统的一种行之有效的途径,并且对研究和应用 Linux 嵌入式系统也有很大的帮助。 在对早期内核源代码的注释过程中,作者发现,早期内核源代码几乎就是目前所使用的较新内核的一个精简版本。其中已经包括了目前新版本中几乎所有的基本功能原理的内容。正如《系统软件:系统

    编程导论》一书的作者 Leland L. Beck 在介绍系统程序以及操作系统设计时,引入了一种极其简化的简单指令计算机(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,其实,前者通常不足一页代码,而后者往往要占到整个系统三分之一的代码总量。”内核中大量的重要细节均未提到。因此并不能让读者理解一个真正的操作系

    统实现的奥妙所在。只有在详细阅读过完整的内核源代码之后,才会对系统有一种豁然开朗的感觉,对

  • 序言

    - 3 -

    整个系统的运作过程有深刻的理解。以后再选择最新的或较新内核源代码进行学习时,也不会碰到大问

    题,基本上都能顺利地理解新代码的内容。

    如何选择要阅读的内核代码版本

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

    1000

    10000

    100000

    1000000

    10000000

    V0.01

    V0.11

    V0.12

    V0.95

    V0.96a

    V0.97

    V1.0

    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 操作系统设计》一书的基本原理

  • 序言

    - 4 -

    开发的,源代码中许多变量或函数的名称都来自该书,因此在阅读本书时,若能适当参考该书,更易于

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

    使用早期版本是否过时?

    表面看来本书对 Linux 早期内核版本注释的内容犹如 Linux 操作系统刚公布时 Tanenbaum 就认为其已经过时的(Linux is obsolete)想法一样,但通过学习本书内容,你就会发现,利用本书学习 Linux 内核,由于内核源代码量短小而精干,因此会有极高的学习效率,能够做到事半功倍,快速入门。并且对

    继续进一步选择新内核部分源代码的学习打下坚实的基础。在学习完本书之后,你将对系统的运作原理

    有一个非常完整而实际的概念,这种完整概念能使人很容易地进一步选择和学习新内核源代码中的任何

    部分,而不需要再去啃读代码量巨大的新内核中完整的源代码。

    Ext2 文件系统与 MINIX 文件系统

    目前 Linux 系统上所使用的 Ext2(或最新的 Ext3)文件系统,是在内核 1.x 之后开发的文件系统,其功能详尽并且其性能也非常完整和稳固,是目前 Linux 操作系统上默认的标准文件系统。但是,作为对 Linux 操作系统完整工作原理入门学习所使用的部分,原则上是越精简越好。为了达到对一个操作系统有完整的理解,并且能不被其中各子系统中的复杂性和过多的细节喧宾夺主,在选择学习剖析用的内

    核版本时,只要系统的部分代码内容能说明实际工作原理,就越简单越好。 Linux 内核 0.11 版上当时仅包含最为简单的 MINIX 1.0 文件系统,对于理解一个操作系统中文件系统的实际组成和工作原理已经足够。这也是选择 Linux 早期内核版本进行学习的主要原因之一。 在完整阅读完本书之后,相信您定会发出这样的感叹:“对于 Linux 内核系统,我现在终于入门了!”。此时,您应该有十分的把握去进一步学习最新 Linux 内核中各部分的工作原理和过程了。 同济大学 赵炯 博士 2002.12

  • 1.1 Linux 的诞生和发展

    - 5 -

    第1章 概述

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

    1.1 Linux 的诞生和发展

    Linux 操作系统是 UNIX 操作系统的一种克隆系统。它诞生于 1991 年的 10 月 5 日(这是第一次正式向外公布的时间)。此后借助于 Internet 网络,经过全世界各地计算机爱好者的共同努力,现已成为当今世界上使用最多的一种 UNIX 类操作系统,并且使用人数还在迅猛增长。 Linux 操作系统的诞生、发展和成长过程依赖于以下五个重要支柱:UNIX 操作系统、MINIX 操作系统、GNU 计划、POSIX 标准和 Internet 网络。下面根据这五个基本线索来追寻一下 Linux 的开发历程、它的酝酿过程以及最初的发展经历。首先分别介绍其中的四个基本要素,然后根据 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 年编制的,主要用于学生学习操作系统原理。到 1991 年时版本是 1.5。目前主要有两个版本在使用:1.5 版和 2.0 版。当时该操作系统在大学使用是免费的,但其它用途则不是。当然目

  • 1.1 Linux 的诞生和发展

    - 6 -

    前 MINIX 系统已经是免费的,可以从许多 FTP 上下载。 对于 Linux 系统,他后来曾表示对其开发者 Linus 的称赞。但他认为 Linux 的发展很大原因是由于他为了保持 MINIX 的小型化,能让学生在一个学期内就能学完,因而没有接纳全世界许多人对 MINIX的扩展要求。因此在这样的前提下激发了 Linus 编写 Linux 系统。当然 Linus 也正好抓住了这个好时机。 作为一个操作系统,MINIX 并不是优秀者,但它同时提供了用 C 语言和汇编语言编写的系统源代码。这是第一次使得有抱负的程序员或 hacker 能够阅读操作系统的源代码。在当时,这种源代码是软件商们一直小心守护着的秘密。

    1.1.3 GNU 计划 GNU 计划和自由软件基金会 FSF(the Free Software Foundation)是由 Richard M. Stallman 于 1984 年一手创办的。旨在开发一个类似 UNIX 并且是自由软件的完整操作系统:GNU 系统(GNU 是"GNU's Not Unix"的递归缩写,它的发音为"guh-NEW")。各种使用 Linux 作为核心的 GNU 操作系统正在被广泛的使用。虽然这些系统通常被称作"Linux",但是 Stallman 认为,严格地说,它们应该被称为 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 的 System 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 的工作就开始展开了。刚开始有十个批准的计划在进行,有近 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 标准的兼容做好了准备工作。在 Linux 0.01 版内核的/include/unistd.h 文件中就已经定义了几个有关 POSXI 标准要求的符号常数,而且 Linus 在注释中已写道:“OK,这也许是个玩笑,但我正在着手研究它呢”。

  • 1.1 Linux 的诞生和发展

    - 7 -

    1991 年 7 月 3 日在 comp.os.minix 上发布的 post 上就已经提到了正在搜集 POSIX 的资料。其中透露了他正在着手一个操作系统的开发,并且在开发之初已经想到要实现与 POSIX 相兼容的问题了。

    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 岁的芬兰年轻人喜欢鼓捣他的计算机,测试计算机的性能和限制。但当时他所缺乏的就是一个专业级的操作系统。 在同一年间,GNU 计划已经开发出了许多工具软件。其中最受期盼的 GNU C 编译器已经出现,但还没有开发出免费的 GNU 操作系统。即使是教学使用的 MINIX 操作系统也开始有了版权,需要购买才能得到源代码。虽然 GNU 的操作系统 HURD 一直在开发之中,但在当时看来不能在几年内完成。 为了能更好地学习计算机知识(或许也只是为了兴趣☺),Linus 使用圣诞节的压岁钱和贷款购买了一台 386 兼容电脑,并从美国邮购了一套 MINIX 系统软件。就在等待 MINIX 软件期间,Linus 认真学习了有关 Intel 80386 的硬件知识。为了能通过 Modem 拨号连接到学校的主机上,他使用汇编语言并利用 80386 CPU 的多任务特性编制出一个终端仿真程序。此后为了将自己一台老式电脑上的软件复制到新电脑上,他还为软盘驱动器、键盘等硬件设备编制出相应的驱动程序。 通过编程实践,并在学习过程中认识到 MINIX 系统的诸多限制(MINIX 虽然很好,但只是一个用于教学目的简单操作系统,而不是一个强有力的实用操作系统),而且通过上述实践 Linus 已经有了一些类似于操作系统硬件设备驱动程序的代码,于是他开始有了编制一个新操作系统的想法。此时 GNU 计划已经开发出许多工具软件,其中最受期盼的GNU C编译器已经出现。虽然GNU的免费操作系统HURD正在开发中。但 Linus 已经等不急了。 从 1991 年 4 月份起,他通过修改终端仿真程序和硬件驱动程序,开始编制起自己的操作系统来。刚开始,他的目的很简单,只是为了学习 Intel 386 体系结构保护模式运行方式下的编程技术。但后来 Linux的发展却完全改变了初衷。根据 Linus 在 comp.os.minix 新闻组上发布的消息,我们可以知道他逐步从学习 MINIX 系统阶段发展到开发自己的 Linux 系统的过程。 Linus 第 1 次向 comp.os.minix 投递消息是在 1991 年 3 月 29 日。所发帖子的题目是“gcc on minix-386 doesn't optimize”,是有关 gcc 编译器在 MINIX-386 系统上运行优化的问题(MINIX-386 是一个由 Bruce Evans 改进的利用 Intel 386 特性的 32 位 MINIX 系统)。由此可知,Linus 在 1991 年初期就已经开始深入研究了 MINIX 系统,并在这段时间有了改进 MINIX 操作系统的思想。在进一步学习 MINIX 系统之后,这个想法逐步演变成想重新设计一个基于 Intel 80386 体系结构的新操作系统的构思。 他在回答有人提出 MINIX 上的一个问题时,所说的第一句话就是“阅读源代码”(“RTFSC (Read the F**ing Source Code :-)”)。他认为答案就在源程序中。这也说明了对于学习系统软件来说,我们不光需要懂得系统的工作基本原理,还需要结合实际系统,学习实际系统的实现方法。因为理论毕竟是理论,其

    中省略了许多枝节,而这些枝节问题虽然没有太多的理论含量,但却是一个系统必要的组成部分,就象

    麻雀身上的一根羽毛。

  • 1.1 Linux 的诞生和发展

    - 8 -

    从 1991 年 4 月份开始,Linus 几乎花费了全部时间研究 MINIX-386 系统(Hacking 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 系统的开发,并且已经想到要实现与 POSIX 兼容的问题了。 在 Linus 另一个发布的消息中(1991 年 8 月 25 日 comp.os.minix),他向所有 MINIX 用户询问“What would you like to see in minix?”(“你最想在 MINIX 系统中见到什么?”),在该消息中他首次透露出正在开发一个(免费的)386(486)操作系统,并且说只是兴趣而已,代码不会很大,也不会象 GNU 的那样专业。希望大家反馈一些对于 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 操作系统版本的变迁 Linux 操作系统从诞生到 1.0 版正式出现,共发布了表 1–1 中所示的一些主要版本。

    表 1–1内核的主要版本

    版本号 发布日期 说明

    0.00 (1991.2-4) 两个进程,分别在屏幕上显示’AAA’和’BBB’。

    0.01 (1991.8) 第一个正式向外公布的 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) 加入虚拟文件系统支持,增加了登录功能。改善了软盘驱动程序

    和文件系统的性能。改变了硬盘编号方式。支持 CDROM。

    0.96 (1992.5.12) 开始加入网络支持。改善了串行驱动、高速缓冲、内存管理的性

    能,支持动态链接库,并能运行 X-Windows 程序。

    0.97 (1992.8.1) 增加了对新的 SCSI 驱动程序的支持。

    0.98 (1992.9.29) 改善了对 TCP/IP(0.8.1)网络的支持,纠正了 extfs 的错误。

    0.99 (1992.12.13) 重新设计进程对内存的使用分配,每个进程有 4G 线性空间。

    1.0 (1994.3.14) 第一个正式版。

  • 1.1 Linux 的诞生和发展

    - 9 -

    将 Linux 系统 0.13 版内核直接改称 0.95 版,Linus 的意思是让大家不要觉得离 1.0 版还很遥远。同时,从 0.95 版开始,对内核的许多改进之处(补丁程序的提供)均以其他人为主了,而 Linus 的主要任务开始变成对内核的维护和决定是否采用某个补丁程序。到现在为止,最新的内核版本是 2003 年 12 月 18日公布的 2.6.2 版。其中包括大约 15000 个文件,而且使用 gz 压缩后源代码软件包也有 40MB 左右!到现在为止,最新版见表 1–2 所示。

    表 1–2新内核源代码字节数

    内核版本号 发布日期 源代码大小(经 gz 压缩后)

    2.4.22 2004.2.4 35MB

    2.6.5 2004.4.4 41MB

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

    字,而且以后为这事我总能说服别人,就象我现在做的这样。”

    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 刚开始发布时起,他就一直在为 Linux 做出贡献。他也是最早向 Linux 内核添加程序的人(Linux 内核 0.10 版中的虚拟盘驱动程序 ramdisk.c 和内核内存分配程序 kmalloc.c)。直到目前为止他仍然从事着与 Linux 有关的工作。在北美洲地区他最早设立了 Linux 的ftp 站点(tsx-11.mit.edu),而且该站点至今仍然为广大 Linux 用户提供服务。他对 Linux 作出的最大贡献之一是提出并实现了 ext2 文件系统。该文件系统现已成为 Linux 世界中事实上的文件系统标准。最近他又推出了 ext3 文件系统。该系统大大提高了文件系统的稳定性和访问效率。作为对他的推崇,第 97 期(2002 年 5 月)的 Linux Journal 期刊将他作为了封面人物,并对他进行了采访。目前,他为 IBM Linux

    1 Linus Torvalds《Just for fun》第 84-88 页。

  • 1.2 内容综述

    - 10 -

    技术中心工作,并从事着有关 Linux 标准库 LSB(Linux Standard Base)等方面的工作。 Linux 社区中另一位著名人物是 Alan Cox。他原工作于英国威尔士斯旺西大学(Swansea University College)。刚开始他特别喜欢玩电脑游戏,尤其是 MUD(Multi-User Dungeon or Dimension,多用户网络游戏)。在 90 年代早期 games.mud 新闻组的 posts 中你可以找到他发表的大量帖子。他甚至为此还写了一篇 MUD 的发展史(rec.games.mud 新闻组,1992 年 3 月 9 日,A history of MUD)。 由于 MUD 游戏与网络密切相关,慢慢地他开始对计算机网络着迷起来。为了玩游戏并提高电脑运行游戏的速度以及网络传输速度,他需要选择一个最为满意的操作平台。于是他开始接触各种类型的操

    作系统。由于没钱,即使是 MINIX 系统他也买不起。当 Linux 0.11 和 386BSD 发布时,他考虑良久总算购置了一台 386SX 电脑。由于 386BSD 需要数学协处理器支持,而采用 Intel 386SX CPU 的电脑是不带数学协处理器的,所以他安装了 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版)。 《内核黑客手册》(The Linux Kernel Hackers' Guide)一书的作者 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

  • 1.2 内容综述

    - 11 -

    日发布的。在发布时包括以下文件: bootimage.Z - 具有美国键盘代码的压缩启动映像文件;

    rootimage.Z - 以 1200kB 压缩的根文件系统映像文件;

    linux-0.11.tar.Z - 内核源代码文件。大小为 94KB,展开后也仅有 325KB;

    as86.tar.Z - Bruce Evans'二进制执行文件。是 16 位的汇编程序和装入程序;

    INSTALL-0.11 - 更新过的安装信息文件。

    目前除了原来的 rootimage.Z 文件,其它四个文件均能找到。不过作者已经利用 Internet 上的资源为Linux 0.11 重新制作出了一个完全可以使用的 rootimage-0.11 根文件系统。并重新为其编译出能在 0.11环境下使用的 gcc 1.40 编译器,配置出可用的实验开发环境。目前,这些文件均可以从 oldlinux.org 网站上下载。 本文主要详细分析 linux-0.11 内核中的所有源代码程序,对每个源程序文件都进行了详细注释,包括对 Makefile 文件的注释。分析过程主要是按照计算机启动过程进行的。因此分析的连贯性到初始化结束内核开始调用 shell 程序为止。其余的各个程序均针对其自身进行分析,没有连贯性,因此可以根据自己的需要进行阅读。但在分析时还是提供了一些应用实例。 所有的程序在分析过程中如果遇到作者认为是较难理解的语句时,将给出相关知识的详细介绍。比如,在阅读代码头一次遇到 C 语言内嵌汇编码时,将对 GNU C 语言的内嵌汇编语言进行较为详细的介绍;在遇到对中断控制器进行输入/输出操作时,将对 Intel 中断控制器(8259A)芯片给出详细的说明,并列出使用的命令和方法。这样做有助于加深对代码的理解,又能更好的了解所用硬件的使用方法,作

    者认为这种解读方法要比单独列出一章内容来总体介绍硬件或其它知识要效率高得多。 拿 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.2 内容综述

    - 12 -

    列表 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 对物理内存的使用分配方式、内核的几种堆栈及其使用方式和虚拟线性地址的使用分配。最后开始注释内核程序包中 Linux/目录下的所看到的第一个文件,也即内核代码的总体Makefile 文件的内容。该文件是所有内核源程序的编译管理配置文件,供编译管理工具软件 make 使用。 第 3 章将详细注释 boot/目录下的三个汇编程序,其中包括磁盘引导程序 bootsect.s、获取 BIOS 中参数的 setup.s 汇编程序和 32 位运行启动代码程序 head.s。这三个汇编程序完成了把内核从块设备上引导加载到内存的工作,并对系统配置参数进行探测,完成了进入 32 位保护模式运行之前的所有工作。为内核系统执行进一步的初始化工作做好了准备。 第 4 章主要介绍 init/目录中内核系统的初始化程序 main.c。它是内核完成所有初始化工作并进入正常运行的关键地方。在完成了系统所有的初始化工作后,创建了用于 shell 的进程。在介绍该程序时将需要查看其所调用的其它程序,因此对后续章节的阅读可以按照这里调用的顺序进行。由于内存管理程序

    的函数在内核中被广泛使用,因此该章内容应该最先选读。当你能真正看懂直到 main.c 程序为止的所有程序时,你应该已经对 Linux 内核有了一定的了解,可以说已经有一半入门了☺,但你还需要对文件系统、系统调用、各种驱动程序等进行更深一步的阅读。 第 5 章主要介绍 kenel/目录中的所有程序。其中最重要的部分是进程调度函数 schedule()、sleep_on()函数和有关系统调用的程序。此时你应该已经对其中的一些重要程序有所了解。 第 6 章对 kernel/dev_blk/目录中的块设备程序进行了注释说明。该章主要含有硬盘、软盘等块设备的驱动程序,主要用来与文件系统和高速缓冲区打交道,含有较多与硬件相关的内容。因此,在阅读这

    章内容时需参考一些硬件资料。最好能首先浏览一下文件系统的章节。 第 7 章对 kernel/dev_chr/目录中的字符设备驱动程序进行注释说明。这一章中主要涉及串行线路驱动程序、键盘驱动程序和显示器驱动程序。这些驱动程序构成了 0.11 内核支持的串行终端和控制台终端设备。因此本章也含有较多与硬件有关的内容。在阅读时需要参考一下相关硬件的书籍。 第 8 章介绍 kernel/math/目录中的数学协处理器的仿真程序。由于本书所注释的内核版本,还没有真正开始支持协处理器,因此本章的内容较少,也比较简单。只需有一般性的了解即可。 第 9 章介绍内核源代码 fs/目录中的文件系统程序,在看这章内容时建议你能够暂停一下而去阅读Andrew S. Tanenbaum 的《操作系统设计与实现》一书中有关 MINIX 文件系统的章节,因为最初的 Linux系统是只支持 MINIX 一种文件系统,Linux 0.11 版也不例外。

  • 1.2 内容综述

    - 13 -

    第 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)文件。 第 14 章介绍了学习内核源代码时的实验环境以及实验方法。主要介绍了在 Bochs 仿真系统下使用和编译 Linux 内核的方法以及磁盘镜象文件的制作方法。还说明了如何修改 Linux 0.11 源代码的语法使其能在 RedHat 9 系统下顺利编译出正确的内核来。 最后是附录和索引。附录中给出了 Linux 内核中的一些常数定义和基本数据结构定义,以及保护模式运行机制的简明描述。 为了便于查阅,在本书的附录中还单独列出了内核中要用到的有关 PC 机硬件方面的信息。在参考文献中,我们仅给出了在阅读源代码时可以参考的书籍、文章等信息,并没有包罗万象地给出一大堆的

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

    全可以从 Tanenbaum 的书中获得。 在对每个程序进行解说时,我们首先简单说明程序的主要用途和目的、输入输出参数以及与其它程序的关系,然后列出程序的完整代码并在其中对代码进行详细注释,注释时对原程序代码或文字不作任

    何方面的改动或删除,因为 C 语言是一种英语类语言,程序中原有的少量英文注释对常数符号、变量名等也提供了不少有用的信息。在代码之后是对程序更为深入的解剖,并对代码中出现的一些语言或硬件

    方面的相关知识进行说明。如果在看完这些信息后回头再浏览一遍程序,你会有更深一层的体会。 对于阅读本书所需要的一些基本概念知识的介绍都散布在各个章节相应的地方,这样做主要是为了能够方便的找到,而且在结合源代码阅读时,对一些基本概念能有更深的理解。 最后要说明的是当你已经完全理解了本文所解说的一切时,并不代表你已经成为一个 Linux 行家了,

  • 1.3 本章小结

    - 14 -

    你只是刚刚踏上 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 成长和壮大的必要环境。最后本章概述了书中的基本内容。

  • 2.1 Linux 内核模式

    - 15 -

    第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 Linux 内核系统体系结构

    - 16 -

    执行系统调用的服务层和支持系统调用的底层函数。见图 2-2 所示。

    图 2-2单内核模式的简单结构模型

    2.2 Linux 内核系统体系结构

    Linux 内核主要由 5 个模块构成,它们分别是:进程调度模块、内存管理模块、文件系统模块、进程间通信模块和网络接口模块。 进程调度模块用来负责控制进程对 CPU 资源的使用。所采取的调度策略是各进程能够公平合理地访问 CPU,同时保证内核能及时地执行硬件操作。内存管理模块用于确保所有进程能够安全地共享机器主内存区,同时,内存管理模块还支持虚拟内存管理方式,使得 Linux 支持进程使用比实际内存空间更多的内存容量。并可以利用文件系统把暂时不用的内存数据块会被交换到外部存储设备上去,当需要时再

    交换回来。文件系统模块用于支持对外部设备的驱动和存储。虚拟文件系统模块通过向所有的外部存储

    设备提供一个通用的文件接口,隐藏了各种硬件设备的不同细节。从而提供并支持与其它操作系统兼容

    的多种文件系统格式。进程间通信模块子系统用于支持多种进程间的信息交换方式。网络接口模块提供

    对多种网络通信标准的访问并支持许多网络硬件。 这几个模块之间的依赖关系见图 2-3 所示。其中的连线代表它们之间的依赖关系,虚线和虚框部分表示 Linux 0.11 中还未实现的部分(从 Linux 0.95 版才开始逐步实现虚拟文件系统,而网络接口的支持到 0.96 版才有)。

    主程序

    系统服务

    支持函数

  • 2.2 Linux 内核系统体系结构

    - 17 -

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

    由图可以看出,所有的模块都与进程调度模块存在依赖关系。因为它们都需要依靠进程调度程序来挂起(暂停)或重新运行它们的进程。通常,一个模块会在等待硬件操作期间被挂起,而在操作完成后

    才可继续运行。例如,当一个进程试图将一数据块写到软盘上去时,软盘驱动程序就可能在启动软盘旋

    转期间将该进程置为挂起等待状态,而在软盘进入到正常转速后再使得该进程能继续运行。另外 3 个模块也是由于类似的原因而与进程调度模块存在依赖关系。 其它几个依赖关系有些不太明显,但同样也很重要。进程调度子系统需要使用内存管理器来调整一特定进程所使用的物理内存空间。进程间通信子系统则需要依靠内存管理器来支持共享内存通信机制。

    这种通信机制允许两个进程访问内存的同一个区域以进行进程间信息的交换。虚拟文件系统也会使用网

    络接口来支持网络文件系统(NFS),同样也能使用内存管理子系统来提供内存虚拟盘(ramdisk)设备。而内存管理子系统也会使用文件系统来支持内存数据块的交换操作。 若从单内核模式结构模型出发,我们还可以根据 Linux 0.11 内核源代码的结构将内核主要模块绘制成图 2-4 所示的框图结构。

    进程调度 进程间通信

    内存管理

    虚拟文件系统

    文件系统

    网络接口

  • 2.3 中断机制

    - 18 -

    图 2-4内核结构框图

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

    2.3 中断机制

    在使用 80X86 组成的 PC 机中,采用了两片 8259A 可编程中断控制芯片。每片可以管理 8 个中断源。通过多片的级联方式,能构成最多管理 64 个中断向量的系统。在 PC/AT 系列兼容机中,使用了两片 8259A芯片,共可管理 15 级中断向量。其级连示意图见图 2-5 所示。其中从芯片的 INT 引脚连接到主芯片的IR2 引脚上。主 8259A 芯片的端口基地址是 0x20,从芯片是 0xA0。

    用户级

    内核级

    内核级

    硬件级

    系统调用接口

    文件子系统

    高速缓冲

    字符设备 块设备

    设备驱动程序

    进程控制

    子系统

    硬 件 控 制

    硬 件

    函 数 库

    用户程序

    内存管理

    进程间通信

    调度程序

  • 2.4 系统定时

    - 19 -

    图 2-5 PC/AT微机级连式 8259控制系统

    在总线控制器控制下,8259A 芯片可以处于编程状态和操作状态。编程状态是 CPU 使用 IN 或 OUT指令对 8259A 芯片进行初始化编程的状态。一旦完成了初始化编程,芯片即进入操作状态,此时芯片即可随时响应外部设备提出的中断请求(IRQ0 – IRQ15)。通过中断判优选择,芯片将选中当前最高优先级的中断请求作为中断服务对象,并通过 CPU 引脚 INT 通知 CPU 外中断请求的到来,CPU 响应后,芯片从数据总线 D7-D0 将编程设定的当前服务对象的中断号送出,CPU 由此获取对应的中断向量值,并执行中断服务程序。 对于 Linux 内核来说,中断信号通常分为两类:硬件中断和软件中断(异常)。每个中断是由 0-255之间的一个数字来标识。对于中断 int0--int31(0x00--0x1f),每个中断的功能由 Intel 公司固定设定或保留用, 属于软件中断,但 Intel 公司称之为异常。因为这些中断是在 CPU 执行指令时探测到异常情况而引起的。通常还可分为故障(Fault)和陷阱(traps)两类。中断 int32--int255 (0x20--0xff)可以由用户自己设定。在 Linux 系统中,则将 int32--int47(0x20--0x2f)对应于 8259A 中断控制芯片发出的硬件中断请求信号IRQ0-IRQ15,并把程序编程发出的系统调用(system_call)中断设置为 int128(0x80)。

    2.4 系统定时

    在 Linux 0.11 内核中,PC 机的可编程定时芯片 Intel 8253 被设置成每隔 10 毫秒就发出一个时钟中断(IRQ0)信号。这个时间节拍就是系统运行的脉搏,我们称之为 1 个系统滴答。因此每经过 1 个滴答就会调用一次时钟中断处理程序(timer_interrupt)。该处理程序主要用来通过 jiffies 变量来累计自系统启动以来经过的时钟滴答数。每当发生一次时钟中断该值就增 1。然后从被中断程序的段选择符中取得当前特权级 CPL 作为参数调用 do_timer()函数。 do_timer()函数则根据特权级对当前进程运行时间作累计。如果 CPL=0,则表示进程是运行在内核态时被中断,因此把进程的内核运行时间统计值 stime 增 1,否则把进程用户态运行时间统计值增 1。如果程序添加过定时器,则对定时器链表进行处理。若某个定时器时间到(递减后等于 0),则调用该定时器的处理函数。然后对当前进程运行时间进行处理,把当前进程运行时间片减 1。如果此时当前进程时间

    IR0IR1 INTIR2 IR3 8259A IR4 主片 IR5 IR6 IR7 A0 CS CAS2-0

    时钟 IRQ0 键盘 IRQ1 接联int IRQ2 串行口 2 IRQ3 串行口 1 IRQ4 并行口 2 IRQ5 软盘 IRQ6 并行口 1 IRQ7 地址 0x20-0x3f

    IR0 CAS2-0IR1 INTIR2 IR3 8259A IR4 从片 IR5 IR6 IR7 A0 CS

    实时钟 IRQ8 INT0AH IRQ9 保留 IRQ10 保留 IRQ11 PS2 鼠标 IRQ12 协处理器 IRQ13 硬盘 IRQ14 保留 IRQ15 地址 0xA0-0xbf

    INTR CPU

    数据D7-D0

  • 2.5 Linux 进程控制

    - 20 -

    片并还大于 0,表示其时间片还没有用完,于是就退出 do_timer()继续运行当前进程。如果此时进程时间片已经递减为 0,表示该进程已经用完了此次使用 CPU 的时间片,于是程序就会根据被中断程序的级别来确定进一步处理的方法。若被中断的当前进程是工作在用户态的(特权级别大于 0),则 do_timer()就会调用调度程序 schedule()切换到其它进程去运行。如果被中断的当前进程工作在内核态,也即在内核程序中运行时被中断,则 do_timer()会立刻退出。因此这样的处理方式决定了 Linux 系统在内核态运行时不会被调度程序切换。内核态程序是不可抢占的,但当处于用户态程序中运行时则是可以被抢占的。

    2.5 Linux 进程控制

    程序是一个可执行的文件,而进程(process)是一个执行中的程序实例。利用分时技术,在 Linux操作系统上同时可以运行多个进程。分时技术的基本原理是把 CPU 的运行时间划分成一个个规定长度的时间片,让每个进程在一个时间片内运行。当进程的时间片用完时系统就利用调度程序切换到另一个进

    程去运行。因此实际上对于具有单个 CPU 的机器来说某一时刻只能运行一个进程。但由于每个进程运行的时间片很短(例如 15 个系统滴答=150 毫秒),所以表面看来好象所有进程在同时运行着。 对于 Linux 0.11 内核来讲,系统最多可有 64 个进程同时存在。除了第一个进程是“手工”建立以外,其余的都是进程使用系统调用 fork 创建的新进程,被创建的进程称为子进程(child process),创建者,则称为父进程(parent process)。内核程序使用进程标识号(process ID,pid)来标识每个进程。进程由可执行的指令代码、数据和堆栈区组成。进程中的代码和数据部分分别对应一个执行文件中的代码段、

    数据段。每个进程只能执行自己的代码和访问自己的数据及堆栈区。进程之间相互之间的通信需要通过

    系统调用来进行。对于只有一个 CPU 的系统,在某一时刻只能有一个进程正在运行。内核通过调度程序分时调度各个进程运行。 Linux 系统中,一个进程可以在内核态(kernel mode)或用户态(user mode)下执行,因此,Linux内核堆栈和用户堆栈是分开的。用户堆栈用于进程在用户态下临时保存调用函数的参数、局部变量等数

    据。内核堆栈则含有内核程序执行函数调用时的信息。

    2.5.1 任务数据结构 内核程序通过进程表对进程进行管理,每个进程在进程表中占有一项。在 Linux 系统中,进程表项是一个 task_struct 任务结构指针。任务数据结构定义在头文件 include/linux/sched.h 中。有些书上称其为进程控制块 PCB(Process Control Block)或进程描述符 PD(Processor Descriptor)。其中保存着用于控制和管理进程的所有信息。主要包括进程当前运行的状态信息、信号、进程号、父进程号、运行时间累

    计值、正在使用的文件和本任务的局部描述符以及任务状态段信息。该结构每个字段的具体含义如下所

    示。 struct task_struct {

    long state //任务的运行状态(-1 不可运行,0 可运行(就绪),>0 已停止)。

    long counter // 任务运行时间计数(递减)(滴答数),运行时间片。

    long priority // 运行优先数。任务开始运行时 counter=priority,越大运行越长。

    long signal // 信号。是位图,每个比特位代表一种信号,信号值=位偏移值+1。

    struct sigaction sigaction[32] // 信号执行属性结构,对应信号将要执行的操作和标志信息。

    long blocked // 进程信号屏蔽码(对应信号位图)。

    int exit_code // 任务执行停止的退出码,其父进程会取。

    unsigned long start_code // 代码段地址。

    unsigned long end_code // 代码长度(字节数)。

    unsigned long end_data // 代码长度 + 数据长度(字节数)。

    unsigned long brk // 总长度(字节数)。

    unsigned long start_stack // 堆栈段地址。

  • 2.5 Linux 进程控制

    - 21 -

    long pid // 进程标识号(进程号)。

    long father // 父进程号。

    long pgrp // 父进程组号。

    long session // 会话号。

    long leader // 会话首领。

    unsigned short uid // 用户标识号(用户 id)。

    unsigned short euid // 有效用户 id。

    unsigned short suid // 保存的用户 id。

    unsigned short gid // 组标识号(组 id)。

    unsigned short egid // 有效组 id。

    unsigned short sgid // 保存的组 id。

    long alarm // 报警定时值(滴答数)。

    long utime // 用户态运行时间(滴答数)。

    long stime // 系统态运行时间(滴答数)。

    long cutime // 子进程用户态运行时间。

    long cstime // 子进程系统态运行时间。

    long start_time // 进程开始运行时刻。

    unsigned short used_math // 标志:是否使用了协处理器。

    int tty // 进程使用 tty 的子设备号。-1 表示没有使用。

    unsigned short umask // 文件创建属性屏蔽位。

    struct m_inode * pwd // 当前工作目录 i 节点结构。

    struct m_inode * root // 根目录 i节点结构。

    struct m_inode * executable // 执行文件 i 节点结构。

    unsigned long close_on_exec // 执行时关闭文件句柄位图标志。(参见 include/fcntl.h)

    struct file * filp[NR_OPEN] // 文件结构指针表,最多 32 项。表项号即是文件描述符的值。

    struct desc_struct ldt[3] // 任务局部描述符表。0-空,1-代码段 cs,2-数据和堆栈段 ds&ss。

    struct tss_struct tss // 进程的任务状态段信息结构。

    };

    当一个进程在执行时,CPU 的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文。当内核需要切换(switch)至另一个进程时,它就需要保存当前进程的所有状态,也即保存当前进程的上下文,以便在再次执行该进程时,能够恢复到切换时的状态执行下去。在 Linux 中,当前进程上下文均保存在进程的任务数据结构中。在发生中断时,内核就在被中断进程的上下文中,在内核态下

    执行中断服务例程。但同时会保留所有需要用到的资源,以便中断服务结束时能恢复被中断进程的执行。

    2.5.2 进程运行状态 一个进程在其生存期内,可处于一组不同的状态下,称为进程状态。见图 2-6 所示。进程状态保存在进程任务结构的 state 字段中。当进程正在等待系统中的资源而处于等待状态时,则称其处于睡眠等待状态。在 Linux 系统中,睡眠等待状态被分为可中断的和不可中断的等待状态。

  • 2.5 Linux 进程控制

    - 22 -

    图 2-6进程状态及转换关系

    ◆运行状态(TASK_RUNNING)

    当进程正在被 CPU 执行,或已经准备就绪随时可由调度程序执行,则称该进程为处于运行状态(running)。进程可以在内核态运行,也可以在用户态运行。当系统资源已经可用时,进程就被唤醒而进入准备运行状态,该状态称为就绪态。这些状态(图中中间一列)在内核中表示方法相同,

    都被成为处于 TASK_RUNNING 状态。 ◆可中断睡眠状态(TASK_INTERRUPTIBLE)

    当进程处于可中断等待状态时,系统不会调度该进行执行。当系统产生一个中断或者释放了进程正

    在等待的资源,或者进程收到一个信号,都可以唤醒进程转换到就绪状态(运行状态)。 ◆不可中断睡眠状态(TASK_UNINTERRUPTIBLE)

    与可中断睡眠状态类似。但处于该状态的进程只有被使用 wake_up()函数明确唤醒时才能转换到可运行的就绪状态。

    ◆暂停状态(TASK_STOPPED) 当进程收到信号 SIGSTOP、SIGTSTP、SIGTTIN 或 SIGTTOU 时就会进入暂停状态。可向其发送SIGCONT 信号让进程转换到可运行状态。在 Linux 0.11 中,还未实现对该状态的转换处理。处于该状态的进程将被作为进程终止来处理。

    ◆僵死状态(TASK_ZOMBIE) 当进程已停止运行,但其父进程还没有询问其状态时,则称该进程处于僵死状态。

    当一个进程的运行时间片用完,系统就会使用调度程序强制切换到其它的进程去执行。另外,如果进程在内核态执行时需要等待系统的某个资源,此时该进程就会调用 sleep_on()或 sleep_on_interruptible()自 愿 地 放 弃 CPU 的 使 用 权 , �