如何在c语言的程序里嵌入Dos命令

dutzzl 2002-05-17 08:07:36
不知道能不能,如果能,该怎么做?
请赐教!
...全文
97 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
blue_soft 2002-05-17
  • 打赏
  • 举报
回复
函数名: system
功 能: 发出一个DOS命令
用 法: int system(char *command);
程序例:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
printf("About to spawn command.com and run a DOS command\n");
system("dir");
return 0;
}

littlebee 2002-05-17
  • 打赏
  • 举报
回复
System
同意 jerry921(jerry) 的
liubingqian 2002-05-17
  • 打赏
  • 举报
回复
#include <stdlib.h>
main()
{
system("DOS命令");
}
veinstone 2002-05-17
  • 打赏
  • 举报
回复
system
-_-中英文数字 2002-05-17
  • 打赏
  • 举报
回复
system(...)
jerry921 2002-05-17
  • 打赏
  • 举报
回复
head file
stdlib.h

int system(const char *command);

int _wsystem(const wchar_t *command);
spark_li 2002-05-17
  • 打赏
  • 举报
回复
system("dir c:\\");
WINDOWS环境 Windows几乎不需要介绍。然而人们很容易忘记Windows给办公室和家庭桌上型计算机所带来的重大改变。Windows在其早期曾经走过一段坎坷的道路,征服桌上型计算机市场的前途一度相当渺茫。 Windows简史 在1981年秋天IBM PC推出之后不久,MS-DOS就已经很明显成为PC上的主流操作系统。MS-DOS代表Microsoft Disk Operating System(磁盘操作系统)。MS-DOS是一个小型的操作系统。MS-DOS提供给用户一种命令列接口,提供如DIR和TYPE的命令,也可以将应用程序加载内存执行。对于应用程序写作者,它提供了一组函数呼叫,进行文件的输入输出(I/O )。对于其它的外围处理-尤其是将文字或图形写到显示器上-应用程序可以直接存取PC的硬件。 由于内存和硬件的限制,成熟的图形环境缓慢地才到来。当苹果计算机公司不幸的Lisa计算机在1983年1月发表时,它提供了不同于文字模式环境的另一种选择,并在1984年1月成为Macintosh上图形环境的一种标准。尽管Macintosh的市场占有率在下降,但是它仍然被认为是衡量所有其它图形环境的标准。包括Macintosh和Windows的所有图形环境,其实都要归功于Xerox Palo Alto Research Center(PARC)在70年代中期所作的开拓性研究工作。 Windows是由微软在1983年11月(在Lisa之后,Macintosh之前)宣布,并在两年后(1985年11月)发行。在此后的两年中,紧随着Microsoft Windows早期版本1.0之后,又推出了几种改进版本,以支持国际商业市场,并提供新型视讯显示器和打印机的驱动程序。 Windows版本2.0是在1987年11月正式在市场上推出的。该版本对使用者接口做了一些改进。这些改进中最有效的是使用了可重迭式窗口,而Windows 1.0中使用的是并排式窗口。Windows 2.0还增强了键盘和鼠标接口,特别是加入了菜单和对话框。 至此,Windows还只要求Intel 8086或者8088等级的微处理器,以「实际模式」执行,只能存取地址在1MB以下的内存。Windows/386(在Windows 2.0之后不久发行的)使用Intel 386微处理器的「虚拟8086」模式,实现将直接存取硬件的多个MS-DOS程序窗口化和多任务化。为了统一起见,Windows版本2.1被更名为Windows/286。 Windows 3.0是在1990年5月22日发表的。它将Windows/286和Windows/386结合到同一种产品中。Windows 3.0有了一个很大的改变,这就是对Intel的286、386和486微处理器保护模式的支持。这能使Windows和Windows应用程序能存取高达16MB的内存。Windows用于执行程序和维护文件的「外壳」程序得到了全面的改进。Windows 3.0是第一个在家用和办公室市场上取得立足点的版本。 任何Windows的历史介绍都必须包括一些OS/2的说明,OS/2是对DOS和Windows的另一种选择,最初是由Microsoft和IBM合作开发的。OS/2版本1.0(只有文字模式)在Intel 286(或者后来的)微处理器上运行,在1987年末发布。在1988年10月的OS/2版本1.1中出现了管理图形使用者接口的PM(Presentation Manager)。PM最初的设计构想是成为Windows的一种保护模式版本,但是图形API改变程度太大,致使软件生产厂商很难提供对这两种平台的支持。 到1990年9月,IBM和Microsoft之间的冲突达到了高峰,导致这两个公司最后分道扬镳。IBM接管了OS/2,而Microsoft明确表示Windows将是他们操作系统策略的中心。虽然OS/2仍然拥有一些狂热的崇拜者,但是它远不及Windows这样的普及程度。 Microsoft Windows版本3.1是1992年4月发布的,其中包括的几个重要特性是TrueType字体技术(给Windows带来可缩放的轮廓字体)、多媒体(声音和音乐)、对象连结和嵌入(OLE:Object Linking and Embedding)和通用对话框。跟OS/2一样,Windows 3.1只能在保护模式下运作,并且要求至少配置了1MB内存的286或386处理器。 在1993年7月发表的Windows NT是第一个支持Intel 386、486和Pentium微处理器32位保护模式的Windows版本。Windows NT提供32位平坦寻址,并使用32位的指令集。(本章后面我会谈到一些寻址空间的问题)。Windows NT还可以移植到非Intel处理器上,并在几种使用RISC芯片的工作站上执行。 Windows 95是在1995年8月发布的。和Windows NT一样,Windows 95也支持Intel 386或更高等级处理器的32位保护模式。虽然它缺少Windows NT中的某些功能,诸如高安全性和对RISC机器的可移植性等,但是Windows 95具有需要较少硬件资源的优点。 Windows 98在1998年6月发布,具有许多加强功能,包括执行效能的提高、更好的硬件支持以及与因特网和全球信息网(WWW)更紧密的结合。 Windows方面 Windows 98和Windows NT都是支持32位优先权式多任务(preemptive multitasking)及多线程的图形操作系统。Windows拥有图形使用者接口(GUI ),这种使用者界面也称作「可视化接口」或「图形窗口环境」。有关GUI的概念可追溯至70年代中期,在Alto和Star等机器上以及SmallTalk等环境中由Xerox PARC所作的研究工作。该项研究的成果后来被Apple Computer和Microsoft引入主流并流行起来。虽然有一些争议,但现在已非常清楚,GUI是(Microsoft的Charles Simonyi的说法)一个在个人计算机工业史上集各方面技术大成于一体的最重要产物。 所有GUI都在点矩阵对应的视讯显示器上处理图形。图形提供了使用屏幕的最佳方式、传递信息的可视化丰富多彩环境,以及能够WYSIWYG(what you see is what you get:所见即所得)的图形视讯显示和为书面文件准备好格式化文字输出内容。 在早期,视讯显示器仅用于响应使用者通过键盘输入的文字。在图形使用者接口中,视讯显示器自身成为使用者输入的一个来源。视讯显示器以图标和输入设备(例如按钮和滚动条)的形式显示多种图形对象。使用者可以使用键盘(或者更直接地使用鼠标等指向设备)直接在屏幕上操纵这些对象,拖动图形对象、按下鼠标按钮以及滚动滚动条。 因此,使用者与程序的交流变得更为亲密。这不再是一种从键盘到程序,再到视讯显示器的单向信息流动,使用者已经能够与显示器上的对象直接交互作用了。 使用者不再需要花费长时间学习如何使用计算机或掌握新程序了。Windows让这一切成真,因为所有应用程序都有相同的基本外观和感觉。程序占据一个窗口-屏幕上的一块矩形区域。每个窗口由一个标题列标识。大多数程序功能由程序的菜单开始。用户可使用滚动条观察那些无法在一个屏幕中装下的信息。某些菜单项目触发对话框,用户可在其中输入额外的信息。几乎在每个大的Windows程序中都有一个用于开启文件的特殊对话框。该对话框在所有这些Windows程序中看起来都一样(或接近相同),而且几乎总是从同一菜单选项中启动。 一旦您了解使用一个Windows程序的方法,您就非常容易学习其它的Windows程序。菜单和对话框允许用户试验一个新程序并探究它的功能。大多数Windows程序同时具有键盘接口和鼠标接口。虽然Windows程序的大多数功能可通过键盘控制,但使用鼠标要容易得多。 从程序写作者的角度看,一致的使用者接口来自于Windows建构菜单和对话框的内置程序。所有菜单都有同样的键盘和鼠标接口,因为这项工作是由Windows处理,而不是由应用程序处理。 为便于多个程序的使用,以及这些程序间信息的交换,Windows支持多任务。在同一时刻能有多个Windows程序显示并运行。每个程序在屏幕上占据一个窗口。用户可在屏幕上移动窗口,改变它们的大小,在不同程序间切换,并从一个程序向另一个程序传送数据。因为这些窗口看起来有些像桌面上的纸(当然,这是计算机还未占据办公桌之前的年代),Windows有时被称作:一个显示多个程序的「具象化桌面」。 Windows的早期版本使用一种「非优先权式(non-preemptive)」的多任务系统。这意味着Windows不使用系统定时器将处理时间分配给系统中运行的多个应用程序,程序必须自愿放弃控制以便其它程序运行。在Windows NT和Windows 98中,多任务是优先权式的,而且程序自身可分割成近乎同时执行的多个执行绪。 操作系统不对内存进行管理便无法实现多任务。当新程序启动、旧程序终止时,内存会出现碎裂空间。系统必须能够将闲置的内存空间组织在一起,因此系统必须能够移动内存中的程序代码和数据块。 即使是在8088微处理器上跑的Windows 1.0也能进行这类内存管理。在实际模式限制下,这种能力被认为是软件工程一个令人惊讶的成就。在Windows 1.0中,PC硬件结构的640KB内存限制,在不要求任何额外内存的情况下被有效地扩展了。但Microsoft并未就此停步:Windows 2.0允许Windows应用程序存取扩充内存(EMS);Windows 3.0在保护模式下,允许Windows应用程序存取高达16MB的扩展内存。Windows NT和Windows 98通过成熟的32位操作系统及平坦寻址空间,摆脱了这些旧的限制。 Windows上执行的程序可共享在称为「动态链接库」的文件中的例程。Windows包括一个机制,能够在执行时连结使用动态链接库中例程的程序。Windows自身基本上就是一个动态链接库的集合。 Windows是一个图形接口,Windows程序能够在视讯显示器和打印机上充分利用图形和格式化文字。图形接口不仅在外观上更有吸引力,而且还能够让使用者传递高层次的信息。 Windows应用程序不能直接存取屏幕和打印机等图形显示设备硬件。相反,Windows提供一种图形程序语言(称作图形设备接口,或者GDI),使显示图形和格式化文字更容易。Windows虚拟化了显示硬件,使为Windows编写的程序可使用任何具有Windows设备驱动程序的视频卡或打印机,而程序无需确定系统相连的设备类型。 对Windows开发者来说,将与设备无关的图形接口输出到IBM PC上不是件轻松的事。PC的设计是基于开放式架构的原则,鼓励第三方硬件制造商为PC开发接口设备,而且开发了大量这样的设备。虽然出现了多种标准,PC上的传统MS-DOS程序仍不得不各自支持许多不同的硬设备。这对MS-DOS字处理软件来说非常普遍,它们连同1到2张有许多小文件的磁盘一同销售,每个文件支持一种特定的打印机。Windows程序不要求每个应用程序都自行开发这些驱动程序,因为这种支持是Windows的一部分。 动态链接 Windows运作机制的核心是一个称作「动态链接」的概念。Windows提供了应用程序丰富的可呼叫函数,大多数用于实作其使用者接口和在视讯显示器上显示文字和图形。这些函数采用动态链接库(Dynamic Linking Library,DLL)的方式撰写。这些动态链接库是些具有.DLL或者有时是.EXE扩展名的文件,在Windows 98中通常位于\WINDOWS\SYSTEM子目录中,在Windows NT中通常位于\WINNT\SYSTEM和\WINNT\SYSTEM32子目录中。 在早期,Windows的主要部分仅通过三个动态链接库实作。这代表了Windows的三个主要子系统,它们被称作Kernel、User和GDI。当子系统的数目在Windows最近版本中增多时,大多数典型的Windows程序产生的函数呼叫仍对应到这三个模块之一。Kernel(日前由16位的KRNL386.EXE和32位的KERNEL32.DLL实现)处理所有在传统上由操作系统核心处理的事务-内存管理、文件I/O和多任务管理。User(由16位的USER.EXE和32位的USER32.DLL实作)指使用者接口,实作所有窗口运作机制。GDI(由16位的GDI.EXE和32位的GDI32.DLL实作)是一个图形设备接口,允许程序在屏幕和打印机上显示文字和图形。 Windows 98支持应用程序可使用的上千种函数呼叫。每个函数都有一个描述名称,例如CreateWindow。该函数(如您所猜想的)为程序建立新窗口。所有应用程序可以使用的Windows函数都在表头文件里预先声明过。 在Windows程序中,使用Windows函数的方式通常与使用如strlen等C语言链接库函数的方式相同。主要的区别在于C语言链接库函数的机械码连结到您的程序代码中,而Windows函数的程序代码在您程序执行文件外的DLL中。 当您执行Windows程序时,它通过一个称作「动态链接」的过程与Windows相接。一个Windows的.EXE文件中有使用到的不同动态链接库的参考数据,所使用的函数即在那些动态链接库中。当Windows程序被加载到内存中时,程序中的呼叫被指向DLL函数的入口。如果该DLL不在内存中,就把它加载到内存中。 当您连结Windows程序以产生一个可执行文件时,您必须连结程序开发环境提供的特定「引用链接库(import library)」。这些引用链接库包含了动态链接库名称和所有Windows函数呼叫的引用信息。连结程序使用该信息在.EXE文件中建立一个表格,在加载程序时,Windows使用它将呼叫转换为Windows函数。 WINDOWS程序设计选项 为说明Windows程序设计的多种技术,本书提供了许多范例程序。这些程序使用C语言撰写并原原本本的使用Windows API来开发程序。我将这种方法称作「古典」Windows程序设计。这是我们在1985年为Windows 1.0写程序的方法,它今天仍是写作Windows程序的有效方法。 API和内存模式 对于程序写作者来说,操作系统是由本身的API定义的。API包含了所有应用程序能够使用的操作系统函数呼叫,同时包含了相关的数据型态和结构。在Windows中,API还意味着一个特殊的程序架构,我们将在每章的开头进行研究。 一般而言,Windows API自Windows 1.0以来一直保持一致,没什么重大改变。具有Windows 98程序写作经验的Windows程序写作者会对Windows 1.0程序的原始码感觉非常熟悉。API改变的一种方式是进行增强。Windows 1.0支持不到450个函数呼叫,现在已有了上千种函数呼叫。 Windows API和它的语法的最大变化来自于从16位架构向32位架构转化的过程中。Windows从版本1.0到版本3.1使用16位Intel 8086、8088、和286微处理器上所谓的分段内存模式,由于兼容性的原因,从386开始的32位Intel微处理器也支持该模式。在这种模式下,微处理器缓存器的大小为16位,因此C的int数据型态也是16位宽。在分段内存模式下,内存地址由两个部分组成-一个16位段(segment)指针和一个16位偏移量(offset)指标。从程序写作者的角度看,这非常凌乱并带来了long或far指针(包括段地址和偏移量地址)和short或near指标(包括带有假定段地址的偏移量地址)的区别。 从Windows NT和Windows 95开始,Windows支持使用Intel 386、486和Pentium处理器32位模式下的32位平坦寻址内存模式。C语言的int数据型态也扩展为32位的值。为32位版本Windows编写的程序使用简单的平坦线性空间寻址的32位指针值。 用于16位版本Windows的API(Windows 1.0到Windows 3.1)现在称作Win16。用于32位版本Windows的API(Windows 95、Windows 98和所有版本的Windows NT)现在称作Win32。许多函数呼叫在从Win16到Win32的转变中保持相同,但有些需要增强。例如,图像坐标点由Win16中的16位值变为Win32中的32位值。此外,某些Win16函数呼叫返回一个包含在32位整数值中的二维坐标点。这在Win32中不可能,因此增加的新函数呼叫以不同方式运作。 所有32位版本的Windows都支持Win16 API(以确保和旧有应用程序兼容)和Win32 API(以运行新应用程序)。非常有趣的是,Windows NT与Windows 95及Windows 98的工作方式不同。在Windows NT中,Win16函数呼叫通过一个转换层被转化为Win32函数呼叫,然后被操作系统处理。在Windows 95和Windows 98中,该操作正相反:Win32函数呼叫通过转换层转换为Win16函数呼叫,再由操作系统处理。 在同一时刻有两个不同的Windows API集(至少名称不同)。Win32s (「s」代表「subset(子集)」)是一个API,允许程序写作者编写在Windows 3.1上执行的32位应用程序。该API仅支持已被Win16支持的32位函数版本。此外,Windows 95 API一度被称作Win32c(「c」代表「compatibility(兼容性)」),但该术语已被抛弃了。 现在,Windows NT和Windows 98都被认为能够支持Win32 API。然而,每个操作系统依然都支持某些不被别的操作系统支持的某些功能特性。因为它们的相同之处是相当可观的,所以有可能编写在两个操作系统下都可执行的程序。而且,人们普遍认为这两个产品最终会合而为一。 语言选项 使用C语言和原始的API不是编写Windows 98程序的唯一方法。然而,这种方法却提供给您最佳的性能、最强大的功能和在发掘Windows特性方面最大的灵活性。可执行文件相对较小且运行时不要求外部链接库(自然,Windows DLL自身除外)。最重要的是,不管您最终以什么方式开发Windows应用程序,熟悉API会使您对Windows内部有更深入的了解。 虽然我认为学习古典的Windows程序设计对任何Windows程序写作者都是重要的,我没有必要建议使用C和API编写每个Windows应用程序。许多程序写作者,特别是那些为公司内部开发程序或在家编写娱乐程序的程序写作者喜欢轻松的开发环境,例如Microsoft Visual Basic或者Borland Delphi(它结合了对象导向的Pascal版本)。这些环境使程序写作者将精力集中于应用程序的使用者接口和相关使用者接口对象的程序代码上。要学习Visual Basic,您也许需要参考Microsoft Press的一些其它图书,例如Michael Halvorson1996年着的《Learn Visual Basic Now》。 在专业程序写作者中-特别是那些开发商业应用程序的程序写作者-Microsoft Visual C++和Microsoft Foundation Class Library(MFC)是近年来流行的选择。MFC在一组C++对象类别中封装了许多Windows程序设计中的琐碎细节。Jeff Prosise的《Programming Windows with MFC,第二版》(Microsoft Press,1999年)提供了MFC程序的写作指南。 最近,Internet和World Wide Web的流行大力推广着Sun Microsystems的Java,这是一个受C++启发却与微处理器无关的程序设计语言,而且结合了可在几个操作系统平台上执行的图形应用程序开发工具组。Microsoft Press有一本关于Microsoft J++(Microsoft的Java)开发工具的好书,《Programming Visual J++ 6.0》(1998年),由Stephen R. Davis着。 显然,很难说哪种方法更有利于开发Windows应用程序。更主要的是,也许是应用程序自身的特性决定了所使用的工具。不管您最后实际上使用什么工具写作程序,学习Windows API将使您更深入地了解Windows工作的方式。Windows是一个复杂的系统,在API上增加一个程序写作层并未减少它的复杂性,仅仅是掩盖了它,早晚您会碰到它。了解API会给您更好的补救机会。 在原始的Windows API之上的任何软件层都必定将您限制在全部功能的一个子集内。您也许发现,例如,使用Visual Basic编写应用程序非常理想,然而它不允许您做一个或两个很简单的基本工作。在这种情况下,您将不得不使用原始的API呼叫。API定义了作为Windows程序写作者所需的一切。没有什么方法比直接使用API更万能的了。 MFC尤其问题百出。虽然它大幅简化了某些工作(例如OLE),我却经常发现要让它们按我所想的去工作时,会在其它特性(例如Document/View架构)上碰壁。MFC还不是Windows程序设计者所追求的灵丹妙药,很少有人认为它是一个好的对象导向设计的模型。MFC程序写作者从他们使用的对象类别定义如何工作中受益颇深,并会发现他们经常参考MFC原始码,搞懂这些原始码是学习Windows API的好处之一。 程序开发环境 在本书中,假定您正使用Microsoft Visual C++ 6.0,标准版、专业版和企业版都可以。经济的标准版足以应付本书中的程序设计需求。Visual C++ 还是Visual Studio 6.0中的一部分。 Microsoft Visual C++ 软件包中包括C编译器和其它编译及连结Windows程序所需的文件和工具等。它还包括Visual C++ Developer Studio,一个可编辑原始码、以交谈方式建立资源(如图标和对话框)以及编辑、编译、执行和测试程序的环境。 如果您正使用Visual C++ 5.0,则需要为Windows 98和Windows NT 5.0更新表头文件和引用链接库,这些东西可从Microsoft的网站上得到。在 http://www.microsoft.com/msdn/,选择「Downloads」,然后选择「 Platform SDK」(软件开发套件),您就能在选择的目录中下载和安装更新文件。要让Microsoft Developer Studio浏览这些目录,可以从「Tool」菜单项选择「 Options」然后按下「Directories」标签。 Microsoft网站上的msdn部分代表「Microsoft Developer Network(Microsoft软件开发者网络)」。这是一个向程序写作者提供了经常更新的CD-ROM的计划,这些CD-ROM中包含了程序写作者在Windows开发中所需的最新东西。您也可以订阅MSDN,这样就避免经常得从Microsoft的网站下载文件。 API文件 本书不是Windows API权威的正式文件的替代品。那组文件不再以印刷形式出版,它仅能从CD-ROM或Internet上取得。 当您安装Visual C++ 6.0时,您将得到一个包括API文件的在线求助系统。您可通过订阅MSDN或使用Microsoft网站上的在线求助系统更新该文件。连接到 http://www.microsoft.com/msdn/,并选择「MSDN Library Online」。 在Visual C++ 6.0中,从「Help」菜单项选择「Contents」项目开启MSDN窗口。API文件按树形结构组织,寻找标有「 Platform SDK」的部分,所有在本书中引用的文件都来自于该部分。我将向您介绍如何从「 Platform SDK」开始寻找以斜线分层分门别类的文件的位置。(我知道「Platform SDK」是整个MSDN知识库中较为晦涩的部分,但我敢保证那是Windows程序设计的基本核心。)例如,对于如何在Windows程序中使用鼠标的文件,您可参考/ Platform SDK / User Interface Services / User Input / Mouse Input。 我在前面提到Windows大致分为Kernel、User和GDI子系统。kernel接口在/ Platform SDK / Windows Base Services中,User界面函数在 / Platform SDK / User Interface Services中,GDI位于 / Platform SDK / Graphics and Multimedia Services / GDI中。 编写第一个WINDOWS程序 现在是开始写些程序的时候了。为了便于对比,让我们以一个非常短的Windows程序和一个简短的文字模式程序开始。这会帮助我们找到使用开发环境并感受建立和编译程序机制的正确方向。 文字模式(Character-Mode)模型 程序写作者们喜爱的一本书是《The C Programming Language》(Prentice Hall,1978年和1988年),由Brian W. Kernighan和Dennis M. Ritchie(亲切地称为K&R)编着。该书的第一章以一个显示「hello, world」的C语言程序开始。 这里是在《The C Programming Language》第一版第6页中出现的程序: main () { printf ("hello, world\n") ; } 以前C程序写作者在使用printf等C执行期链接库函数时,无需先声明它们。但这是90年代,我们愿意给编译器一个在我们的程序中标出错误的机会。这里是在K&R第二版中修正的程序: #include main () { printf ("hello, world\n") ; } 该程序仍然是那么短。但它可通过编译并执行得很好,但当今许多程序写作者更愿意清楚地说明main函数的返回值,在这种情况下ANSI C规定该函数必须返回一个值: #include int main () { printf ("hello, world\n") ; return 0 ; } 我们还可以包括main的参数,把程序弄得更长一些,但让我们暂且这样就好了-包括一个include声明、程序的进入点、一个对执行期链接库函数的呼叫和一个return语句。 同样效果的Windows程序 Windows关于「hello, world」程序的等价程序有和文字模式版本完全相同的组件。它有一个include声明、一个程序进入点、一个函数呼叫和一个return语句。下面便是该程序: /*------------------------------------------------------------------ HelloMsg.c -- Displays "Hello, Windows 98!" in a message box (c) Charles Petzold, 1998 --------------------------------------------------------------------*/ #include int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MessageBox (NULL, TEXT ("Hello, Windows 98!"), TEXT ("HelloMsg"), 0); return 0 ; } 在剖析该程序之前,让我们看一下在Visual C++ Developer Studio中建立新程序的方式。 首先,从File菜单中选New。在 New对话框中,单击Projects页面标签,选择 Win32 Application。在Location栏中,选择一个子目录,在 Project Name栏中,输入该项目的名称,此时该名称是HelloMsg,这便是在 Location栏中显示的目录的子目录。Create New Workspace复选框应该勾起来,Platforms部分应该显示 Win32,选择OK。 将会出现一个标题为Win32 Application - Step 1 Of 1的对话框,指出要建立一个Empty Project,并按下Finish按钮。 从File菜单中再次选择New。在 New对话框中,选择Files页面标签,选择 C++ Source File。Add To Project复选框应被选中,并应显示HelloMsg。在 File Name栏中输入HelloMsg.c,选中OK。 现在您可输入上面所示的HELLOMSG.C文件,您也可以选择Insert菜单和 File As Text选项从本书附带的CD-ROM上复制HELLOMSG.C的内容。 从结构上说,HELLOMSG.C与K&R的「hello,world」程序是相同的。表头文件STDIO.H已被WINDOWS.H所代替,进入点main被WinMain所代替,而且C语言执行时期链接库函数printf被Windows API函数MessageBox所代替。然而,在程序中有许多新东西,包括几个陌生的大写标识符。 让我们从头开始。 表头文件 HELLOMSG.C以一个前置处理器指示命令开始,实际上在每个用C编写的Windows程序的开头都可看到:
Tcl_TK编程权威指南pdf 内容简介回到顶部↑Tcl/Tk是第一种能通过Windows、Macintosh和Solaris等主要平台处理企业级任务的脚本语言。本书共分为55章,依次详细讲述了Tcl基础、Tcl高级特性、TK基础、TK组件、TK详解、C语言编程、各版本之间的差异等方面的知识,并通过大量实例,生动翔实地向读者介绍了Tcl/Tk编程,是读者掌握Tcl/Tt的必备参考书。 本书适合各个层次的读者阅读。 目录回到顶部↑第1部分 tcl基础 第1章 tcl的基本知识 tcl命令 hello,world! 变量 命令替换 数学表达式 反斜杠替换 使用花括号和双引号进行分组 过程 一个阶乘的例子 更多有关变量的知识 更多有关数学表达式的内容 注释 有关替换与分组的总结 要点 参考 第2章 开始使用 source命令 unix上的tcl脚本程序 .windows 95的开始菜单 macintosh与resedit console命令 命令行变元 预定义变量 第3章 cgi应用程序--顾客留言簿 html简介 使用cgi创建动态页面 guestbook.cgi脚本程序 定义表单以及处理表单数据 cgi.tcl软件包 接下去的几步 第4章 tcl中的字符串处理 string命令 append命令 format命令 scan命令 binary命令 相关章节 第5章 tcl列表 tcl列表 构建列表 获取列表元素 修改列表 搜索列表 对列表进行排序 split命令 join命令 相关章节 第6章 控制结构命令 if then else switch while foreach for break与continue catch error return 第7章 过程与作用域 proc命令 使用rename来改变命令名 作用域 global命令 通过upvar以名字进行调用 使用upvar来处理变量别名 第8章 tcl数组 数组的语法 array命令 使用数组来构建数据结构 第9章 对文件和程序的操作 使用exec运行程序 file命令 跨平台的文件命名方式 操作文件和目录 文件属性 对i/o命令的总结 打开文件用于i/o操作 读写操作 当前目录-cd和pwd 使用glob来匹配文件名 exit和pid命令 环境变量 registry命令 第2部分tcl高级特性 第10章 引用问题与eval 使用list命令来构建代码 在eval内部利用concat uplevel命令 subst命令 第11章 正则表达式 何时使用正则表达式 正则表达式的语法 高级正则表达式(are) 语法总结 regexp命令 rgsub命令 使用regsub将数据转换为程序 其他使用正则表达式的命令 第12章 脚本库及软件包 确定软件包的位置:auto-path变量 使用软件包 对软件包加载的总结 package命令 基于文件tclindex的库 unknown命令 方便交互 tclshell的库环境 编码风格 第13章 反射与调试 clock命令 info命令 跨平台支持 跟踪变量的值 交互式命令历史记录 调试 scriptics的tclpro 其他工具 性能调校 第14章 名字空间 使用名字空间 名字空间变量 命令查找 嵌套名字空间 过程的进口与输出 回调与名字空间 内省(introspection) namespace命令 转换现有的软件包以使用名字空间 [incrtcl]对象系统 注意事项 第15章 国际化(internationalization) 字符集与编码 消息目录 第16章 事件驱动的编程 tcl事件循环 after命令 fileevent命令 vwait命令 fconfigure命令 第17章 套接字编程 客户端套接字 服务器端套接字 回送(echo)服务 使用http获取一个url http软件包 基本认证 第18章 tclhttpd web服务器 将 tclhttpd与你的应用程序集成 域处理程序 应用执导的url 文档类型 html+tcl模板 表单处理程序 编程参考 标准应用执导(application-dirct)的url tclhttpd发行版 服务器配置 第19章 多解释器与 safe-tcl interp命令 创建解释器 安全解释器 命令别名 隐藏命令 替换 从安全解释器中执行i/o操作 安全基础 安全策略 第20章 safe-tk与浏览器插件 子解释器中的tk 浏览器插件 安全策略与浏览器插件 配置安全策略 第3部分 tk基础 第21章 tk的基本知识 th中的hello,world! tk组件的命名 配置tk组件 tk组件属性与资源数据库 tk命令概要 第22章 tk实例解析 execlog example browser tcl shell 第23章 打包摆放布局管理器(pack) 朝一侧摆放 水平与垂直难叠 空腔模型( cavity model) 打包摆放空间(packing space)与显w空间(display space) 尺寸调整与一expand 挂靠 摆放顺序 选择用于摆放的父组件 取消一个组件的摆放 打包器总结 窗口的堆叠顺序 第24章 栅格摆放布局管理器( grid) 一种基本栅格 跨行列摆放 行列约束 grid命令 第25 章定位摆放布局管理器( place) place的基础知识 面板管理器 place命令 第26章 将命令与事件编联 bind命令 bindtags命令 事件的语法 修饰符 事件序列 虚拟事件 事件关键词 第4部分 tk组件 第27章 按钮与菜单 按钮命令与作用域问题 与tcl变量关联的按钮 按钮属性 按钮操作 菜单和菜单按钮 键盘遍历 操纵菜单和菜单条目 菜单属性 通过名字来指定菜单的软件包 第28章 资源数据库 有关资源的介绍 加载选项数据库 添加单一的数据库条目 存取数据库 用户定义的按钮 用户定义的菜单 第29章 简单的tk组件 框架组件与顶层窗口 标签组件 消息组件 标尺组件 bell命令 第30章 滚动条 使用滚动条 滚动条协议 滚动条组件 第31章 输入条组件 使用输入条组件 输入条组件 第32章 列表框组件 使用列表框组件 列表框组件的编联 列表框组件的属性 第33章 文本组件 文本索引 文本标记 文本标签 文本信息的选择( selection) 标签的编联 文本搜索 嵌入组件 图片的嵌入 查看文本组件的内部信息 文本组件的编联 文本组件的操作 文本组件的属性 第34章 画布组件 画布坐标 hello, world! 最小和最大标尺的例子 画布对象 画布组件的操作 产生postscript输出 画布组件的属性 建议 第5部分 tk详解 第35章 选择和剪贴板 选择模型 selection命令 clipboard命令 选择处理程序 第36章 焦点、焦点的捕获和对话框 标准对话框 定制对话框 使用update命令实现动画 第37章 tk组件的属性 配置属性 尺寸 边界与浮雕效果 焦点的高亮显示 补自(padding)与挂靠(anchor) 第38章 颜色、图片和鼠标指针 颜色 色彩映射与视频种类 位图和图片 文本插入光标 鼠标指针 第39章 字体与文本属性 字体命名 x字体名 字模 font命令 文本属性 栅格化、尺寸调整和布局 一个字体选择应用程序 第40章 send send命令 发送者脚本 通信进程 通过套接字来实现远程eval 第41章 窗口管理器与窗口信息 win命令 winfo命令 tk命令 第42章 管理用户首选项 应用默认设置文件 定义首选项 首选项的用户界面 管理首选项文件 跟踪对首选项变量的修改 对该软件包的改进 第43章 一种操作编联的用户界面 一对协调工作的列表框 编辑界面 保存与加载编联 第6部分 c语言编程 第44章 c语言编程与tcl 基本概念 创建可加载软件包 一个用c语言实现的命令过程 blob命令的例于 字符串与国际化 tolmain和tcl-applnit tk_main 事件循环 从c中调用脚本 第45章 编译tci及扩展模块 标准目录结构 从源代码建立tci 使用占位函数库(stub library) 使用autoconf 扩展模块范例 makefile.in 第46章 使用c语言编写tk组件 初始化扩展模块 组件的数据结构 组件的类命令 组件实例命令 配置和重新配置属性 指定组件属性 时钟的显示 窗口事件过程 最后的清除工作 第47章 c函数库概览 tclc函数库概览 tk c函数库概览 第7部分 各版本之间的差异 第48章 tcl 7.4/tk 4.0 wish 过时废弃的功能 cgct操作 输入焦点的高亮显示 编联 滚动条接日 pack info 焦点 send命令 按钮的内部补白 单选按钮的值 输入条组件 菜单 列表框 没有了geometry属性 文本组件 颜色属性 颜色分配与tk colormodel 画布组件的scrollincrement 选择 bell命令 第49章 tcl 7.5/tk 4.1 跨平台脚本 clock命令 load命令 package命令 多个foreach循环变量 事件循环从tk转移到了tcl 网络套接字 多解释器与safe-tcl grid布局管理器 文本组件 输入条组件 第50章 tcl7.6/tk 4.2 更多的file操作 虚拟事件 标准对话框 新的grid布局管理器 macintosh的unsupportedl命令 第51章 tcl/tk 8.0 tcl编译器 名字空间 safe-tcl 新的lsort tcl_precision变量 2000年约定 http软件包 串行线i/o 独立于平台的字体 tk scaling命令 应用程序的嵌入 本地化菜单与菜单条 cde的边界宽度 本地化的按钮和滚动条 文本组件中的图片 destroy不再产生错误 grid rowconfigure 补丁版本 第52章 tcl/tk 8.1 unicode与国际化 线程安全 高级正则表达式 新字符串命令 dde扩展模块 杂类 第53章 tcl/tk 8.2 trf补丁 更快的字符串操作 空数组名 浏览器插件的兼容性 第54章 tcl/tk 8.3 关于tcl的修改建议 关于tk的改动建议 第55章 有关本书的cd-rom ↓展开全部内容 序言回到顶部↑Tcl为工具命令语言(Tool Command Language)的缩写。它其实是指两样东西:一种脚本语言,以及该脚本语言的解释器。该解释器可以很容易地嵌入到你的应用程序中。Tcl和与之关联的图形用户界面工具包(Tk)是由加州大学的John Ousterhout教授设计并编写的。尽管它是个商用软件包,但你也可以在Internet上找到它(见第VII页),而且可以在自己的应用程序中自由使用这个软件包。Tcl解释器已经从Unix平台移植到了DOS、Windows、OS/2、NT以及Macintosh环境中,而TK工具包也从X window系统移植到了Windows和Macintosh环境中。 1988年,当我在Berkeley做ousterhout教授的博士生时,第一次听说了Tcl。我们当时正在设计一种名为Sprite的网络操作系统。同学们在努力编制一个新式的内核程序,而John编写了一个新的编辑器和终端仿真程序。他使用Tcl作为这两种工具的命令语言,这样用户就可以定义菜单或者对那些程序进行定制。那时还处在使用X10的时代,他计划编写一个基于Tcl的X工具包,以使程序之间通过Tcl命令进行通信,彼此相互协作。对我来说,这种工具之间的相互协作就是Tcl的实质。 这种早期的设想就是让应用程序由包含编译代码的大块实体和一小部分用于进行配置和编写高级命令的Tcl代码组成。John的编辑器皿,还有终端仿真程序tx就遵循了这种模式。虽然这种模式仍然是有效的,但结果表明用Tcl来编写整个应用程序也是可能的。这是因为Tcl/Tk的shell程序wish提供了对其他程序、文件系统和网络套接字的存取功能,同时还能够创建图形用户界面。不管怎样,现在发现包含几千行Tcl脚本的应用程序并不稀奇。 我编写这本书的原因就是,虽然自己觉得使用Tcl与Tk既有乐趣又高效,但是也有令人头痛的时候。此外,在Xerox PARC工作,那里有许多语言和系统上的专家,我不得不强迫自己去理解Tcl/Tk的长处和弱点。我的许多同事都在他们的项目中采用了Tcl和Tk,但是他们也很快指出了它的缺点。因此,我就总结了一套编程技巧以充分利用Tcl/Tk的强大功能,同时回避一些棘手的问题。这本书就是一本帮助你最大限度地利用Tcl/Tk并回避一些我所经历过的令人头痛的问题的实用编程指南。 我接触Tcl语言大概已经有10年的时间了,而本书的第一版也已经出版5年了。在过去的几年中,我一直在John Ousterhout的手下工作,最初是在Sun微系统公司,而现在是在Scriptics公司。我一直使自己在很大程度上保持着一个Tcl程序员的角色,而我们工作组中的其他人员则埋头于Tcl本身的C语言实现。我创建的应用程序有HTML编辑器、EMAIL比用户接口程序、Web服务器以及用户数据库,我们的商务应用就建立在它们的基础上。这些经历在本书中有所反映。本书的大部分内容是有关Tcl脚本编程的,而有关使用C语言来创建Tcl扩展模块的内容没有着重讲述。我有幸一直参与Tcl核心技术的开发活动,希望通过本书能够将自己使用Tcl时获得的切身体会表达出来。 为什么要使用Tcl 作为一种脚本语言,Tcl与其他的Unix shell语言,如Bourne Shell(sh)、C Shell(csh)、Korn Shell以及Perl类似。Shell程序可以让你执行其他的程序。它们提供了足够的可编程特性(变量、流程控制和过程),使你可以将现有程序组装成符合自己需要的复杂的脚本程序。Shell程序非常适用于一些日常任务的自动化处理工作。 Tcl解释器可以很容易地添加到你的应用程序中,这种能力将它与其他的shell语言区分开来。Tcl扮演了一种扩展语言的角色,用来配置和定制应用程序。你没有必要再去为自己的新应用程序发明一种命令语言,或是费力为自己的工具提供某种用户可编程特性。其实,你可以通过添加一个Tcl解释器,来将自己的应用程序组织成一组操作原语,并使用这些原语来构造最符合用户需求的脚本程序。这样还可以允许其他的程序通过编程来控制你的应用程序,以使套装应用程序能够很好地在一起工作。 Tcl的C函数库拥有清晰的接口而且便于使用。该函数库实现了基本的解释器,它有一套实现变量、流程控制和过程的核心脚本命令,而且还有一组用来存取操作系统服务以运行其他程序、存取文件系统和使用网络套接字的命令。Tcl和Tk提供了一台可以在UNIX、Windows和Macintosh环境中可移植的"虚拟机"。 因为你的应用程序可以定义新的Tcl命令,所以Tcl虚拟机是可扩展的。这些命令与你的应用程序所提供的C或C++过程关联。结果应用程序就分割成一组用编译语言编写的原语,并输出成为相应的Tcl命令。使用Tcl脚本程序可以将这些原语组装成完整的应用程序。脚本语言层可以存取与shell类似的功能以运行其他的程序,可以存取文件系统,还可以直接通过自己定义的Tcl命令来调用应用程序中编译的代码部分。此外,从C编程的层面上来说,你还可以调用Tcl脚本程序、设置和询问Tcl变量,甚至跟踪Tcl解释器的执行。 在Internet上有许多可自由使用的Tcl扩展模块。许多扩展模块都包含了一个提供某种新功能的C函数库,以及该函数库的Tcl接口。这样的例子包括数据库存取、电话控制、MIDI控制器存取,还有expect,它为控制交互式程序增加了一组Tcl命令。 最为著名的扩展模块就是Tk,这是一种图形用户界面工具包。Tk定义了用来创建和操作用户界面组件的Tcl命令。这种基于脚本的用户界面编程方法有三个好处: . 由于快速的响应周期,所以开发迅速,不存在漫长的编译等待过程。 . Tcl命令提供了一种比绝大多数由标准C函数库实现的用户界面工具包更为高级的接口。它只需一小组命令就可以定义简单的用户界面,同时又可以对用户界面进行细化以恰当地实现每一个细节。快速的响应周期又为这种细化过程提供了帮助。 用户界面处理可以从你的应用程序的其余部分分离出来。因而开发人员能够专心致志地实现应用程序的核心部分,然后再颇为轻松地构建出用户界面。Tk组件的核心功能通常能够满足你所有的用户界面需求。不过,你还可以用C语言来编写定制的Tk组件,而且网上还有许多大家提供的Tk组件可以使用。 还有其他可以用做扩展语言的选择,这包括VisualBasic、Scheme、Elisp、Perl;Python和Javascript等,你可以依照个人喜好从中进行选择。Tcl拥有简单的结构,而且还有些地方类似于C语言,可以通过编制C过程来增添新的Tcl原语。Tcl非常易学,许多有关用户使用Tcl在很短的时间内(例如几个星期)就完成了相当难度的项目,并且他们以前压根就没有接触过Tcl。 当本书第一次出版时,Java轰动了计算机界。Java是一种极为优秀的系统编程语言,长远来看还有可能代替C和C什语言。这对Tcl来说挺好,它在设计时就被用来将由任意系统编程语言编写的构件粘连起来。Tcl过去被设计与C语言一起工作,但是现在已经被改造成能够与Java虚拟机一起工作。在我们提到"C或C++"的地方,现在也可以说"C、C++或Java"了,但是对于Java来说,其细节上还多少存在些差异。这本书并没有描述TcVJava接口,但是你可以在CD-ROM上找到TclBlend。TclBlend将Java虚拟机加载到你的Tc3应用程序中并允许你调用Java方法,它还可以让你使用Java而不是C或C十十来实现Tcl命令。 Javascript是一种来自于Netscape的语言,它被设计用来编写与w曲页面进行交互的脚本程序。由于Netscape的广泛使用,Javascript就显得很重要,然而Tcl提供了一种更为通用的脚本方案,可以在更为广泛的范围中使用。Tcl/Tk的Web浏览器插件提供了一种在浏览器中运行Tcl的方式,结果使得Tcl更像是一种Java的替代品而不是Javascript的替代品。该插件可以让你在浏览器中执行Tcl应用程序,而Javascript则为你提供了对浏览器和HTML显示的精细控制。这种插件将在第20章有所描述。TcI与Tk的版本 Tcl与Tk仍在继续演变。请参看http://www.beedub.com/book/来了解有关最新的Tcl版本的更新和消息。由于历史原因,Tcl与Tk曾各有各的版本号,但是它们成对发行,并一起工作。这本书的原始版本基于Tcl7.4和Tk 4.0并有几处引用了Tk 3.6的功能。第三版已经进行了更新,它反映了直到Tcl/Tk8.2以来所增添的各种新特性: . Tcl7.5和Tk 4.1的最终发布在1996年5月。这些版本的特点是将Tk移植到了Windows和Macintosh环境。它引入了Safe-Tcl安全机制,以支持网络小应用程序(Applet)的 .安全执行。它还提供了对网络套接字的支持以及一种新的输入输出(I/O)子系统,以支持高性 能的事件驱动I/O。 . Tcl7.6和Tk4.2的最终发布是在1996年的10月。这些版本包含了对S池-Tcl的改进,以及对在Tk 4.1中引进的grid布局管理器的改进。跨平台的支持包括虚拟事件(例如,以<<Copy>>宋代表<Control-c>=、标准对话框,还有更多的文件操作命令。 . Tcl 7.7和Tk 4.3是内部版本,用于开发NetscapeNavigator和MicrosoftInternetExplorer Web浏览器的Tcl/Tk插件。它们的开发工作实际上与Tcl7.6和Tk 4.2并行进行。Tcl/Tt插件已经发布了许多各种平台上的版本,其中包括Solaris/SPARC、Solaris/INTEL、SunOS、Linux、Digital UNIX、IRIX、HP/UX、Windows95、Windows NT以及Macintosh。该浏览器插件支持Web页面中的Tcl小应用程序(Applet),同时使用Safe-Tcl复杂的安全机制来提供安全保证。 . Tcl8.0为Tcl新增了一个运行时用的编译器,这个编译器提供了数倍于Tcl脚本的执行速度。Tcl8.0支持内嵌空字符的字符串。编译器对脚本来说是透明的,但是扩展模块编写入员需要学习一些新的C API才能发挥它的潜力。由于John Ousterhout从Sun微系统公司到了Scriptics公司,发布8.0版的时间推迟了几年。广泛使用的版本8.0p2是在1997年完成的,但是最终的补丁版本8.0.5直到1999年春才发布。 . 在8.0时,Tk更改了版本号以与Tcl相匹配。Tk 8.0包含了一种新的独立于平台的字体机制,它还包含了本地化菜单和菜单条,以及更多的本地化组件,它们在Windows和Macintosh上拥有更好的本地化外观。 Tcl/Tk8.1新特性主要包括对Unicode的完整支持,以及线程安全,这样你就可以将Tcl嵌入到多线程的应用程序中。Unicode是一种新的正则表达式引擎,它提供了在Perl5中所能找到的所有功能。Tk为找到正确的用于显示Unicode字符的字体完成了卓越的工作,它还增加了一种信息目录设施,这样你就可以编写国际化的应用程序。Tcylk 8.1的发布史中还包括了Sun到Scriptics的过渡。第一个alpha版本完成于1997年秋,而最终的补丁版本完成于1999年5月。 Tcl/Tk 8.2主要是一个进行bug修正和稳固化的版本。它对TclC函数库API进行了几处微小增补,这样无须核心补丁程序也能支持更多的扩展模块。Tcl/Tk 8.2很快在1999年夏进入最终版本。 谁应当阅读本书 本书不仅适用于熟练的编程人员,同样也适用于初学者。对于初学者和熟练编程人员来说,我建议大家仔细学习一下策1章"Tcl的基本知识"。Tcl的编程模型被设计成一种简单的模式,它与许多编程语言存在差异。该模型基于字符串替换,你对这一点的正确理解很重要,这样才能避免在复杂情况下遇到麻烦。这本书的其余部分则包含了演示如何高效地使用Tcl与Tt的例子。每一章中都有对其中所描述的Tcl命令和Tk组件进行总结的表格,以供参考。 本书假定你有一些编程经验,但是你如果是个彻头彻尾的新手也能够读下去。对Unix shell的了解将会对你有所帮助,但这并不是必须的。在那些涉及Windows系统的地方,我会提供一些背景信息。第2章详细描述了在UNIX、Windows和Macintosh上使用Tcl与Tk的内容。 如何阅读本书 本书最好能在上机实习中使用,可以在计算机上尝试一下书中的例子。Tcl与Tk的命令手册尽管完整但却缺少上下文的的相关信息和例子,本书就试图填补在简明手册与现有的文档化或没有很好文档化的Tcl程序之间的空隙。 我推荐使用联机手册来查阅有关的Tcl/Tk命令。它为每个命令都提供了详细的参考指南,但是它没能提供完整的细节,这在每一次发布的版本中都有所不同。HTML版本的联机手册可以在随书的CD-ROM中找到。
内容简介   本书从只有二十行的引导扇区代码出发,一步一步地向读者呈现一个操作系统框架的完成过程。书中不仅关注代码本身,同时关注完成这些代码的思路和过程。本书不同于其他的理论型书籍,而是提供给读者一个动手实践的路线图。读者可以根据路线图逐步完成各部分的功能,从而避免了一开始就面对整个操作系统数万行代码时的迷茫和挫败感。书中讲解了大量在开发操作系统中需注意的细节问题,这些细节不仅能使读者更深刻地认识操作系统的核心原理,而且使整个开发过程少走弯路。本书分上下两篇,共11章。其中每一章都以前一章的工作成果为基础,实现一项新的功能。而在章的内部,一项大的功能被分解成许多小的步骤,通过完成每个小的步骤,读者可以不断获得阶段性的成果,从而让整个开发过程变得轻松并且有趣。   本书适合各类程序员、程序开发爱好者阅读,也可作为高等院校操作系统课程的实践参考书。 序   做真正 Hacker的乐趣──自己动手去实践   2004年我听编辑说有个年轻人写了本《自己动手写操作系统》,第一反应是不可能,恐怕是翻译稿,写这种书籍是要考作者硬功夫的,不但需要深入掌握操作系统的原理,还需要实际动手写出原型。   历史上的 Linux就是这么产生的,Linus Torvalds当时是一名赫尔辛基大学计算机科学系的二年级学生,经常要用自己的电脑去访问大学主机上的新闻组和邮件,为了方便读写和下载文件,他自己编写了磁盘驱动程序和文件系统,这成为了 Linux第一个内核的雏形。   我想中国有能力写出内核原型的程序员应该也有,但把这个题目写成一本书,感觉上不会有人愿意做这件事情,作者要花很多时间,加上主题比较硬,销售量不会太高,经济上回报有限。   但拿来文稿一看,整个编辑部大为惊艳,内容文笔俱佳,而且绝对原创,马上决定在《程序员》连载。2005年博文视点出版的第一版也广受好评。   不过有很多读者还是质疑:现在软件编程主要领域是框架和应用,还需要了解操作系统底层吗?   经过四年的磨练成长,于渊又拿出第二版的书稿《Orange'S:一个操作系统的实现》,这本书是属于真正 Hacker的。我虽然已经有多年不写代码了,但看这本书的时候,让我又重新感受到做程序员的乐趣:用代码建设属于自己的系统,让电脑听从自己的指令,对系统的每个部分都了如指掌。   黑客(hacker)实际是褒义词,维基百科的解释是喜欢用智力通过创造性方法来挑战脑力极限的人,特别是他们所感兴趣的领域,例如软件编程或电气工程。个人电脑、软件和互联网等划时代的产品都是黑客创造出来的,如苹果的 Apple电脑、微软的 Basic解释器、互联网的 Mosaic浏览器。   回答前面读者的质疑,学软件编程并不需要看这本书,想成为优秀程序员和黑客的朋友,我强烈建议你花时间来阅读这本书,并亲自动手实践。正如于渊在本书结尾中所说“我们写自己的操作系统是出于一种好奇,或者说一种求知欲。我希望这样不停地‘过把瘾’能让这种好奇不停地延续”。   好奇心是动力的源泉,追究问题的本质是优秀黑客的必备素质,只有充分掌握了系统原理,才能在技术上游刃有余,才能有真正的创新和发展。中国需要更多真正的黑客,也希望更多的程序员能享受属于黑客的创造乐趣。   蒋涛   2009年 4月 作者自序   本书是《自己动手写操作系统》的第二版,通过一个具体的实例向读者呈现一个操作系统雏形的实现过程。有关操作系统的书籍资料可以找到很多,但是关注如何帮助读者实现一个试验性操作系统的书籍却不多见,本书便是从一个简单的引导扇区开始,讲述一个操作系统成长的故事,以作读者参考之用。   本书面向实践,通过具体实例教读者开发自己的操作系统。书中的步骤遵循由小到大、由浅入深的顺序,跟随这些步骤,读者可以由一个最简单的引导扇区开始,逐渐完善代码,扩充功能,最后形成一个小的操作系统。   本书不仅介绍操作系统的各要素,同时涉及开发操作系统需要的各个方面,比如如何建立开发环境、如何调试以及如何在虚拟机中运行等。书中的实例操作系统采用IA32作为默认平台,所以保护模式也作为必备知识储备收入书中,而这是传统的操作系统实践书籍经常忽略的。总之,只要是开发自己的操作系统中需要的知识,书中都尽量涉及,以便于读者参考。   众所周知,一个成型的操作系统往往非常复杂。如果考虑到操作系统作为软硬件桥梁的特殊地位,那么它可能看上去比一般的软件系统更难理解,因为其核心部分往往包含许多直接针对CPU、内存和 I/O端口的操作,它们夹杂在一片代码汪洋之中,显得更加晦涩。   我们有许多源代码公开的操作系统,可供随时下载和阅读,看上去好像让实现一个供自己把玩的微型操作系统变得容易很多,但事实往往不尽人意,因为这些代码动辄上万甚至几十几百万行,而且细节之间经常互相关联,要理解它们着实不易。我们有许多容易得到的操作系统教程,但读来好像总觉得跟我们有隔膜,不亲近。造成这些的根本原因,在于学习者一开始就面对一个完整的操作系统,或者面对前辈们积累了几十年的一系列理论成果。而无论作者多么擅长写作,读者多么聪明,或者代码多么优秀,要一个初学者理清其中的头绪都将是非常困难的。   我并非在此危言耸听,因为这曾经是我的亲身体会。当然,如果只是为了考试,几本操作系统理论书籍就足够了,你不需要对细节那么清楚。但如果是出于兴趣呢?如果你是想编写自己的操作系统呢?你会发现理论书籍好像一下子变得无用武之地,你会发现任何一个细节上的理解错误都可能导致自己辛辛苦苦编写的代码运行异常甚至崩溃。   我经历过这一切!我曾经翻遍了一本《操作系统:设计与实现》,也没有找到实现一个操作系统应该从何处着手。并不是这些书不好,也不是前人的代码不优秀,而是作为一无所知的初学者,我们所不了解的不仅是高居庙堂的理论知识,还有让我们举步维艰的实践细节。   可能在这些教科书作者的眼里,操作的细节不属于课程的一部分,或者这些细节看上去太容易,根本不值一提,甚至作者认为这些属于所谓“经验”的一部分,约定俗成是由读者本人去摸索的。但是实际情况往往是,这些书中忽略掉的内容恰恰占去了一个初学者大部分的时间,甚至影响了学习的热情。   我至今仍记得当我开始编写自己的操作系统时所遭受的挫败感,那是一种不知道如何着手的无助的感觉。还好我坚持了下来,克服了各种困难,并完成了自己的操作系统雏形。   进而我想到,一定不只是我一个人对编写自己的操作系统怀有兴趣,也一定不只是我一个人在实践时遇到困难。或许我应该把自己的经历写下来,从而可以帮助跟我相似的后来者,就这样,我编写了本书的第一版,也就是《自己动手写操作系统》。我相信,如果你也对神奇的计算机世界充满好奇,并且希望通过自己编写操作系统的方式来了解背后发生的故事,那么你一定可以在这本书中得到一些帮助。而假如你真的因为我的书而重新燃起实践的热情,从而开始一段操作系统旅程,我将会感到非常高兴。   不过我得坦白,在写作《自己动手写操作系统》的时候,我并不敢期待它能引起多少反响,一方面因为操作系统并不是时尚的话题,另一方面我也是走在学习的路上,或许只是比读者早走了一小步而已。然而出乎我的意料,它面世后重印多次,甚至一度登上销量排行榜的榜首,这让我觉得它的确有一定的参考价值,我要借此机会感谢所有支持我的读者。   在我写作《自己动手写操作系统》的时候,并没有想过今天会有一个第二版。原因在于,我希望这本书是用来填补空白的,而不是重复去做别人已经做得很好的事情。所谓填补空白,具体说就是让像我一样的操作系统爱好者在读完本书之后,能够有信心去读其他比较流行的开源的操作系统代码,有能力从零开始自己动手写操作系统,而这个任务第一版已经完成了。   那么为什么我又写作了第二版呢?原因有几个方面。第一,虽然第一版未曾涉及的进程间通信、文件系统等内容在许多书中都有讲解,但阅读的时候还是感觉有语焉不详的通病,作者本人可能很清楚原委,但写得太简略,以至于读者看来未必清晰。第二,我自己想把这个圈画圆。第一版的书虽然完成了它的使命,但毕竟到书的结尾,读者看到的不是一个真正的操作系统,它没有文件系统,没有内存管理,什么也干不了。在第二版中,你将会看到,你已经可以通过交叉编译的方式为我们的实验性 OS编写应用程序了,也就是说,它已经具备操作系统的基本功能,虽然仍然极其简陋,但第一个圈,毕竟是已经圆起来了。第三,实践类的操作系统书籍还是太少了,以至于你要想看看别人是怎么做的,除了读以《操作系统:设计与实现》为代表的极少数书籍之外,就是一头扎进源代码中,而结果有时相当令人气馁。我自己也气馁过,所以我在第二版中,仍然试图把话说细一点,把自己的经验拿出来分享。而且我选择我能想到的最精简的设计,以便让读者不至于陷入太多细节而无法看到全貌。我想这是本书可能具有的价值所在──简化的易懂的设计,还有尽量详细的文字。   在这一版中,内容被划分成上下两篇。上篇基本上是第一版的修订,只是做了一个调整,那便是在兼顾 Windows和Linux两方面用户的基础上,默认在Linux下建立开发环境来编写我们的操作系统。至于这样做的原因,在本书第 2章有比较详细的说明。当然,开发环境毕竟是第二位的,书中讲述的内容以及涉及的代码跟第一版都是一致的。本书的下篇全部都是新鲜内容,主要是增加了进程间通信、文件系统和内存管理。跟第一版的做法相同,下篇仍然不仅关注结果,更加致力于将形成一个结果的过程呈现出来。与此同时,由于本书旨在分享和引路,所以尽可能地简化了设计,以便将最重要的部分凸显出来。读者将看到,一个操作系统的文件系统和内存管理可以简陋到什么程度。简陋不是缺点,对于我们初学者而言,正是需要从简陋入手。换言之,如果你已经对实现一个操作系统有了一定的经验,那么这本书可能不适合你。这本书适合从来没有编写过操作系统的初学者。   本书的排版是我用L ATEX自己完成的。在排版中我花了一些工夫,因为我希望读者购买的首先是一本易于阅读且赏心悦目的书,其次才是编写操作系统的方法。另外,书中列出的代码均由我自己编写的程序自动嵌入L ATEX源文件,从而严格保证书和光盘的一致性,读者可以根据文件名和行号方便地找到光盘中   代码的准确位置。   此外,在第二版中还有一些小的变化。首先是操作系统的名字改变了,原因在于虽然我们的试验性   OS从前辈们那里借鉴了很多东西,但其各个部分的设计(比如文件系统和内存管理)往往有其独特之处,所以我将原先的 Tinix(本意为 TryMinix)改成了新名字Orange ’S(这个名字来自于我的妻子 ,),以表示它们的不同。另外,书中的代码风格,有些地方也做了调整。   我想,虽然第二版有着这样那样的变化,但有一点没有变,那就是本书试图将我在编写自己操作系统的过程中的经验尽可能地告诉读者,同时尽可能将我当初的思路和编码过程呈现出来。很可能读者比我更聪明,有更好的解决问题的方法,但无论如何,我认为我自己的经验可以为读者所借鉴。如果真是如   此,我将会非常欣慰。   在第二版的编写过程中,我同样要感谢许多人。感谢我的父母和爷爷对我的爱,并希望爷爷不要为我担心,写书是件辛苦的事,但同时也使我收获良多。爸爸在第二版的最后阶段帮我订正文字,这本书里有你的功劳。我要感谢博文视点的各位朋友,感谢郭老师的理解和支持,感谢李玲的辛勤工作,感谢江立和李冰,你们的高效让我非常钦佩。我还要感谢孟岩老师,你给我的鼓励我一直记在心里。我要感谢我的挚友郭洪桥,不仅仅因为你在技术上给我的帮助,更加因为你在精神上给我的支持。感谢我的同事和朋友张会昌,你在技术上的广度和深度总令我钦佩。另外,在第一版中帮助我的人,我要再次谢谢你们,因为没有第一版,也就没有第二版。   在所有人中我最应该感谢和最想感谢的,是我的妻子黄丹红,感谢你给我的所有建议,还有你帮我画的图。尤其是,当这本书在我预想的时间内没有完成的时候,当我遇到困难迟迟不能解决的时候,你总在一旁给我鼓励,在你那里,我从来都能感觉到一种温暖,我深知,如果没有你的支持,我无法坚持下来将书写完。谢谢你,这本书同样属于你。   跟第一版相比,这本书涉及的内容触及操作系统设计的更多方面,而由于笔者的水平实在有限,难免有纰漏甚至错误。如果读者有任何的问题、意见或建议,请登录http://www.osfromscratch.org,让我们共同探讨,共同进步。   本书导读   这本书适合谁   本书是一本操作系统实践的技术书籍。对于操作系统技术感兴趣,想要亲身体验编写操作系统过程的实践主义者,以及Minix、Linux源代码爱好者,都可以在本书中得到实践中所需的知识和思路。   本书以“动手写”为指导思想,只要是跟“动手写”操作系统有关的知识,都作为介绍对象加以讨论,所以,从开发环境的搭建,到保护模式,再到IBMPC中有关芯片的知识,最后到操作系统本身的设计实现,都能在本文中找到相应介绍。所以如果你也想亲身实践的话,本书可以省去你在书店和互联网寻找相应资料的过程,使你的学习过程事半功倍。在读完本书后,你不但可以获得对于操作系统初步的感性认识,并且对 IBMPC的接口、IA架构之保护模式,以及操作系统整体上的框架都将会有一定程度的了解。   笔者相信,当你读完本书之后,如果再读那些纯理论性的操作系统书籍,所获得的体验将会完全不同,因为那些对你而言不再是海市蜃楼。   对于想阅读 Linux源代码的操作系统爱好者,本书可以提供阅读前所必要的知识储备,而这些知识储备不但在本书中有完整的涉及,而且在很多 Linux书籍中是没有提到的。   特别要提到的是,对于想通过阅读 Andrew S. Tanenbaum和 Albert S. Woodhull的《操作系统:设计与实现》来学习操作系统的读者,本书尤其适合作为你的引路书籍,因为它翔实地介绍了初学者入门时所必需的知识积累,而这些知识在《操作系统:设计与实现》一书中是没有涉及的,笔者本人是把这本书作为写操作系统的主要参考书籍之一,所以在本书中对它多有借鉴。   你需要什么技术基础   在本书中所用到的计算机语言只有两种:汇编和 C语言。所以只要你具备汇编和 C语言的经验,就可以阅读本书。除对操作系统常识性的了解(比如知道中断、进程等概念)之外,本书不假定读者具备其他任何经验。   如果你学习过操作系统的理论课程,你会发现本书是对于理论的吻合和补充。它是从实践的角度为你展现一幅操作系统画面。   书中涉及了 Intel CPU保护模式、Linux命令等内容,到时候会有尽可能清晰的讲解,如果笔者认为某些内容可以通过其他教材系统学习,会在书中加以说明。   另外,本书只涉及 Intel x86平台。   统一思想——让我们在这些方面达成共识   道篇   让我们有效而愉快地学习   你大概依然记得在你亲自敲出第一个“Hello world”程序并运行成功时的喜悦,那样的成就感助燃了你对编写程序浓厚的兴趣。随后你不断地学习,每学到新的语法都迫不及待地在计算机上调试运行,在调试的过程中克服困难,学到新知,并获得新的成就感。   可现在请你设想一下,假如课程不是这样的安排,而是先试图告诉你所有的语法,中间没有任何实践的机会,试问这样的课程你能接受吗?我猜你唯一的感受将是索然寡味。   原因何在?只是因为你不再有因为不断实践而获得的源源不断的成就感。而成就感是学习过程中快乐的源泉,没有了成就感,学习的愉快程度将大打折扣,效果于是也将变得不容乐观。   每个人都希望有效而且愉快的学习过程,可不幸的是,我们见到的操作系统课程十之八九令我们失望,作者喋喋不休地讲述着进程管理存储管理I/O控制调度算法,可我们到头来也没有一点的感性认识。我们好像已经理解却又好像一无所知。很明显,没有成就感,一点也没有。笔者痛恨这样的学习过程,也决不会重蹈这样的覆辙,让读者获得成就感将是本书的灵魂。   其实这本书完全可以称作一本回忆录,记载了笔者从开始不知道保护模式为何物到最终形成一个小小   OS的过程,这样的回忆录性质保证了章节的安排完全遵从操作的时间顺序,于是也就保证了每一步的可操作性,毫无疑问,顺着这样的思路走下来,每一章的成果都需要努力但又尽在眼前,步步为营是我   们的战术,成就感是我们的宗旨。   我们将从二十行代码开始,让我们最简单的操作系统婴儿慢慢长大,变成一个翩翩少年,而其中的每一步,你都可以在书中的指导下自己完成,不仅仅是看到,而是自己做到!你将在不断的实践中获得不断的成就感,笔者真心希望在阅读本书的过程中,你的学习过程可以变得愉快而有效。   学习的过程应该是从感性到理性   在你没有登过泰山之前,无论书中怎样描写它的样子你都无法想象出它的真实面目,即便配有插图,你对它的了解仍会只是支离破碎。毫无疑问,一千本对泰山描述的书都比不上你一次登山的经历。文学家的描述可能是华丽而优美的,可这样的描述最终产生的效果可能是你非去亲自登泰山不可。反过来想呢,假如你已经登过泰山,这样的经历产生的效果会是你想读尽天下描述泰山的书而后快吗?可能事实恰恰相反,你可能再也不想去看那些文字描述。   是啊,再好的讲述,又哪比得上亲身的体验?人们的认知规律本来如此,有了感性的认识,才能上升为理性的理论。反其道而行之只能是事倍功半。   如果操作系统是一座这样的大山,本书愿做你的导游,引领你进入它的门径。传统的操作系统书籍仅仅是给你讲述这座大山的故事,你只是在听讲,并没有身临其境,而随着这本书亲身体验,则好像置身于山门之内,你不但可以看见眼前的每一个细节,更是具有了走完整座大山的信心。   值得说明的是,本书旨在引路,不会带领你走完整座大山,但是有兴趣的读者完全可以在本书最终形成的框架的基础上容易地实现其他操作系统书籍中讲到的各种原理和算法,从而对操作系统有个从感性到理性的清醒认识。   暂时的错误并不可怕   当我们对一件事情的全貌没有很好理解的时候,很可能会对某一部分产生理解上的误差,这就是所谓的断章取义。很多时候断章取义是难免的,但是,在不断学习的过程中,我们会逐渐看到更多,了解更多,对原先事物的认识也会变得深刻甚至不同。   对于操作系统这样复杂的东西来说,要想了解所有的细节无疑是非常困难的,所以在实践的过程中,可能在很多地方,会有一些误解发生。这都没有关系,随着了解的深入,这些误解总会得到澄清,到时你会发现,自己对某一方面已经非常熟悉了,这时的成就感,一定会让你感到非常愉悦。   本书内容的安排遵从的是代码编写的时间顺序,它更像是一本开发日记,所以在书中一些中间过程不完美的产物被有意保留了下来,并会在以后的章节中对它们进行修改和完善,因为笔者认为,一些精妙的东西背后,一定隐藏着很多中间的产物,一个伟大的发现在很多情况下可能不是天才们刹那间的灵光一闪,背后也一定有着我们没有看到的不伟大甚至是谬误。笔者很想追寻前辈们的脚步,重寻他们当日的足迹。做到这一点无疑很难,但即便无法做到,只要能引起读者的一点思索,也是本书莫大的幸事。   挡住了去路的,往往不是大树,而是小藤   如果不是亲身去做,你可能永远都不知道,困难是什么。   就好像你买了一台功能超全的微波炉回家,研究完了整本说明书,踌躇满志想要烹饪的时候,却突然发现家里的油盐已经用完。而当时已经是晚上十一点,所有的商店都已经关门,你气急败坏,简直想摸起铁勺砸向无辜的微波炉。   研究说明书是没有错的,但是在没开始之前,你永远都想不到让你无法烹饪的原因居然是十块钱一瓶的油和一块钱一袋的更加微不足道的盐。你还以为困难是微波炉面板上密密麻麻的控制键盘。   其实做其他事情也是一样的,比如写一个操作系统,即便一个很小的可能受理论家们讥笑的操作系统雏形,仍然可能遇到一大堆你没有想过的问题,而这些问题在传统的操作系统书籍中根本没有提到。所以唯一的办法,便是亲自去做,只有实践了,才知道是怎么回事。   术篇   用到什么再学什么   我们不是在考试,我们只是在为了自己的志趣而努力,所以就让我们忠于自己的喜好吧,不必为了考试而看完所有的章节,无论那是多么的乏味。让我们马上投入实践,遇到问题再图解决的办法。笔者非常推崇这样的学习方法:   实践 →遇到问题 →解决问题 →再实践   因为我们知道我们为什么学习,所以我们才会非常投入;由于我们知道我们的目标是解决什么问题,所以我们才会非常专注;由于我们在实践中学习,所以我们才会非常高效。而最有趣的是,最终你会发现你并没有因为选择这样的学习方法而少学到什么,相反,你会发现你用更少的时间学到更多的东西,并且格外的扎实。   只要用心,就没有学不会的东西   笔者还清楚地记得刚刚下载完 Intel Architecture Software Developer Manual那三个可怕的 PDF文件时的心情,那时心里暗暗嘀咕,什么时候才能把这些东西读懂啊!可是突然有一天,当这些东西真的已经被基本读完的时候,我想起当初的畏惧,时间其实并没有过去多少。   所有的道理都是相通的,没有什么真正可怕,尤其是,我们所做的并非创造性的工作,所有的问题前人都曾经解决,所以我们更是无所畏惧,更何况我们不仅有书店,而且有互联网,动动手脚就能找到需要的资料,我们只要认真研究就够了。   所以当遇到困难时,请静下心来,慢慢研究,因为只要用心,就没有学不会的东西。   适当地囫囵吞枣   如果囫囵吞枣仅仅是学习的一个过程而非终点,那么它并不一定就是坏事。大家都应该听说过鲁迅先生学习英语的故事,他建议在阅读的过程中遇到不懂的内容可以忽略,等到过一段时间之后,这些问题会自然解决。   在本书中,有时候可能先列出一段代码,告诉你它能完成什么,这时你也可以大致读过,因为下面会有对它详细的解释。第一遍读它的时候,你只要了解大概就够了。    本书的原则   1.宁可啰嗦一点,也不肯漏掉细节   在书中的有些地方,你可能觉得有些很“简单”的问题都被列了出来,甚至显得有些啰嗦,但笔者宁可让内容写得啰嗦点,因为笔者自己在读书的时候有一个体验,就是有时候一个问题怎么也想不通,经过很长时间终于弄明白的时候才发现原来是那么“简单”。可能作者认为它足够简单以至于可以跳过不提,但读者未必那么幸运一下子就弄清楚。   不过本书到后面的章节,如果涉及的细节是前面章节提到过的,就有意地略过了。举个非常简单的例子,开始时本书会提醒读者增加一个源文件之后不要忘记修改Makefile,到后来就假定读者已经熟悉了这个步骤,可能就不再提及了。   2.努力做到平易近人   笔者更喜欢把本书称作一本笔记或者学习日志,不仅仅是因为它基本是真实的学习过程的再现,而且笔者不想让它有任何居高临下甚至是晦涩神秘的感觉。如果有一个地方你觉得书中没有说清楚以至于你没有弄明白,请你告诉我,我会在以后做出改进。 3.代码注重可读性但不注重效率   本书的代码力求简单易懂,在此过程中很少考虑运行的效率。一方面因为书中的代码仅仅供学习之用,暂时并不考虑实际用途;另一方面笔者认为当我们对操作系统足够了解之后再考虑效率的问题也不迟。   本书附带光盘说明   本书附带光盘中有本书用到的所有源代码。值得一提的是,其中不止包含完整的操作系统代码,还包含各个步骤的中间产物。换句话说,开发中每一步骤的代码,都可在光盘中单独文件夹中找到。举例说明,书的开篇介绍引导扇区,读者在相应文件夹中就只看到引导扇区的代码;第 9章介绍文件系统,在相应文件夹中就不会包含第 10章内存管理的代码。在任何一个步骤对应的文件夹中,都包含一个完整可编译运行的代码树,以方便读者试验之用。这样在学习的任何一个阶段,读者都可彻底了解阶段性成果,且不必担心受到自己还未学习的内容的影响,从而使学习不留死角。   在书的正文中引用的代码会标注出出自哪个文件。以“chapter5/b/bar.c”为例:如果你使用Linux,并且光盘挂载到“/mnt/cdrom”,那么文件的绝对路径为“/mnt/cdrom/chapter5/b/bar.c”;如果你使用Windows,并且光盘是 X:盘,那么文件的绝对路径为“X:nchapter5nbnbar.c”。 目 录   上 篇   第1章 马上动手写一个最小的“操作系统” 2   1.1 准备工作 2   1.2 十分钟完成的操作系统 3   1.3 引导扇区 4   1.4 代码解释 4   1.5 水面下的冰山 6   1.6 回顾 7   第2章 搭建你的工作环境 8   2.1 虚拟计算机Bochs 8   2.1.1 Bochs初体验 8   2.1.2 Bochs的安装 9   2.1.3 Bochs的使用 10   2.1.4 用Bochs调试操作系统 12   2.2 QEMU 15   2.3 平台之争:Windows还是*nix 16   2.4 GNU/Linux下的开发环境 20   2.5 Windows下的开发环境 22   2.6 总结 23   第3章 保护模式(Protect Mode) 25   3.1 认识保护模式 25   3.1.1 保护模式的运行环境 29   3.1.2 GDT(Global Descriptor Table) 31   3.1.3 实模式到保护模式,不一般的jmp 33   3.1.4 描述符属性 35   3.2 保护模式进阶 38   3.2.1 海阔凭鱼跃 38   3.2.2 LDT(Local Descriptor Table) 44   3.2.3 特权级概述 48   3.2.4 特权级转移 51   3.2.5 关于“保护”二字的一点思考 65   3.3 页式存储 65   3.3.1 分页机制概述 66   3.3.2 编写代码启动分页机制 67   3.3.3 PDE和PTE 68   3.3.4 cr3 71   3.3.5 回头看代码 72   3.3.6 克勤克俭用内存 73   3.3.7 进一步体会分页机制 81   3.4 中断和异常 87   3.4.1 中断和异常机制 87   3.4.2 外部中断 90   3.4.3 编程操作8259A 91   3.4.4 建立IDT 94   3.4.5 实现一个中断 95   3.4.6 时钟中断试验 96   3.4.7 几点额外说明 98   3.5 保护模式下的I/O 100   3.5.1 IOPL 100   3.5.2 I/O许可位图(I/O Permission Bitmap) 100   3.6 保护模式小结 101   第4章 让操作系统走进保护模式 102   4.1 突破512字节的限制 102   4.1.1 FAT12 103   4.1.2 DOS可以识别的引导盘 108   4.1.3 一个最简单的Loader 108   4.1.4 加载Loader入内存 109   4.1.5 向Loader交出控制权 116   4.1.6 整理boot.asm 116   4.2 保护模式下的“操作系统” 117   第5章 内核雏形 119   5.1 在Linux下用汇编写Hello World 119   5.2 再进一步,汇编和C同步使用 120   5.3 ELF(Executable and Linkable Format) 123   5.4 从Loader到内核 127   5.4.1 用Loader加载ELF 127   5.4.2 跳入保护模式 131   5.4.3 重新放置内核 137   5.4.4 向内核交出控制权 142   5.5 扩充内核 143   5.5.1 切换堆栈和GDT 144   5.5.2 整理我们的文件夹 148   5.5.3 Makefile 149   5.5.4 添加中断处理 155   5.5.5 两点说明 168   5.6 小结 169   第6章 进程 171   6.1 迟到的进程 171   6.2 概述 171   6.2.1 进程介绍 172   6.2.2 未雨绸缪——形成进程的必要考虑 172   6.2.3 参考的代码 173   6.3 最简单的进程 174   6.3.1 简单进程的关键技术预测 175   6.3.2 第一步——ring0→ring1 178   6.3.3 第二步——丰富中断处理程序 189   6.4 多进程 200   6.4.1 添加一个进程体 200   6.4.2 相关的变量和宏 200   6.4.3 进程表初始化代码扩充 202   6.4.4 LDT 203   6.4.5 修改中断处理程序 203   6.4.6 添加一个任务的步骤总结 206   6.4.7 号外:Minix的中断处理 207   6.4.8 代码回顾与整理 212   6.5 系统调用 220   6.5.1 实现一个简单的系统调用 222   6.5.2 get_ticks的应用 227   6.6 进程调度 232   6.6.1 避免对称——进程的节奏感 232   6.6.2 优先级调度总结 240   第7章 输入/输出系统 242   7.1 键盘 242   7.1.1 从中断开始——键盘初体验 242   7.1.2 AT、PS/2键盘 243   7.1.3 键盘敲击的过程 244   7.1.4 用数组表示扫描码 248   7.1.5 键盘输入缓冲区 251   7.1.6 用新加的任务处理键盘操作 253   7.1.7 解析扫描码 254   7.2 显示器 263   7.2.1 初识TTY 264   7.2.2 基本概念 264   7.2.3 寄存器 267   7.3 TTY任务 270   7.3.1 TTY任务框架的搭建 272   7.3.2 多控制台 277   7.3.3 完善键盘处理 281   7.3.4 TTY任务总结 288   7.4 区分任务和用户进程 289   7.5 printf 291   7.5.1 为进程指定TTY 292   7.5.2 printf()的实现 292   7.5.3 系统调用write() 294   7.5.4 使用printf() 296   下 篇   第8章 进程间通信 300   8.1 微内核还是宏内核 300   8.1.1 Linux的系统调用 302   8.1.2 Minix的系统调用 303   8.1.3 我们的选择 305   8.2 IPC 306   8.3 实现IPC 306   8.3.1 assert()和panic() 309   8.3.2 msg_send()和msg_receive() 313   8.3.3 增加消息机制之后的进程调度 321   8.4 使用IPC来替换系统调用get_ticks 322   8.5 总结 324   第9章 文件系统 325   9.1 硬盘简介 325   9.2 硬盘操作的I/O 端口 326   9.3 硬盘驱动程序 327   9.4 文件系统 337   9.5 硬盘分区表 338   9.6 设备号 344   9.7 用代码遍历所有分区 347   9.8 完善硬盘驱动程序 352   9.9 在硬盘上制作一个文件系统 355   9.9.1 文件系统涉及的数据结构 356   9.9.2 编码建立文件系统 358   9.10 创建文件 366   9.10.1 Linux下的文件操作 366   9.10.2 文件描述符(file descriptor) 367   9.10.3 open() 369   9.11 创建文件所涉及的其他函数 377   9.11.1 strip_path() 377   9.11.2 search_file() 378   9.11.3 get_inode()和sync_inode() 379   9.11.4 init_fs() 381   9.11.5 read_super_block()和get_super_block() 382   9.12 关闭文件 383   9.13 查看已创建的文件 384   9.14 打开文件 386   9.15 读写文件 387   9.16 测试文件读写 390   9.17 文件系统调试 393   9.18 删除文件 395   9.19 插曲:奇怪的异常 401   9.20 为文件系统添加系统调用的步骤 403   9.21 将TTY纳入文件系统 404   9.22 改造printf 411   9.23 总结 413   第10章 内存管理 414   10.1 fork 414   10.1.1 认识fork 414   10.1.2 fork前要做的工作(为fork所做的准备) 417   10.1.3 fork()库函数 421   10.1.4 MM 421   10.1.5 运行 427   10.2 exit和wait 427   10.3 exec 432   10.3.1 认识exec 433   10.3.2 为自己的操作系统编写应用程序 434   10.3.3 “安装”应用程序 436   10.3.4 实现exec 442   10.4 简单的shell 447   10.5 总结 449   第11章 尾声 451   11.1 让mkfs()只执行一次 451   11.2 从硬盘引导 455   11.2.1 编写硬盘引导扇区和硬盘版loader 455   11.2.2 “安装”hdboot.bin和hdldr.bin 461   11.2.3 grub 461   11.2.4 小结 463   11.3 将OS安装到真实的计算机 465   11.3.1 准备工作 465   11.3.2 安装Linux 466   11.3.3 编译源代码 466   11.3.4 开始安装 467   11.4 总结 467   参考文献 470 解密《一个操作系统的实现》这本书 5 月 18 日见到了《 Orange'S :一个操作系统的实现》的样书,多少有些激动。想一想前一版本《自己动手写操作系统》是那么畅销,这一本一定不能含糊。整个出版过程我能看到作者于渊为此付出的努力,还在自己排版的过程有深入体会,通过于渊的讲座也让博文视点的员工分享到他在排版过程中的很多心得。 应该有几万个朋友读过《自己动手写操作系统》了,本书的第 2 版《 Orange'S :一个操作系统的实现》出来肯定有非常多的朋友想问,这两本书到底有何区别呢?就此博文视点对本书作者于渊进行了简单的采访。 * 提问:《 Orange'S :一个操作系统的实现》与《自己动手写操作系统》明显区别在哪些方面? * 于渊:作为《自己动手写操作系统》(以下简称《自》)的第二版,《 Orange'S :一个操作系统的实现》(以下简称“新版”)主要有以下变化: 1. 书中示例操作系统的名字改为 Orange'S 2. 书名改为《 Orange'S :一个操作系统的实现》 3. 增加了有关 IPC 、 FS 、 MM 等内容 4. 将默认开发平台改为 GNU/Linux ,同时兼顾 Windows 5. 更改了排版工具,并使用技术手段增加书的可读性,比如代码行号的运用 6. 建立专门网站以服务读者 7. 建立专门讨论区供读者交流 读过《自己动手写操作系统》的读者一定知道,其中默认使用 Windows 作为开发平台,同时使用虚拟机来编译及运行自己的 OS ,在新版中这一点发生了变化(如上述第 4 条所述),具体的变化原因在书中第二章有详细的叙述。虽然开发平台是第二位的事情,但书中的默认平台却不免影响到叙述细节,所以,如果读者基于自己的原因坚持在 Windows 上开发(可能的原因或许有对 Linux 不熟悉、需要边开发操作系统边登录某些网上银行等等),则可能对读到的内容进行一点点额外加工。当然,所需的额外加工是少量的,而且在第二章中也有专门的文字介绍如何在两种平台下搭建工作环境。此外,如果读者不介意花钱,还可以同时购买《自己动手写操作系统》和新版,相互参照阅读。 * 提问:《 Orange'S :一个操作系统的实现》与《自己动手写操作系统》相比是否有所增加吗?增加了多少内容量呢? 于渊:新版的内容是有增加的,新增文字约占整本书的三分之一,《 Orange'S :一个操作系统的实现》新增代码则是《自己动手写操作系统》中代码的数倍。这些新增的内容,读者只能从新版中获得。目前并未有将新增内容单独成书的打算,所以读者即便仅想阅读第八章以后的内容,也需要购买整本《 Orange'S :一个操作系统的实现》。已经购买了《自己动手写操作系统》的读者可能觉得有点浪费,但事实并不如此,因为《自己动手写操作系统》的内容经过了重新排版、修订和编辑(比如代码格式进行了重排,更方便与光盘中的文件对照阅读,以及其中所有的矢量图都用 pgf/TikZ 重新绘制等)笔者倾注的心血使得新版的感官已经大为不同,读者一看便知。 * 提问:在《自己动手写操作系统》大卖的时候,您是否想过会有第二版出版呢? * 于渊:坦白讲,我在写作《自》的时候,并没有想过今天会有一个第二版。原因在于,我希望这本书是用来填补空白的,而不是重复去做别人已经做得很好的事情。所谓填补空白,具体说就是让像我一样的操作系统爱好者在读完本书之后,能够有信心去读其他比较流行的开源的操作系统代码,有能力从零开始自己动手写操作系统,而这个任务第一版已经完成了。 * 提问:那么为什么又写作了第二版呢? * 于渊:原因有几个方面。第一,虽然第一版未曾涉及的进程间通信、文件系统等内容在许多书中都有讲解,但阅读的时候还是感觉有语焉不详的通病,作者本人可能很清楚原委,但写得太简略,以至于读者看来未必清晰。第二,我自己想把这个圈画圆。第一版的书虽然完成了它的使命,但毕竟到书的结尾,读者看到的不是一个真正的操作系统,它没有文件系统,没有内存管理,什么也干不了。在第二版中,你将会看到,你已经可以通过交叉编译的方式为我们的实验性 OS 编写应用程序了,也就是说,它已经具备操作系统的基本功能,虽然仍然极其简陋,但第一个圈,毕竟是已经圆起来了。第三,实践类的操作系统书籍还是太少了,以至于你要想看看别人是怎么做的,除了读以《操作系统:设计与实现》为代表的极少数书籍之外,就是一头扎进源代码中,而结果有时相当令人气馁。我自己也气馁过,所以我在第二版中,仍然试图把话说细一点,把自己的经验拿出来分享。而且我选择我能想到的最精简的设计,以便让读者不至于陷入太多细节而无法看到全貌。我想这是本书可能具有的价值所在──简化的易懂的设计,还有尽量详细的文字。 * 提问:这本书为何不考虑用 WORD 排版? * 于渊:新版的排版是我用 LaTeX 自己完成的。在排版中我花了一些工夫,因为我希望读者购买的首先是一本易于阅读且赏心悦目的书,其次才是编写操作系统的方法。另外,书中列出的代码均由我自己编写的程序自动嵌入 LaTeX 源文件,从而严格保证书和光盘的一致性,读者可以根据文件名和行号方便地找到光盘中代码的准确位置。 * 提问:第二版还有哪些区别呢? Orange'S 这个名字很特别,有什么寓意吗? * 于渊:新版中还有一些小的变化。首先是操作系统的名字改变了,原因在于虽然我们的试验性 OS 从前辈们那里借鉴了很多东西,但其各个部分的设计(比如文件系统和内存管理)往往有其独特之处,所以我将原先的 Tinix (本意为 TryMinix )改成了新名字 Orange'S (这个名字来自于我的妻子),以表示它们的不同。另外,书中的代码风格,有些地方也做了调整。 新版中,原先的叙述风格都尽量地得以贯彻,而在表现形式上,新版用了更多心思,我相信读者能在其中发现这些特点:关注动手细节,探寻代码背后的故事,结果与过程兼顾,内容与形式并重。加上专门为本书建立的网站和讨论区,我相信读者能更容易地阅读,更轻松地学习。
内容简介   本书从只有二十行的引导扇区代码出发,一步一步地向读者呈现一个操作系统框架的完成过程。书中不仅关注代码本身,同时关注完成这些代码的思路和过程。本书不同于其他的理论型书籍,而是提供给读者一个动手实践的路线图。读者可以根据路线图逐步完成各部分的功能,从而避免了一开始就面对整个操作系统数万行代码时的迷茫和挫败感。书中讲解了大量在开发操作系统中需注意的细节问题,这些细节不仅能使读者更深刻地认识操作系统的核心原理,而且使整个开发过程少走弯路。本书分上下两篇,共11章。其中每一章都以前一章的工作成果为基础,实现一项新的功能。而在章的内部,一项大的功能被分解成许多小的步骤,通过完成每个小的步骤,读者可以不断获得阶段性的成果,从而让整个开发过程变得轻松并且有趣。   本书适合各类程序员、程序开发爱好者阅读,也可作为高等院校操作系统课程的实践参考书。 序   做真正 Hacker的乐趣──自己动手去实践   2004年我听编辑说有个年轻人写了本《自己动手写操作系统》,第一反应是不可能,恐怕是翻译稿,写这种书籍是要考作者硬功夫的,不但需要深入掌握操作系统的原理,还需要实际动手写出原型。   历史上的 Linux就是这么产生的,Linus Torvalds当时是一名赫尔辛基大学计算机科学系的二年级学生,经常要用自己的电脑去访问大学主机上的新闻组和邮件,为了方便读写和下载文件,他自己编写了磁盘驱动程序和文件系统,这成为了 Linux第一个内核的雏形。   我想中国有能力写出内核原型的程序员应该也有,但把这个题目写成一本书,感觉上不会有人愿意做这件事情,作者要花很多时间,加上主题比较硬,销售量不会太高,经济上回报有限。   但拿来文稿一看,整个编辑部大为惊艳,内容文笔俱佳,而且绝对原创,马上决定在《程序员》连载。2005年博文视点出版的第一版也广受好评。   不过有很多读者还是质疑:现在软件编程主要领域是框架和应用,还需要了解操作系统底层吗?   经过四年的磨练成长,于渊又拿出第二版的书稿《Orange'S:一个操作系统的实现》,这本书是属于真正 Hacker的。我虽然已经有多年不写代码了,但看这本书的时候,让我又重新感受到做程序员的乐趣:用代码建设属于自己的系统,让电脑听从自己的指令,对系统的每个部分都了如指掌。   黑客(hacker)实际是褒义词,维基百科的解释是喜欢用智力通过创造性方法来挑战脑力极限的人,特别是他们所感兴趣的领域,例如软件编程或电气工程。个人电脑、软件和互联网等划时代的产品都是黑客创造出来的,如苹果的 Apple电脑、微软的 Basic解释器、互联网的 Mosaic浏览器。   回答前面读者的质疑,学软件编程并不需要看这本书,想成为优秀程序员和黑客的朋友,我强烈建议你花时间来阅读这本书,并亲自动手实践。正如于渊在本书结尾中所说“我们写自己的操作系统是出于一种好奇,或者说一种求知欲。我希望这样不停地‘过把瘾’能让这种好奇不停地延续”。   好奇心是动力的源泉,追究问题的本质是优秀黑客的必备素质,只有充分掌握了系统原理,才能在技术上游刃有余,才能有真正的创新和发展。中国需要更多真正的黑客,也希望更多的程序员能享受属于黑客的创造乐趣。   蒋涛   2009年 4月 作者自序   本书是《自己动手写操作系统》的第二版,通过一个具体的实例向读者呈现一个操作系统雏形的实现过程。有关操作系统的书籍资料可以找到很多,但是关注如何帮助读者实现一个试验性操作系统的书籍却不多见,本书便是从一个简单的引导扇区开始,讲述一个操作系统成长的故事,以作读者参考之用。   本书面向实践,通过具体实例教读者开发自己的操作系统。书中的步骤遵循由小到大、由浅入深的顺序,跟随这些步骤,读者可以由一个最简单的引导扇区开始,逐渐完善代码,扩充功能,最后形成一个小的操作系统。   本书不仅介绍操作系统的各要素,同时涉及开发操作系统需要的各个方面,比如如何建立开发环境、如何调试以及如何在虚拟机中运行等。书中的实例操作系统采用IA32作为默认平台,所以保护模式也作为必备知识储备收入书中,而这是传统的操作系统实践书籍经常忽略的。总之,只要是开发自己的操作系统中需要的知识,书中都尽量涉及,以便于读者参考。   众所周知,一个成型的操作系统往往非常复杂。如果考虑到操作系统作为软硬件桥梁的特殊地位,那么它可能看上去比一般的软件系统更难理解,因为其核心部分往往包含许多直接针对CPU、内存和 I/O端口的操作,它们夹杂在一片代码汪洋之中,显得更加晦涩。   我们有许多源代码公开的操作系统,可供随时下载和阅读,看上去好像让实现一个供自己把玩的微型操作系统变得容易很多,但事实往往不尽人意,因为这些代码动辄上万甚至几十几百万行,而且细节之间经常互相关联,要理解它们着实不易。我们有许多容易得到的操作系统教程,但读来好像总觉得跟我们有隔膜,不亲近。造成这些的根本原因,在于学习者一开始就面对一个完整的操作系统,或者面对前辈们积累了几十年的一系列理论成果。而无论作者多么擅长写作,读者多么聪明,或者代码多么优秀,要一个初学者理清其中的头绪都将是非常困难的。   我并非在此危言耸听,因为这曾经是我的亲身体会。当然,如果只是为了考试,几本操作系统理论书籍就足够了,你不需要对细节那么清楚。但如果是出于兴趣呢?如果你是想编写自己的操作系统呢?你会发现理论书籍好像一下子变得无用武之地,你会发现任何一个细节上的理解错误都可能导致自己辛辛苦苦编写的代码运行异常甚至崩溃。   我经历过这一切!我曾经翻遍了一本《操作系统:设计与实现》,也没有找到实现一个操作系统应该从何处着手。并不是这些书不好,也不是前人的代码不优秀,而是作为一无所知的初学者,我们所不了解的不仅是高居庙堂的理论知识,还有让我们举步维艰的实践细节。   可能在这些教科书作者的眼里,操作的细节不属于课程的一部分,或者这些细节看上去太容易,根本不值一提,甚至作者认为这些属于所谓“经验”的一部分,约定俗成是由读者本人去摸索的。但是实际情况往往是,这些书中忽略掉的内容恰恰占去了一个初学者大部分的时间,甚至影响了学习的热情。   我至今仍记得当我开始编写自己的操作系统时所遭受的挫败感,那是一种不知道如何着手的无助的感觉。还好我坚持了下来,克服了各种困难,并完成了自己的操作系统雏形。   进而我想到,一定不只是我一个人对编写自己的操作系统怀有兴趣,也一定不只是我一个人在实践时遇到困难。或许我应该把自己的经历写下来,从而可以帮助跟我相似的后来者,就这样,我编写了本书的第一版,也就是《自己动手写操作系统》。我相信,如果你也对神奇的计算机世界充满好奇,并且希望通过自己编写操作系统的方式来了解背后发生的故事,那么你一定可以在这本书中得到一些帮助。而假如你真的因为我的书而重新燃起实践的热情,从而开始一段操作系统旅程,我将会感到非常高兴。   不过我得坦白,在写作《自己动手写操作系统》的时候,我并不敢期待它能引起多少反响,一方面因为操作系统并不是时尚的话题,另一方面我也是走在学习的路上,或许只是比读者早走了一小步而已。然而出乎我的意料,它面世后重印多次,甚至一度登上销量排行榜的榜首,这让我觉得它的确有一定的参考价值,我要借此机会感谢所有支持我的读者。   在我写作《自己动手写操作系统》的时候,并没有想过今天会有一个第二版。原因在于,我希望这本书是用来填补空白的,而不是重复去做别人已经做得很好的事情。所谓填补空白,具体说就是让像我一样的操作系统爱好者在读完本书之后,能够有信心去读其他比较流行的开源的操作系统代码,有能力从零开始自己动手写操作系统,而这个任务第一版已经完成了。   那么为什么我又写作了第二版呢?原因有几个方面。第一,虽然第一版未曾涉及的进程间通信、文件系统等内容在许多书中都有讲解,但阅读的时候还是感觉有语焉不详的通病,作者本人可能很清楚原委,但写得太简略,以至于读者看来未必清晰。第二,我自己想把这个圈画圆。第一版的书虽然完成了它的使命,但毕竟到书的结尾,读者看到的不是一个真正的操作系统,它没有文件系统,没有内存管理,什么也干不了。在第二版中,你将会看到,你已经可以通过交叉编译的方式为我们的实验性 OS编写应用程序了,也就是说,它已经具备操作系统的基本功能,虽然仍然极其简陋,但第一个圈,毕竟是已经圆起来了。第三,实践类的操作系统书籍还是太少了,以至于你要想看看别人是怎么做的,除了读以《操作系统:设计与实现》为代表的极少数书籍之外,就是一头扎进源代码中,而结果有时相当令人气馁。我自己也气馁过,所以我在第二版中,仍然试图把话说细一点,把自己的经验拿出来分享。而且我选择我能想到的最精简的设计,以便让读者不至于陷入太多细节而无法看到全貌。我想这是本书可能具有的价值所在──简化的易懂的设计,还有尽量详细的文字。   在这一版中,内容被划分成上下两篇。上篇基本上是第一版的修订,只是做了一个调整,那便是在兼顾 Windows和Linux两方面用户的基础上,默认在Linux下建立开发环境来编写我们的操作系统。至于这样做的原因,在本书第 2章有比较详细的说明。当然,开发环境毕竟是第二位的,书中讲述的内容以及涉及的代码跟第一版都是一致的。本书的下篇全部都是新鲜内容,主要是增加了进程间通信、文件系统和内存管理。跟第一版的做法相同,下篇仍然不仅关注结果,更加致力于将形成一个结果的过程呈现出来。与此同时,由于本书旨在分享和引路,所以尽可能地简化了设计,以便将最重要的部分凸显出来。读者将看到,一个操作系统的文件系统和内存管理可以简陋到什么程度。简陋不是缺点,对于我们初学者而言,正是需要从简陋入手。换言之,如果你已经对实现一个操作系统有了一定的经验,那么这本书可能不适合你。这本书适合从来没有编写过操作系统的初学者。   本书的排版是我用L ATEX自己完成的。在排版中我花了一些工夫,因为我希望读者购买的首先是一本易于阅读且赏心悦目的书,其次才是编写操作系统的方法。另外,书中列出的代码均由我自己编写的程序自动嵌入L ATEX源文件,从而严格保证书和光盘的一致性,读者可以根据文件名和行号方便地找到光盘中   代码的准确位置。   此外,在第二版中还有一些小的变化。首先是操作系统的名字改变了,原因在于虽然我们的试验性   OS从前辈们那里借鉴了很多东西,但其各个部分的设计(比如文件系统和内存管理)往往有其独特之处,所以我将原先的 Tinix(本意为 TryMinix)改成了新名字Orange ’S(这个名字来自于我的妻子 ,),以表示它们的不同。另外,书中的代码风格,有些地方也做了调整。   我想,虽然第二版有着这样那样的变化,但有一点没有变,那就是本书试图将我在编写自己操作系统的过程中的经验尽可能地告诉读者,同时尽可能将我当初的思路和编码过程呈现出来。很可能读者比我更聪明,有更好的解决问题的方法,但无论如何,我认为我自己的经验可以为读者所借鉴。如果真是如   此,我将会非常欣慰。   在第二版的编写过程中,我同样要感谢许多人。感谢我的父母和爷爷对我的爱,并希望爷爷不要为我担心,写书是件辛苦的事,但同时也使我收获良多。爸爸在第二版的最后阶段帮我订正文字,这本书里有你的功劳。我要感谢博文视点的各位朋友,感谢郭老师的理解和支持,感谢李玲的辛勤工作,感谢江立和李冰,你们的高效让我非常钦佩。我还要感谢孟岩老师,你给我的鼓励我一直记在心里。我要感谢我的挚友郭洪桥,不仅仅因为你在技术上给我的帮助,更加因为你在精神上给我的支持。感谢我的同事和朋友张会昌,你在技术上的广度和深度总令我钦佩。另外,在第一版中帮助我的人,我要再次谢谢你们,因为没有第一版,也就没有第二版。   在所有人中我最应该感谢和最想感谢的,是我的妻子黄丹红,感谢你给我的所有建议,还有你帮我画的图。尤其是,当这本书在我预想的时间内没有完成的时候,当我遇到困难迟迟不能解决的时候,你总在一旁给我鼓励,在你那里,我从来都能感觉到一种温暖,我深知,如果没有你的支持,我无法坚持下来将书写完。谢谢你,这本书同样属于你。   跟第一版相比,这本书涉及的内容触及操作系统设计的更多方面,而由于笔者的水平实在有限,难免有纰漏甚至错误。如果读者有任何的问题、意见或建议,请登录http://www.osfromscratch.org,让我们共同探讨,共同进步。   本书导读   这本书适合谁   本书是一本操作系统实践的技术书籍。对于操作系统技术感兴趣,想要亲身体验编写操作系统过程的实践主义者,以及Minix、Linux源代码爱好者,都可以在本书中得到实践中所需的知识和思路。   本书以“动手写”为指导思想,只要是跟“动手写”操作系统有关的知识,都作为介绍对象加以讨论,所以,从开发环境的搭建,到保护模式,再到IBMPC中有关芯片的知识,最后到操作系统本身的设计实现,都能在本文中找到相应介绍。所以如果你也想亲身实践的话,本书可以省去你在书店和互联网寻找相应资料的过程,使你的学习过程事半功倍。在读完本书后,你不但可以获得对于操作系统初步的感性认识,并且对 IBMPC的接口、IA架构之保护模式,以及操作系统整体上的框架都将会有一定程度的了解。   笔者相信,当你读完本书之后,如果再读那些纯理论性的操作系统书籍,所获得的体验将会完全不同,因为那些对你而言不再是海市蜃楼。   对于想阅读 Linux源代码的操作系统爱好者,本书可以提供阅读前所必要的知识储备,而这些知识储备不但在本书中有完整的涉及,而且在很多 Linux书籍中是没有提到的。   特别要提到的是,对于想通过阅读 Andrew S. Tanenbaum和 Albert S. Woodhull的《操作系统:设计与实现》来学习操作系统的读者,本书尤其适合作为你的引路书籍,因为它翔实地介绍了初学者入门时所必需的知识积累,而这些知识在《操作系统:设计与实现》一书中是没有涉及的,笔者本人是把这本书作为写操作系统的主要参考书籍之一,所以在本书中对它多有借鉴。   你需要什么技术基础   在本书中所用到的计算机语言只有两种:汇编和 C语言。所以只要你具备汇编和 C语言的经验,就可以阅读本书。除对操作系统常识性的了解(比如知道中断、进程等概念)之外,本书不假定读者具备其他任何经验。   如果你学习过操作系统的理论课程,你会发现本书是对于理论的吻合和补充。它是从实践的角度为你展现一幅操作系统画面。   书中涉及了 Intel CPU保护模式、Linux命令等内容,到时候会有尽可能清晰的讲解,如果笔者认为某些内容可以通过其他教材系统学习,会在书中加以说明。   另外,本书只涉及 Intel x86平台。   统一思想——让我们在这些方面达成共识   道篇   让我们有效而愉快地学习   你大概依然记得在你亲自敲出第一个“Hello world”程序并运行成功时的喜悦,那样的成就感助燃了你对编写程序浓厚的兴趣。随后你不断地学习,每学到新的语法都迫不及待地在计算机上调试运行,在调试的过程中克服困难,学到新知,并获得新的成就感。   可现在请你设想一下,假如课程不是这样的安排,而是先试图告诉你所有的语法,中间没有任何实践的机会,试问这样的课程你能接受吗?我猜你唯一的感受将是索然寡味。   原因何在?只是因为你不再有因为不断实践而获得的源源不断的成就感。而成就感是学习过程中快乐的源泉,没有了成就感,学习的愉快程度将大打折扣,效果于是也将变得不容乐观。   每个人都希望有效而且愉快的学习过程,可不幸的是,我们见到的操作系统课程十之八九令我们失望,作者喋喋不休地讲述着进程管理存储管理I/O控制调度算法,可我们到头来也没有一点的感性认识。我们好像已经理解却又好像一无所知。很明显,没有成就感,一点也没有。笔者痛恨这样的学习过程,也决不会重蹈这样的覆辙,让读者获得成就感将是本书的灵魂。   其实这本书完全可以称作一本回忆录,记载了笔者从开始不知道保护模式为何物到最终形成一个小小   OS的过程,这样的回忆录性质保证了章节的安排完全遵从操作的时间顺序,于是也就保证了每一步的可操作性,毫无疑问,顺着这样的思路走下来,每一章的成果都需要努力但又尽在眼前,步步为营是我   们的战术,成就感是我们的宗旨。   我们将从二十行代码开始,让我们最简单的操作系统婴儿慢慢长大,变成一个翩翩少年,而其中的每一步,你都可以在书中的指导下自己完成,不仅仅是看到,而是自己做到!你将在不断的实践中获得不断的成就感,笔者真心希望在阅读本书的过程中,你的学习过程可以变得愉快而有效。   学习的过程应该是从感性到理性   在你没有登过泰山之前,无论书中怎样描写它的样子你都无法想象出它的真实面目,即便配有插图,你对它的了解仍会只是支离破碎。毫无疑问,一千本对泰山描述的书都比不上你一次登山的经历。文学家的描述可能是华丽而优美的,可这样的描述最终产生的效果可能是你非去亲自登泰山不可。反过来想呢,假如你已经登过泰山,这样的经历产生的效果会是你想读尽天下描述泰山的书而后快吗?可能事实恰恰相反,你可能再也不想去看那些文字描述。   是啊,再好的讲述,又哪比得上亲身的体验?人们的认知规律本来如此,有了感性的认识,才能上升为理性的理论。反其道而行之只能是事倍功半。   如果操作系统是一座这样的大山,本书愿做你的导游,引领你进入它的门径。传统的操作系统书籍仅仅是给你讲述这座大山的故事,你只是在听讲,并没有身临其境,而随着这本书亲身体验,则好像置身于山门之内,你不但可以看见眼前的每一个细节,更是具有了走完整座大山的信心。   值得说明的是,本书旨在引路,不会带领你走完整座大山,但是有兴趣的读者完全可以在本书最终形成的框架的基础上容易地实现其他操作系统书籍中讲到的各种原理和算法,从而对操作系统有个从感性到理性的清醒认识。   暂时的错误并不可怕   当我们对一件事情的全貌没有很好理解的时候,很可能会对某一部分产生理解上的误差,这就是所谓的断章取义。很多时候断章取义是难免的,但是,在不断学习的过程中,我们会逐渐看到更多,了解更多,对原先事物的认识也会变得深刻甚至不同。   对于操作系统这样复杂的东西来说,要想了解所有的细节无疑是非常困难的,所以在实践的过程中,可能在很多地方,会有一些误解发生。这都没有关系,随着了解的深入,这些误解总会得到澄清,到时你会发现,自己对某一方面已经非常熟悉了,这时的成就感,一定会让你感到非常愉悦。   本书内容的安排遵从的是代码编写的时间顺序,它更像是一本开发日记,所以在书中一些中间过程不完美的产物被有意保留了下来,并会在以后的章节中对它们进行修改和完善,因为笔者认为,一些精妙的东西背后,一定隐藏着很多中间的产物,一个伟大的发现在很多情况下可能不是天才们刹那间的灵光一闪,背后也一定有着我们没有看到的不伟大甚至是谬误。笔者很想追寻前辈们的脚步,重寻他们当日的足迹。做到这一点无疑很难,但即便无法做到,只要能引起读者的一点思索,也是本书莫大的幸事。   挡住了去路的,往往不是大树,而是小藤   如果不是亲身去做,你可能永远都不知道,困难是什么。   就好像你买了一台功能超全的微波炉回家,研究完了整本说明书,踌躇满志想要烹饪的时候,却突然发现家里的油盐已经用完。而当时已经是晚上十一点,所有的商店都已经关门,你气急败坏,简直想摸起铁勺砸向无辜的微波炉。   研究说明书是没有错的,但是在没开始之前,你永远都想不到让你无法烹饪的原因居然是十块钱一瓶的油和一块钱一袋的更加微不足道的盐。你还以为困难是微波炉面板上密密麻麻的控制键盘。   其实做其他事情也是一样的,比如写一个操作系统,即便一个很小的可能受理论家们讥笑的操作系统雏形,仍然可能遇到一大堆你没有想过的问题,而这些问题在传统的操作系统书籍中根本没有提到。所以唯一的办法,便是亲自去做,只有实践了,才知道是怎么回事。   术篇   用到什么再学什么   我们不是在考试,我们只是在为了自己的志趣而努力,所以就让我们忠于自己的喜好吧,不必为了考试而看完所有的章节,无论那是多么的乏味。让我们马上投入实践,遇到问题再图解决的办法。笔者非常推崇这样的学习方法:   实践 →遇到问题 →解决问题 →再实践   因为我们知道我们为什么学习,所以我们才会非常投入;由于我们知道我们的目标是解决什么问题,所以我们才会非常专注;由于我们在实践中学习,所以我们才会非常高效。而最有趣的是,最终你会发现你并没有因为选择这样的学习方法而少学到什么,相反,你会发现你用更少的时间学到更多的东西,并且格外的扎实。   只要用心,就没有学不会的东西   笔者还清楚地记得刚刚下载完 Intel Architecture Software Developer Manual那三个可怕的 PDF文件时的心情,那时心里暗暗嘀咕,什么时候才能把这些东西读懂啊!可是突然有一天,当这些东西真的已经被基本读完的时候,我想起当初的畏惧,时间其实并没有过去多少。   所有的道理都是相通的,没有什么真正可怕,尤其是,我们所做的并非创造性的工作,所有的问题前人都曾经解决,所以我们更是无所畏惧,更何况我们不仅有书店,而且有互联网,动动手脚就能找到需要的资料,我们只要认真研究就够了。   所以当遇到困难时,请静下心来,慢慢研究,因为只要用心,就没有学不会的东西。   适当地囫囵吞枣   如果囫囵吞枣仅仅是学习的一个过程而非终点,那么它并不一定就是坏事。大家都应该听说过鲁迅先生学习英语的故事,他建议在阅读的过程中遇到不懂的内容可以忽略,等到过一段时间之后,这些问题会自然解决。   在本书中,有时候可能先列出一段代码,告诉你它能完成什么,这时你也可以大致读过,因为下面会有对它详细的解释。第一遍读它的时候,你只要了解大概就够了。    本书的原则   1.宁可啰嗦一点,也不肯漏掉细节   在书中的有些地方,你可能觉得有些很“简单”的问题都被列了出来,甚至显得有些啰嗦,但笔者宁可让内容写得啰嗦点,因为笔者自己在读书的时候有一个体验,就是有时候一个问题怎么也想不通,经过很长时间终于弄明白的时候才发现原来是那么“简单”。可能作者认为它足够简单以至于可以跳过不提,但读者未必那么幸运一下子就弄清楚。   不过本书到后面的章节,如果涉及的细节是前面章节提到过的,就有意地略过了。举个非常简单的例子,开始时本书会提醒读者增加一个源文件之后不要忘记修改Makefile,到后来就假定读者已经熟悉了这个步骤,可能就不再提及了。   2.努力做到平易近人   笔者更喜欢把本书称作一本笔记或者学习日志,不仅仅是因为它基本是真实的学习过程的再现,而且笔者不想让它有任何居高临下甚至是晦涩神秘的感觉。如果有一个地方你觉得书中没有说清楚以至于你没有弄明白,请你告诉我,我会在以后做出改进。 3.代码注重可读性但不注重效率   本书的代码力求简单易懂,在此过程中很少考虑运行的效率。一方面因为书中的代码仅仅供学习之用,暂时并不考虑实际用途;另一方面笔者认为当我们对操作系统足够了解之后再考虑效率的问题也不迟。   本书附带光盘说明   本书附带光盘中有本书用到的所有源代码。值得一提的是,其中不止包含完整的操作系统代码,还包含各个步骤的中间产物。换句话说,开发中每一步骤的代码,都可在光盘中单独文件夹中找到。举例说明,书的开篇介绍引导扇区,读者在相应文件夹中就只看到引导扇区的代码;第 9章介绍文件系统,在相应文件夹中就不会包含第 10章内存管理的代码。在任何一个步骤对应的文件夹中,都包含一个完整可编译运行的代码树,以方便读者试验之用。这样在学习的任何一个阶段,读者都可彻底了解阶段性成果,且不必担心受到自己还未学习的内容的影响,从而使学习不留死角。   在书的正文中引用的代码会标注出出自哪个文件。以“chapter5/b/bar.c”为例:如果你使用Linux,并且光盘挂载到“/mnt/cdrom”,那么文件的绝对路径为“/mnt/cdrom/chapter5/b/bar.c”;如果你使用Windows,并且光盘是 X:盘,那么文件的绝对路径为“X:nchapter5nbnbar.c”。 目 录   上 篇   第1章 马上动手写一个最小的“操作系统” 2   1.1 准备工作 2   1.2 十分钟完成的操作系统 3   1.3 引导扇区 4   1.4 代码解释 4   1.5 水面下的冰山 6   1.6 回顾 7   第2章 搭建你的工作环境 8   2.1 虚拟计算机Bochs 8   2.1.1 Bochs初体验 8   2.1.2 Bochs的安装 9   2.1.3 Bochs的使用 10   2.1.4 用Bochs调试操作系统 12   2.2 QEMU 15   2.3 平台之争:Windows还是*nix 16   2.4 GNU/Linux下的开发环境 20   2.5 Windows下的开发环境 22   2.6 总结 23   第3章 保护模式(Protect Mode) 25   3.1 认识保护模式 25   3.1.1 保护模式的运行环境 29   3.1.2 GDT(Global Descriptor Table) 31   3.1.3 实模式到保护模式,不一般的jmp 33   3.1.4 描述符属性 35   3.2 保护模式进阶 38   3.2.1 海阔凭鱼跃 38   3.2.2 LDT(Local Descriptor Table) 44   3.2.3 特权级概述 48   3.2.4 特权级转移 51   3.2.5 关于“保护”二字的一点思考 65   3.3 页式存储 65   3.3.1 分页机制概述 66   3.3.2 编写代码启动分页机制 67   3.3.3 PDE和PTE 68   3.3.4 cr3 71   3.3.5 回头看代码 72   3.3.6 克勤克俭用内存 73   3.3.7 进一步体会分页机制 81   3.4 中断和异常 87   3.4.1 中断和异常机制 87   3.4.2 外部中断 90   3.4.3 编程操作8259A 91   3.4.4 建立IDT 94   3.4.5 实现一个中断 95   3.4.6 时钟中断试验 96   3.4.7 几点额外说明 98   3.5 保护模式下的I/O 100   3.5.1 IOPL 100   3.5.2 I/O许可位图(I/O Permission Bitmap) 100   3.6 保护模式小结 101   第4章 让操作系统走进保护模式 102   4.1 突破512字节的限制 102   4.1.1 FAT12 103   4.1.2 DOS可以识别的引导盘 108   4.1.3 一个最简单的Loader 108   4.1.4 加载Loader入内存 109   4.1.5 向Loader交出控制权 116   4.1.6 整理boot.asm 116   4.2 保护模式下的“操作系统” 117   第5章 内核雏形 119   5.1 在Linux下用汇编写Hello World 119   5.2 再进一步,汇编和C同步使用 120   5.3 ELF(Executable and Linkable Format) 123   5.4 从Loader到内核 127   5.4.1 用Loader加载ELF 127   5.4.2 跳入保护模式 131   5.4.3 重新放置内核 137   5.4.4 向内核交出控制权 142   5.5 扩充内核 143   5.5.1 切换堆栈和GDT 144   5.5.2 整理我们的文件夹 148   5.5.3 Makefile 149   5.5.4 添加中断处理 155   5.5.5 两点说明 168   5.6 小结 169   第6章 进程 171   6.1 迟到的进程 171   6.2 概述 171   6.2.1 进程介绍 172   6.2.2 未雨绸缪——形成进程的必要考虑 172   6.2.3 参考的代码 173   6.3 最简单的进程 174   6.3.1 简单进程的关键技术预测 175   6.3.2 第一步——ring0→ring1 178   6.3.3 第二步——丰富中断处理程序 189   6.4 多进程 200   6.4.1 添加一个进程体 200   6.4.2 相关的变量和宏 200   6.4.3 进程表初始化代码扩充 202   6.4.4 LDT 203   6.4.5 修改中断处理程序 203   6.4.6 添加一个任务的步骤总结 206   6.4.7 号外:Minix的中断处理 207   6.4.8 代码回顾与整理 212   6.5 系统调用 220   6.5.1 实现一个简单的系统调用 222   6.5.2 get_ticks的应用 227   6.6 进程调度 232   6.6.1 避免对称——进程的节奏感 232   6.6.2 优先级调度总结 240   第7章 输入/输出系统 242   7.1 键盘 242   7.1.1 从中断开始——键盘初体验 242   7.1.2 AT、PS/2键盘 243   7.1.3 键盘敲击的过程 244   7.1.4 用数组表示扫描码 248   7.1.5 键盘输入缓冲区 251   7.1.6 用新加的任务处理键盘操作 253   7.1.7 解析扫描码 254   7.2 显示器 263   7.2.1 初识TTY 264   7.2.2 基本概念 264   7.2.3 寄存器 267   7.3 TTY任务 270   7.3.1 TTY任务框架的搭建 272   7.3.2 多控制台 277   7.3.3 完善键盘处理 281   7.3.4 TTY任务总结 288   7.4 区分任务和用户进程 289   7.5 printf 291   7.5.1 为进程指定TTY 292   7.5.2 printf()的实现 292   7.5.3 系统调用write() 294   7.5.4 使用printf() 296   下 篇   第8章 进程间通信 300   8.1 微内核还是宏内核 300   8.1.1 Linux的系统调用 302   8.1.2 Minix的系统调用 303   8.1.3 我们的选择 305   8.2 IPC 306   8.3 实现IPC 306   8.3.1 assert()和panic() 309   8.3.2 msg_send()和msg_receive() 313   8.3.3 增加消息机制之后的进程调度 321   8.4 使用IPC来替换系统调用get_ticks 322   8.5 总结 324   第9章 文件系统 325   9.1 硬盘简介 325   9.2 硬盘操作的I/O 端口 326   9.3 硬盘驱动程序 327   9.4 文件系统 337   9.5 硬盘分区表 338   9.6 设备号 344   9.7 用代码遍历所有分区 347   9.8 完善硬盘驱动程序 352   9.9 在硬盘上制作一个文件系统 355   9.9.1 文件系统涉及的数据结构 356   9.9.2 编码建立文件系统 358   9.10 创建文件 366   9.10.1 Linux下的文件操作 366   9.10.2 文件描述符(file descriptor) 367   9.10.3 open() 369   9.11 创建文件所涉及的其他函数 377   9.11.1 strip_path() 377   9.11.2 search_file() 378   9.11.3 get_inode()和sync_inode() 379   9.11.4 init_fs() 381   9.11.5 read_super_block()和get_super_block() 382   9.12 关闭文件 383   9.13 查看已创建的文件 384   9.14 打开文件 386   9.15 读写文件 387   9.16 测试文件读写 390   9.17 文件系统调试 393   9.18 删除文件 395   9.19 插曲:奇怪的异常 401   9.20 为文件系统添加系统调用的步骤 403   9.21 将TTY纳入文件系统 404   9.22 改造printf 411   9.23 总结 413   第10章 内存管理 414   10.1 fork 414   10.1.1 认识fork 414   10.1.2 fork前要做的工作(为fork所做的准备) 417   10.1.3 fork()库函数 421   10.1.4 MM 421   10.1.5 运行 427   10.2 exit和wait 427   10.3 exec 432   10.3.1 认识exec 433   10.3.2 为自己的操作系统编写应用程序 434   10.3.3 “安装”应用程序 436   10.3.4 实现exec 442   10.4 简单的shell 447   10.5 总结 449   第11章 尾声 451   11.1 让mkfs()只执行一次 451   11.2 从硬盘引导 455   11.2.1 编写硬盘引导扇区和硬盘版loader 455   11.2.2 “安装”hdboot.bin和hdldr.bin 461   11.2.3 grub 461   11.2.4 小结 463   11.3 将OS安装到真实的计算机 465   11.3.1 准备工作 465   11.3.2 安装Linux 466   11.3.3 编译源代码 466   11.3.4 开始安装 467   11.4 总结 467   参考文献 470 解密《一个操作系统的实现》这本书 5 月 18 日见到了《 Orange'S :一个操作系统的实现》的样书,多少有些激动。想一想前一版本《自己动手写操作系统》是那么畅销,这一本一定不能含糊。整个出版过程我能看到作者于渊为此付出的努力,还在自己排版的过程有深入体会,通过于渊的讲座也让博文视点的员工分享到他在排版过程中的很多心得。 应该有几万个朋友读过《自己动手写操作系统》了,本书的第 2 版《 Orange'S :一个操作系统的实现》出来肯定有非常多的朋友想问,这两本书到底有何区别呢?就此博文视点对本书作者于渊进行了简单的采访。 * 提问:《 Orange'S :一个操作系统的实现》与《自己动手写操作系统》明显区别在哪些方面? * 于渊:作为《自己动手写操作系统》(以下简称《自》)的第二版,《 Orange'S :一个操作系统的实现》(以下简称“新版”)主要有以下变化: 1. 书中示例操作系统的名字改为 Orange'S 2. 书名改为《 Orange'S :一个操作系统的实现》 3. 增加了有关 IPC 、 FS 、 MM 等内容 4. 将默认开发平台改为 GNU/Linux ,同时兼顾 Windows 5. 更改了排版工具,并使用技术手段增加书的可读性,比如代码行号的运用 6. 建立专门网站以服务读者 7. 建立专门讨论区供读者交流 读过《自己动手写操作系统》的读者一定知道,其中默认使用 Windows 作为开发平台,同时使用虚拟机来编译及运行自己的 OS ,在新版中这一点发生了变化(如上述第 4 条所述),具体的变化原因在书中第二章有详细的叙述。虽然开发平台是第二位的事情,但书中的默认平台却不免影响到叙述细节,所以,如果读者基于自己的原因坚持在 Windows 上开发(可能的原因或许有对 Linux 不熟悉、需要边开发操作系统边登录某些网上银行等等),则可能对读到的内容进行一点点额外加工。当然,所需的额外加工是少量的,而且在第二章中也有专门的文字介绍如何在两种平台下搭建工作环境。此外,如果读者不介意花钱,还可以同时购买《自己动手写操作系统》和新版,相互参照阅读。 * 提问:《 Orange'S :一个操作系统的实现》与《自己动手写操作系统》相比是否有所增加吗?增加了多少内容量呢? 于渊:新版的内容是有增加的,新增文字约占整本书的三分之一,《 Orange'S :一个操作系统的实现》新增代码则是《自己动手写操作系统》中代码的数倍。这些新增的内容,读者只能从新版中获得。目前并未有将新增内容单独成书的打算,所以读者即便仅想阅读第八章以后的内容,也需要购买整本《 Orange'S :一个操作系统的实现》。已经购买了《自己动手写操作系统》的读者可能觉得有点浪费,但事实并不如此,因为《自己动手写操作系统》的内容经过了重新排版、修订和编辑(比如代码格式进行了重排,更方便与光盘中的文件对照阅读,以及其中所有的矢量图都用 pgf/TikZ 重新绘制等)笔者倾注的心血使得新版的感官已经大为不同,读者一看便知。 * 提问:在《自己动手写操作系统》大卖的时候,您是否想过会有第二版出版呢? * 于渊:坦白讲,我在写作《自》的时候,并没有想过今天会有一个第二版。原因在于,我希望这本书是用来填补空白的,而不是重复去做别人已经做得很好的事情。所谓填补空白,具体说就是让像我一样的操作系统爱好者在读完本书之后,能够有信心去读其他比较流行的开源的操作系统代码,有能力从零开始自己动手写操作系统,而这个任务第一版已经完成了。 * 提问:那么为什么又写作了第二版呢? * 于渊:原因有几个方面。第一,虽然第一版未曾涉及的进程间通信、文件系统等内容在许多书中都有讲解,但阅读的时候还是感觉有语焉不详的通病,作者本人可能很清楚原委,但写得太简略,以至于读者看来未必清晰。第二,我自己想把这个圈画圆。第一版的书虽然完成了它的使命,但毕竟到书的结尾,读者看到的不是一个真正的操作系统,它没有文件系统,没有内存管理,什么也干不了。在第二版中,你将会看到,你已经可以通过交叉编译的方式为我们的实验性 OS 编写应用程序了,也就是说,它已经具备操作系统的基本功能,虽然仍然极其简陋,但第一个圈,毕竟是已经圆起来了。第三,实践类的操作系统书籍还是太少了,以至于你要想看看别人是怎么做的,除了读以《操作系统:设计与实现》为代表的极少数书籍之外,就是一头扎进源代码中,而结果有时相当令人气馁。我自己也气馁过,所以我在第二版中,仍然试图把话说细一点,把自己的经验拿出来分享。而且我选择我能想到的最精简的设计,以便让读者不至于陷入太多细节而无法看到全貌。我想这是本书可能具有的价值所在──简化的易懂的设计,还有尽量详细的文字。 * 提问:这本书为何不考虑用 WORD 排版? * 于渊:新版的排版是我用 LaTeX 自己完成的。在排版中我花了一些工夫,因为我希望读者购买的首先是一本易于阅读且赏心悦目的书,其次才是编写操作系统的方法。另外,书中列出的代码均由我自己编写的程序自动嵌入 LaTeX 源文件,从而严格保证书和光盘的一致性,读者可以根据文件名和行号方便地找到光盘中代码的准确位置。 * 提问:第二版还有哪些区别呢? Orange'S 这个名字很特别,有什么寓意吗? * 于渊:新版中还有一些小的变化。首先是操作系统的名字改变了,原因在于虽然我们的试验性 OS 从前辈们那里借鉴了很多东西,但其各个部分的设计(比如文件系统和内存管理)往往有其独特之处,所以我将原先的 Tinix (本意为 TryMinix )改成了新名字 Orange'S (这个名字来自于我的妻子),以表示它们的不同。另外,书中的代码风格,有些地方也做了调整。 新版中,原先的叙述风格都尽量地得以贯彻,而在表现形式上,新版用了更多心思,我相信读者能在其中发现这些特点:关注动手细节,探寻代码背后的故事,结果与过程兼顾,内容与形式并重。加上专门为本书建立的网站和讨论区,我相信读者能更容易地阅读,更轻松地学习。

69,382

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧