Software Basic( 软件基础 )
程序是用于执行特定任务的计算机指令组合。程序可以用汇编语言,一种非常低级的计算机语言来编写,也可以使用和机器无关的高级语言,比如 C 语言编写。操作系统是一个特殊的程序,允许用户通过它运行应用程序,比如电子表和文字处理等等。本章介绍了基本的编程原理,并简介操作系统的目的和功能。
2.1 Computer Languages( 计算机语言 )
2.1.1. 汇编语言
CPU 从内存中读取和执行的指令对于人类来讲无法理解。它们是机器代码,精确的告诉计算机要做什么。比如十六进制数 0x89E5 ,是 Intel 80486 的指令,将寄存器 ESP 的内容拷贝到寄存器 EBP 中。早期计算机中最初的软件工具之一是汇编程序,它读入人类可以阅读的源文件,将其装配成机器代码。汇编语言明确地处理对寄存器和对数据的操作,而这种操作对于特定的微处理器而言是特殊的。 Intel X86 微处理器的汇编语言和 Alpha AXP 微处理器的汇编语言完全不同。以下 Alpha AXP 汇编代码演示了程序可以执行的操作类型:
Ldr r16, (r15) ; 第一行
Ldr r17, 4(r15) ; 第二行
Beq r16,r17,100; 第三行
Str r17, (r15); 第四行
100: ; 第五行
第一条语句(第一行)将寄存器 15 指定的地址中的内容加载到寄存器 16 中。第二条指令将紧接着的内存中的内容加载到寄存器 17 中。第三行比较寄存器 16 和寄存器 17 ,如果相等,分支到标号 100 ,否则,继续执行第四行,将寄存器 17 的内容存到内存中。如果内存中的数据相同,就不必存储数据。编写汇编级的程序需要技巧而且十分冗长,容易出错。 Linux 系统的核心很少的一部分是用汇编语言编写,而这些部分之所以使用汇编语言只是为了提高效率,并且和具体的微处理器相关。
2.1.2 The C Programming Language and Compiler (C 语言和编译器 )
使用汇编语言编写大型程序十分困难,消耗时间,容易出错而且生成的程序不能移植,只能束缚在特定的处理器家族。更好的选择是使用和机器无关的语言,例如 C 。 C 允许你用逻辑算法描述程序和要处理的数据。被称为编译程序( compiler )的特殊程序读入 C 程序,并将它转换为汇编语言,进而产生机器相关的代码。好的编译器生成的汇编指令可以和好的汇编程序员编写的程序效率接近。大部分 Linux 核心是用 C 语言编写的。以下的 C 片断:
if (x != y)
x = y;
执行了和前面示例中汇编代码完全一样的操作。如果变量 x 的内容和变量 y 的内容不一样,变量 y 的内容被拷贝到变量 x 。 C 代码用例程( routine )进行组合,每一个例程执行一项任务。例程可以返回 C 所支持的任意的数值或数据类型。大型程序比如 Linux 核心分别由许多的 C 语言模块组成,每一个模块有自己的例程和数据结构。这些 C 源代码模块共同构成了逻辑功能比如文件系统的处理代码。
C 支持多种类型的变量。一个变量是内存中的特定位置,可用符号名引用。上述的 C 片断中, x 和 y 引用了内存中的位置。程序员不需要关心变量在内存中的具体位置,这是连接程序(下述)必须处理的。一些变量包含不同的数据例如整数、浮点数等和另一些则包含指针。
指针是包含其它数据在内存中的地址的变量。假设一个变量 x ,位于内存地址 0x80010000 , 你可能有一个指针 px ,指向 x 。 Px 可能位于地址 0x80010030 。 Px 的值则是变量 x 的地址, 0x80010000 。
C 允许你将相关的变量集合成为结构。例如:
Struct {
Int I;
Char b;
} my_struct;
是一个叫做 my_struct 的数据结构,包括两个元素:一个整数( 32 位) I 和一个字符( 8 位数据) b 。
2.1.3 Linkers (连接程序)
连接程序将几个目标模块和库文件连接在一起成为一个单独的完整程序。目标模块是汇编程序或编译程序的机器码输出,它包括机器码、数据和供连接程序使用的连接信息。比如:一个目标模块可能包括程序的所有数据库功能,而另一个目标模块则包括处理命令行参数的函数。连接程序确定目标模块之间的引用关系,即确定一个模块所引用的例程和数据在另一个模块中的实际位置。 Linux 核心是由多个目标模块连接而成的独立的大程序。
2.2 What is an Operating System (什么是操作系统?)
没有软件,计算机只是一堆发热的电子元件。如果说硬件是计算机的心脏,则软件就是它的灵魂。操作系统是允许用户运行应用程序的一组系统程序。操作系统将系统的硬件抽象,呈现在用户和应用程序之前的是一个虚拟的机器。是软件造就了计算机系统的特点。大多数 PC 可以运行一到多个操作系统,而每一个操作系统从外观和感觉上都大不相同。 Linux 由不同功能的部分构成,这些部分总体组合构成了 Linux 操作系统。 Linux 最明显的部分就是 Kernel 自身,但是如果没有 shell 或 libraries 一样没有用处。
为了了解什么是操作系统,看一看在你输入最简单的命令时发生了什么:
$ls
Mail c images perl
Docs tcl
$
这里的 $ 是登录的 shell 输出的提示符(此例是 bash ):表示 shell 在等候你(用户)输入命令。输入 ls 引发键盘驱动程序识别输入的字符,键盘驱动程序将识别的字符传递给 shell 去处理。 shell 先查找同名的可执行映象,它找到了 /bin/ls, 然后调用核心服务将 ls 执行程序加载到虚拟内存中并开始执行。 ls 执行程序通过执行核心的文件子系统的系统调用查找文件。文件系统可能使用缓存的文件系统信息或通过磁盘设备驱动程序从磁盘上读取文件信息 , 也可能是通过网络设备驱动程序同远程主机交换信息而读取本系统所访问的远程文件的详细信息(文件系统可以通过 NFS 网络文件系统远程安装)。不管文件信息是如何得到的, ls 都将信息输出,通过显示驱动程序显示在屏幕上。
以上的过程看起来相当复杂,但是它说明了即使是最简单的命令也是操作系统各个功能模块之间共同协作的结果,只有这样才能提供给你(用户)一个完整的系统视图。
2.2.1 Memory management (内存管理)
如果拥有无限的资源,例如内存,那么操作系统所必须做的很多事情可能都是多余的。所有操作系统的一个基本技巧就是让少量的物理内存工作起来好像有相当多的内存。这种表面看起来的大内存叫做虚拟内存,就是当软件运行的时候让它相信它拥有很多内存。系统将内存分为容易处理的页,在系统运行时将这些页交换到硬盘上。而应用软件并不知道,因为操作系统还使用了另一项技术:多进程。
2.2.2 Processes ( 进程 )
进程可以看作一个在执行的程序,每一个进程都是正在运行的特定的程序的独立实体。如果你观察一下你的 Linux 系统,你会发现有很多进程在运行。例如:在我的系统上输入 ps 显示了以下进程:
$ ps
PID TTY STAT TIME COMMAND
158 pRe 1 0:00 -bash
174 pRe 1 0:00 sh /usr/X11R6/bin/startx
175 pRe 1 0:00 xinit /usr/X11R6/lib/X11/xinit/xinitrc --
178 pRe 1 N 0:00 bowman
182 pRe 1 N 0:01 rxvt -geometry 120x35 -fg white -bg black
184 pRe 1
185 pRe 1
187 pp6 1 9:26 /bin/bash
202 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black
203 ppc 2 0:00 /bin/bash
1796 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black
1797 v06 1 0:00 /bin/bash
3056 pp6 3
3270 pp6 3 0:00 ps
$
如果我的系统拥有多个 CPU 那么每个进程可能(至少在理论上如此)都在不同的 CPU 上运行。不幸的是,只有一个,所以操作系统又使用技巧,在短时间内依次运行每一个进程。这个时间段叫做时间片。这种技巧叫做多进程或调度,它欺骗了每一个进程,好像它们是唯一的进程。进程相互之间受到保护,所以如果一个进程崩溃或不能工作,不会影响其他进程。操作系统通过给每一个进程一个独立的地址空间来实现保护,进程只能访问它自己的地址空间。
2.2.3 Device Drivers (设备驱动程序)
设备驱动程序组成了 Linux 核心的主要部分。象操作系统的其他部分一样,它们在一个高优先级的环境下工作,如果发生错误,可能会引发严重问题。设备驱动程序控制了操作系统和它控制的硬件设备之间的交互。比如:文件系统向 IDE 磁盘写数据块是使用通用块设备接口。驱动程序控制细节,并处理和设备相关的部分。设备驱动程序和它驱动的具体的控制器芯片相关,所以,如果你的系统有一个 NCR810 的 SCSI 控制器,那么你需要 NCR810 的驱动程序。
2.2.4 The Filesystems (文件系统)
象 Unix 一样,在 Linux 里,系统对独立的文件系统不是用设备标示符来存取(比如驱动器编号或驱动器名称),而是连接成为一个树型结构。 Linux 在安装新的文件系统时,把它安装到指定的安装目录,比如 /mnt/cdrom ,从而合并到这个单一的文件