第二章软件基础
程序是执行某个特定任务的计算机指令集合程序可以用多种程序语言来编写从低
级计算机语言-汇编语言到高级的与机器本身无关的语言入C 程序语言操作系统是一个
Linux Kernel Page 13
允许用户运行如电子表格或者字处理软件等应用程序的特殊程序本章将介绍程序设计的
基本原则同时给出操作系统设计目标与功能的概述
2.1 计算机编程语言
2.1.1 汇编语言
那些CPU 从主存读取出来执行的指令对人类来说是根本不可理解的它们是告诉计算
机如何准确动作的机器代码在Intel 80486 指令中16 进制数0x89E5表示将ESP 寄存器的
内容拷入EBP 寄存器为最早的计算机设计的工具之一就是汇编器它可以将人们可以理
解的源文件汇编成机器代码汇编语言需要显式的操作寄存器和数据并且与特定处理器
相关比如说Intel X86微处理器的汇编语言与Alpha AXP 微处理器的汇编语言决然不同
以下是一段Alpha AXP 汇编指令程序
ldr r16, (r15) ; Line 1
ldr r17, 4(r15) ; Line 2
beq r16,r17,100 ; Line 3
str r17, (r15) ; Line 4
100: ; Line 5
第一行语句将寄存器15 所指示的地址中的值加载到寄存器16 中接下来将邻接单元
内容加载到寄存器17 中第三行语句比较寄存器16 和寄存器17 中的值如果相等则跳
转到标号100 处否则继续执行第四行语句将寄存器17 的内容存入内存中如果寄存
器中值相等则无须保存汇编级程序一般冗长并且很难编写同时还容易出错 Linux 核
心中只有很少一部分是用汇编语言编写并且这些都是为了提高效率或者是需要兼容不同
的CPU
2.1.2 C编程语言和编译器
用汇编语言编写程序是一件困难且耗时的工作同时还容易出错并且程序不可移植
只能在某一特定处理器家族上运行而用C 语言这样的与具体机器无关的语言就要好得
多C 程序语言允许用它所提供的逻辑算法来描叙程序同时它提供编译器工具将C 程序
转换成汇编语言并最终产生机器相关代码好的编译器能产生和汇编语言程序相接近的效
率Linux内核中大部分用C 语言来编写以下是一段C 语言片段
if (x != y)
x = y ;
它所执行的任务和汇编语言代码示例中相同如果变量X 的值和变量Y 的不相同则将
Y 的内容赋予X C代码被组织成子程序单独执行某一任务子程序可以返回由C 支持
的任何数据类型的值较庞大的程序如Linux 核心由许多单独的C 源代码模块组成每个
Linux Kernel Page 14
模块有其自身的子程序与数据结构这些C 源代码模块将相关函数组合起来完成如文件处
理等功能 C 支持许多类型的变量变量是一个通过符号名称引用的内存位置在以上的
例子中X 和Y 都是内存中的位置程序员并不关心变量放在什么地方这些工作由连接
程序来完成有些变量包含不同类型的数据整数和浮点数以及指针指针是那些包含
其他数据内存位置或者地址的变量假设有变量X 位于内存地址0x80010000 处你可以
使用指针变量px 来指向X 则px 的值为0x80010000 C 语言允许相关变量组合起来形
成数据结构例如
struct {
int i ;
char b ;
} my_struct ;
这是一个叫做my_struct 的结构它包含两个元素一个是32 位的整数i,另外一个是8 位
的字符b
2.1.3 连接程序
连接程序是一个将几个目标模块和库过程连接起来形成单一程序的应用目标模块是
从汇编器或者编译器中产生的机器代码它包含可执行代码和数据模块结合在一起形成
程序例如一个模块可能包含程序中所有的数据库函数而另一个主要处理命令行参数连
接程序修改目标模块之间的引用关系使得在某一模块中引用的数据或者子程序的确存在
于其他模块中Linux 核心是由许多目标模块连接形成的庞大程序
2.2 操作系统概念
如果没有软件计算机只不过是一堆发热的电子器件如果将硬件比做计算机的心脏
则软件就是它的灵魂操作系统是一组系统程序的集合它提供给用户运行应用软件的功
能操作系统对系统硬件进行抽象它提供给系统用户一台虚拟的机器大多数PC 可以
运行一种或者多种操作系统每个操作系统都有不同的外观Linux 由许多独立的功能段
组成比如Linux 内核如果没有库函数和外壳程序内核是没有什么用的为了理解操
作系统到底是什么思考一下当你敲入一个简单命令时系统中发生了什么
$ ls
Mail c images perl
docs tcl
$
$符号是由用户登录外壳(这里指Bash)提供的提示符它表示正在等待用户敲入一些命
令敲入ls 命令首先键盘驱动程序识别出敲入的内容然后键盘驱动将它们传递给外壳
程序由外壳程序来负责查找同名的可执行程序(ls) 如果在/bin/ls 目录中找到了ls 则调
用核心服务将ls 的可执行映象读入虚拟内存并开始执行ls 调用核心的文件子系统来寻找
那些文件是可用的文件系统使用缓冲过的文件系统信息或者调用磁盘设备驱动从磁盘
Linux Kernel Page 15
上读取信息当然ls 还可能引起网络驱动程序和远程机器来交换信息以找出关于系统要访
问的远程文件系统信息(文件系统可以通过网络文件系统或者NFS 进行远程安装) 当得到
这些信息后ls 将这些信息通过调用视频驱动写到显示器屏幕上以上这些听起来十分复
杂这个非常简单命令的处理过程告诉我们操作系统是一组协同工作的函数的集合它们
给所有的用户对系统有一致的印象
2.2.1 内存管理
由于资源的有限比如内存操作系统处理事务的过程看起来十分冗长操作系统的
一个基本功能就是使一个只有少量物理内存的系统工作起来象有多得多的内存一样这个
大内存叫为虚拟内存其思想就是欺骗系统中运行的软件让它们认为有大量内存可用
系统将内存划分成易于处理的页面在系统运行时将这些页面交换到硬盘上去由于有另
外一个技巧:多处理的存在这些软件更加感觉不到系统中真实内存的大小
2.2.2 进程
进程可以认为是处于执行状态的程序每个进程有一个特定的程序实体观察以下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 < 0:00 xclock -bg grey -geometry -1500-1500 -padding 0
185 pRe 1 < 0:00 xload -bg grey -geometry -0-0 -label xload
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 < 0:02 emacs intro/introduction.tex
3270 pp6 3 0:00 ps
$
如果系统有许多个CPU 则每个进程可以运行在不同的CPU 上不幸的是大多数
系统中只有一个CPU 这样操作系统将轮流运行几个程序以产生它们在同时运行的假象
这种方式叫时间片轮转同时这种方法还骗过了进程使它们都认为只有自己在运行进程
之间被隔离开以便某个进程崩溃或者误操作不会影响到别的进程操作系统通过为每个
进程提供分立的地址空间来作到这一点
Linux Kernel Page 16
2.2.3 设备驱动
设备驱动组成了Linux 核心的主要部分象操作系统的其他部分一样它们运行在高
权限环境中且一旦出错将引起灾难性后果设备驱动控制操作系统和硬件设备之间的相互
操作例如当文件系统通过使用通用块设备接口来对IDE 磁盘写入数据块设备驱动负责
处理所有设备相关细节设备驱动与特定的控制器芯片有关如果系统中有一个NCR810
SCSI控制卡则需要有NCR810 SCSI的驱动程序
2.2.4 文件系统
Linux 和Unix 一样系统中的独立文件系统不是通过设备标志符来访问而是通过表
示文件系统的层次树结构来访问当Linux添加一个新的文件系统到系统中时会将它mount
到一个目录下比如说/mnt/cdrom Linux 的一个重要特征就是支持多种文件系统这使
得它非常灵活并且可与其他操作系统并存Linux 中最常用的文件系统是EXT2 文件系统
它在大多数Linux 分发版本中都得到了支持文件系统提供给用户一个关于系统的硬盘上
文件和目录的总体映象而不管文件的类型和底层物理设备的特性 Linux 透明地支持多
种文件系统并将当前安装的所有文件和文件系统集成到虚拟文件系统中去所以用户和
进程一般都不知道某个文件位于哪种文件系统中他们只是使用它块设备驱动将物理块
设备类型例如IDE 和SCSI 和文件系统中的差别隐藏起来物理设备只是数据块的线性
存储集合设备的不同导致块大小的不同从软盘设备的512 字节到IDE 磁盘的1024 字
节这些都隐藏了起来对系统用户来说这都是不可见的不管设备类型如何EXT2 文
件系统看起来总是一样
2.3 核心数据结构
操作系统可能包含许多关于系统当前状态的信息当系统发生变化时这些数据结构
必须做相应的改变以反映这些情况例如当用户登录进系统时将产生一个新的进程核
心必须创建表示新进程的数据结构同时将它和系统中其他进程的数据结构连接在一起
大多数数据结构存在于物理内存中并只能由核心或者其子系统来访问数据结构包括数据
和指针还有其他数据结构的地址或者子程序的地址它们混在一起让Linux 核心数据结
构看上去非常混乱尽管可能被几个核心子系统同时用到每个数据结构都有其专门的用
途理解Linux 核心的关键是理解它的数据结构以及Linux 核心中操纵这些数据结构的各
种函数本书把Linux核心的描叙重点放在数据结构上主要讨论每个核心子系统的算法
完成任务的途径以及对核心数据结构的使用
2.3.1 连接列表
Linux 使用的许多软件工程的技术来连接它的数据结构在许多场合下它使用linked
或者chained 数据结构每个数据结构描叙某一事物比如某个进程或网络设备核心必
须能够访问到所有这些结构在链表结构中个根节点指针包含第一个结构的地址而在
每个结构中又包含表中下一个结构的指针表的最后一项必须是0 或者NULL 以表明这
Linux Kernel Page 17
是表的尾部在双向链表中每个结构包含着指向表中前一结构和后一结构的指针使用
双向链表的好处在于更容易在表的中部添加与删除节点但需要更多的内存操作这是一
种典型的操作系统开销与CPU 循环之间的折中
2.3.2 散列表
链表用来连接数据结构比较方便但链表的操作效率不高如果要搜寻某个特定内容
我们可能不得不遍历整个链表Linux 使用另外一种技术:散列表来提高效率散列表是指
针的数组或向量指向内存中连续的相邻数据集合散列表中每个指针元素指向一个独立
链表如果你使用数据结构来描叙村子里的人则你可以使用年龄作为索引为了找到某
个人的数据可以在人口散列表中使用年龄作为索引找到包含此人特定数据的数据结构
但是在村子里有很多人的年龄相同这样散列表指针变成了一个指向具有相同年龄的人数
据链表的指针搜索这个小链表的速度显然要比搜索整个数据链表快得多由于散列表加
快了对数据结构的访问速度Linux 经常使用它来实现Caches Caches 是保存经常访问的
信息的子集经常被核心使用的数据结构将被放入Cache 中保存Caches 的缺点是比使用
和维护单一链表和散列表更复杂寻找某个数据结构时如果在Cache 中能够找到这种
情况称为cache 命中这的确很不错但是如果没有找到则必须找出它并且添加到Cache
中去如果Cache 空间已经用完则Linux 必须决定哪一个结构将从其中抛弃但是有可能
这个要抛弃的数据就是Linux下次要使用的数据
2.3.3 抽象接口
Linux 核心常将其接口抽象出来接口指一组以特定方式执行的子程序和数据结构的
集合例如所有的网络设备驱动必须提供对某些特定数据结构进行操作的子程序通用
代码可能会使用底层的某些代码例如网络层代码是通用的它得到遵循标准接口的特定
设备相关代码的支持通常在系统启动时底层接口向更高层接口注册(Register)自身这
些注册操作包括向链表中加入结构节点例如构造进核心的每个文件系统在系统启动时
将其自身向核心注册文件/proc/filesysems 中可以看到已经向核心注册过的文件系统注
册数据结构通常包括指向函数的指针以文件系统注册为例它向Linux 核心注册时必须
将那些mount文件系统连接时使用的一些相关函数的地址传入
阅读(606) | 评论(0) | 转发(0) |