首页资源分类嵌入式系统 > 计算机操作系统 第三版

计算机操作系统 第三版

已有 445476个资源

下载专区

上传者其他资源

    文档信息举报收藏

    标    签:操作系统

    分    享:

    文档简介

    计算机操作系统 第三版 课本 西电出版

    文档预览

    新世纪计算机类本科规划教材 部级优秀教材 计算机操作系统 (第 三 版) 汤小丹 梁红兵 编著 哲凤屏 汤子瀛 西安电子科技大学出版社 2007 内容简介 本书全面介绍了计算机系统中的一个重要软件——操作系统(OS),本书是第三版,对 2001 年出版的 修订版的各章内容均作了较多的修改,基本上能反映当前操作系统发展的现状,但章节名称基本保持不变。 全书仍分为 10 章,第一章介绍了 OS 的发展、特征、功能以及 OS 结构;第二、三章深入地阐述了进程和 线程的基本概念、同步与通信、调度与死锁;第四章对连续和离散存储器管理方式及虚拟存储器进行了介 绍;第五章为设备管理,对 I/O 软件的层次结构作了较深入的阐述;第六、七章分别是文件管理和用户接 口;第八章介绍了计算机网络、网络体系结构、网络提供的功能和服务以及 Internet;第九章对保障系统 安全的各种技术和计算机病毒都作了较详细的介绍;第十章是一个典型的 OS 实例——UNIX 系统内核 结构。 本书可作为计算机硬件和软件以及计算机通信专业的本科生教材,也可作为从事计算机及通信工作的 相关科技人员的参考书。 图书在版编目(CIP)数据 计算机操作系统/汤小丹等编著. —3 版. —西安:西安电子科技大学出版社,2007.5 新世纪计算机类本科规划教材 ISBN 978 – 7 – 5606 – 0496 – 1 Ⅰ. 计… Ⅱ. 汤… Ⅲ. 操作系统—高等学校—教材 Ⅳ. TP316 中国版本图书馆 CIP 数据核字(2007)第 039199 号 责任编辑 李惠萍 出版发行 西安电子科技大学出版社(西安市太白南路 2 号) 电 话 (029)88242885 88201467 邮 编 710071 http://www.xduph.com E-mail: xdupfxb@pub.xaonline.com 经 销 新华书店 印刷单位 西安文化彩印厂 版 次 1996 年 12 月第 1 版 2001 年 8 月第 2 版 2007 年 5 月第 3 版 2007 年 5 月第 21 次印刷 开 本 787 毫米×1092 毫米 1/16 印张 25.25 字 数 598 千字 印 数 166 001~170 000 册 定 价 30.00 元 ISBN 978 – 7 – 5606 – 0496 – 1/TP·0232 XDUP 0766A13-21 * * * 如有印装问题可调换 * * * 本社图书封面为激光防伪覆膜,谨防盗版。 第三版前言 本书从 1996 年出版至今,这是第二次修订(2001 年修订过一次)。在本次修订中,仍将 全书分为 10 章,虽然每章的标题没有改变,但却增、删或更新了其中的一部分内容,使其 能更好地反映操作系统的发展现状和前沿技术。 本书 10 章内容的具体安排如下: 第一章为操作系统引论,介绍了 OS 的发展、特征、功能等。在 OS 的发展中增加了微 处理机的发展,对 OS 结构设计的内容进行了更新,篇幅也有较大的扩充。第二章深入地 阐述了进程和线程的基本概念以及同步与通信,对进程的内容进行了适当增添,对管程以 及线程的内容进行了较大的更新。第三章为处理机调度与死锁,对其中的作业调度、进程 调度以及实时调度的内容都有一定的增加和修改。由于篇幅的限制,删除了多处理机调度 的内容。第四章对连续和离散存储器管理方式及虚拟存储器进行了介绍,对其中的分配算 法方面的内容有一定的增加和修改,并增添了对存储器的层次结构的介绍。第五章为设备 管理,对 I/O 软件的层次结构作了较深入的阐述。第六章是文件管理,对其中的外存分配 方式以及磁盘容错技术等内容进行了更新和扩充。第七章是用户与操作系统的接口,对接 口方式以及系统调用等部分内容进行了较大的更新及修改。第八章为网络操作系统,在本 章中我们对各节都作了修改,并增加了许多内容,使其能反映 21 世纪网络在硬件和软件方 面的发展现状。同样是因为篇幅的限制而删除了 Windows NT 的内容。第九章较详细地介 绍了保障系统安全的各种技术。由于近年来计算机病毒已严重地威胁到系统的安全,故我 们特增加了一节,对计算机病毒作了较全面的介绍。第十章是一个典型的 OS 实例——UNIX 系统内核结构。 在本书的编写过程中,得到了西安电子科技大学出版社,特别是责任编辑李惠萍老师 的大力支持与合作。此外,王侃雅和汤蓓莉等同志在校对、整理等工作中,都付出了辛勤 的劳动。在此谨向以上各位表以衷心的感谢。 虽然本书经过了反复修改,我们也希望能把它写得更好,但限于编者的水平,书中仍 难免会有错误和不当之处,恳请读者批评指正。 编者 2007 年 2 月 第二版前言 操作系统(OS)是最重要的计算机系统软件,同时也是最活跃的学科之一,其发展极为 迅速。为使本教材内容能紧跟时代潮流,从 1981 年至今,我们已对本教材做过多次修改。 2000 年我们又对 1996 年出版的《计算机操作系统》教材进行了重写。为了适当压缩篇幅, 我们调整了该教材的结构,从原来的 15 章改为 10 章。即将原来的第二、三章合并为“进 程管理”一章;原来的第五、六章合并为“存储器管理”一章;第八、九章合并为“文件 管理”一章;第十一、十二章合并为“网络操作系统”一章。另外,考虑到在大学低年级 的教学实践中,学生已经学习过 Windows OS 的使用,故本次修订时删去了原版第十五章。 我们在本教材中,介绍了许多在 20 世纪 90 年代引入或广泛使用的技术,如微内核 OS 结构、线程的控制与通信、数据一致性、系统容错技术等,又因为 20 世纪 90 年代是计算 机网络特别是 Internet 大发展的年代,故我们对网络操作系统一章做了较大的修改。还应强 调说明的是,随着网络的广泛应用,系统安全性问题提到了头等重要的地位。事实上,若 不能确保系统(网络)的安全性,则系统(网络)是难以被人接受的。故在国内外的 OS 教科书 中,大多都增加了一章或几章内容用于介绍系统的安全性保障。我们在第九章中对系统安 全性做了较全面的阐述。 本次再版的《计算机操作系统》一书共分 10 章。第一章仍为操作系统引论,介绍 OS 的发展过程、基本特征和功能,新增加了 OS 的结构设计;第二、三章详细地阐述了进程 和线程的基本概念、进程控制、同步与通信以及调度与死锁,增加了线程的控制、线程的 同步与通信;第四章为存储器管理,内容有连续分配、离散式分配存储管理方式和虚拟存 储器;第五、六章分别为设备管理和文件管理;第七章介绍操作系统接口,其中,增加了 UNIX 系统的 Shell 语言和系统调用的实现方法;第八章为网络操作系统,扼要地介绍了计 算机网络的基本概念,网络 OS 的工作模式、功能和提供的服务,以及 Internet/Intranet;第 九章对保障系统和网络安全的存取控制、认证、数据加密和防火墙四大技术做了较详细的 阐述;第十章介绍了当前广泛使用的 OS 实例——UNIX 系统内核结构。 本教材在编写过程中,得到了西安电子科技大学出版社的大力支持与合作。此外,汤 蓓莉、王侃雅等同志在整理、校对、绘图等工作中,都付出了艰辛的劳动,使本教材能如 期地与读者见面。在此谨向以上各位表示衷心感谢。 本教材虽经多次修改,突出了操作系统的基本概念,反映了当代操作系统的新技术, 但限于编者水平,在本次编写的教材中,仍难免会有错误和不当之处,恳请读者批评指正。 编者 2000 年 12 月 目录 第一章 操作系统引论 1.1 操作系统的目标和作用.................................1 1.1.1 操作系统的目标......................................1 1.1.2 操作系统的作用......................................2 1.1.3 推动操作系统发展的主要动力 ...............4 1.2 操作系统的发展过程.....................................5 1.2.1 无操作系统的计算机系统.......................5 1.2.2 单道批处理系统......................................6 1.2.3 多道批处理系统......................................7 1.2.4 分时系统.................................................9 1.2.5 实时系统............................................... 11 1.2.6 微机操作系统的发展 ............................ 12 1.3 操作系统的基本特性................................... 14 1.3.1 并发性................................................... 14 1.3.2 共享性................................................... 15 1.3.3 虚拟技术 ...............................................16 1.3.4 异步性...................................................17 1.4 操作系统的主要功能 ...................................18 1.4.1 处理机管理功能....................................18 1.4.2 存储器管理功能....................................19 1.4.3 设备管理功能........................................21 1.4.4 文件管理功能........................................21 1.4.5 操作系统与用户之间的接口 .................22 1.5 OS 结构设计 ................................................24 1.5.1 传统的操作系统结构 ............................24 1.5.2 客户/服务器模式...................................26 1.5.3 面向对象的程序设计 ............................27 1.5.4 微内核 OS 结构.....................................29 习题......................................................................33 第二章 进 程 管 理 2.1 进程的基本概念 .......................................... 34 2.1.1 程序的顺序执行及其特征..................... 34 2.1.2 前趋图................................................... 35 2.1.3 程序的并发执行及其特征..................... 36 2.1.4 进程的特征与状态................................ 37 2.1.5 进程控制块 ........................................... 41 2.2 进程控制 ..................................................... 43 2.2.1 进程的创建 ........................................... 43 2.2.2 进程的终止 ........................................... 45 2.2.3 进程的阻塞与唤醒................................ 46 2.2.4 进程的挂起与激活................................ 47 2.3 进程同步 ..................................................... 47 2.3.1 进程同步的基本概念 ............................ 47 2.3.2 信号量机制 ........................................... 50 2.3.3 信号量的应用 ....................................... 53 2.3.4 管程机制............................................... 55 2.4 经典进程的同步问题 ...................................58 2.4.1 生产者—消费者问题 ............................58 2.4.2 哲学家进餐问题....................................61 2.4.3 读者—写者问题....................................63 2.5 进程通信......................................................65 2.5.1 进程通信的类型....................................65 2.5.2 消息传递通信的实现方法.....................66 2.5.3 消息传递系统实现中的若干问题..........68 2.5.4 消息缓冲队列通信机制.........................69 2.6 线程 .............................................................71 2.6.1 线程的基本概念....................................72 2.6.2 线程间的同步和通信 ............................75 2.6.3 线程的实现方式....................................77 2.6.4 线程的实现 ...........................................78 习题......................................................................81 ·1· 第三章 处理机调度与死锁 3.1 处理机调度的层次 ...................................... 84 3.1.1 高级调度............................................... 84 3.1.2 低级调度............................................... 86 3.1.3 中级调度............................................... 87 3.2 调度队列模型和调度准则 ........................... 88 3.2.1 调度队列模型 ....................................... 88 3.2.2 选择调度方式和调度算法的若干 准则 ...................................................... 90 3.3 调度算法 ..................................................... 91 3.3.1 先来先服务和短作业(进程)优先 调度算法............................................... 91 3.3.2 高优先权优先调度算法 ........................ 93 3.3.3 基于时间片的轮转调度算法................. 95 3.4 实时调度 ..................................................... 97 3.4.1 实现实时调度的基本条件..................... 97 3.4.2 实时调度算法的分类 ............................99 3.4.3 常用的几种实时调度算法...................100 3.5 产生死锁的原因和必要条件......................103 3.5.1 产生死锁的原因..................................103 3.5.2 产生死锁的必要条件 ..........................105 3.5.3 处理死锁的基本方法 ..........................105 3.6 预防死锁的方法.........................................106 3.6.1 预防死锁 .............................................106 3.6.2 系统安全状态......................................107 3.6.3 利用银行家算法避免死锁...................108 3.7 死锁的检测与解除..................................... 111 3.7.1 死锁的检测 ......................................... 111 3.7.2 死锁的解除 .........................................113 习题....................................................................114 第四章 存 储 器 管 理 4.1 存储器的层次结构 .................................... 116 4.1.1 多级存储器结构.................................. 116 4.1.2 主存储器与寄存器.............................. 117 4.1.3 高速缓存和磁盘缓存 .......................... 117 4.2 程序的装入和链接 .................................... 118 4.2.1 程序的装入 ......................................... 118 4.2.2 程序的链接 ......................................... 120 4.3 连续分配方式............................................ 121 4.3.1 单一连续分配 ..................................... 121 4.3.2 固定分区分配 ..................................... 122 4.3.3 动态分区分配 ..................................... 123 4.3.4 伙伴系统............................................. 126 4.3.5 哈希算法............................................. 126 4.3.6 可重定位分区分配.............................. 127 4.3.7 对换 .................................................... 129 4.4 基本分页存储管理方式............................. 130 4.4.1 页面与页表 ......................................... 130 4.4.2 地址变换机构 ..................................... 131 ·2· 4.4.3 两级和多级页表..................................133 4.5 基本分段存储管理方式 .............................135 4.5.1 分段存储管理方式的引入...................135 4.5.2 分段系统的基本原理 ..........................136 4.5.3 信息共享 .............................................138 4.5.4 段页式存储管理方式 ..........................140 4.6 虚拟存储器的基本概念 .............................141 4.6.1 虚拟存储器的引入 ..............................142 4.6.2 虚拟存储器的实现方法.......................143 4.6.3 虚拟存储器的特征 ..............................144 4.7 请求分页存储管理方式 .............................144 4.7.1 请求分页中的硬件支持.......................144 4.7.2 内存分配策略和分配算法...................147 4.7.3 调页策略 .............................................148 4.8 页面置换算法 ............................................149 4.8.1 最佳置换算法和先进先出置换算法....150 4.8.2 最近最久未使用(LRU)置换算法.........151 4.8.3 Clock 置换算法 ...................................153 4.8.4 其它置换算法 ..................................... 154 4.9 请求分段存储管理方式............................. 155 4.9.1 请求分段中的硬件支持 ...................... 155 4.9.2 分段的共享与保护 ..............................157 习题....................................................................159 第五章 设 备 管 理 5.1 I/O 系统 ..................................................... 160 5.1.1 I/O 设备 .............................................. 160 5.1.2 设备控制器 ......................................... 162 5.1.3 I/O 通道 .............................................. 164 5.1.4 总线系统............................................. 166 5.2 I/O 控制方式 ............................................. 167 5.2.1 程序 I/O 方式...................................... 167 5.2.2 中断驱动 I/O 控制方式....................... 168 5.2.3 直接存储器访问(DMA)I/O 控制 方式 .................................................... 169 5.2.4 I/O 通道控制方式 ............................... 170 5.3 缓冲管理 ................................................... 171 5.3.1 缓冲的引入 ......................................... 171 5.3.2 单缓冲和双缓冲.................................. 172 5.3.3 循环缓冲............................................. 174 5.3.4 缓冲池................................................. 175 5.4 I/O 软件 ..................................................... 177 5.4.1 I/O 软件的设计目标和原则................177 5.4.2 中断处理程序......................................179 5.4.3 设备驱动程序......................................181 5.4.4 设备独立性软件..................................184 5.4.5 用户层的 I/O 软件...............................186 5.5 设备分配....................................................186 5.5.1 设备分配中的数据结构.......................186 5.5.2 设备分配时应考虑的因素...................187 5.5.3 独占设备的分配程序 ..........................188 5.5.4 SPOOLing 技术 ...................................189 5.6 磁盘存储器的管理.....................................191 5.6.1 磁盘性能简述......................................191 5.6.2 磁盘调度 .............................................194 5.6.3 磁盘高速缓存......................................197 5.6.4 提高磁盘 I/O 速度的其它方法 ............199 5.6.5 廉价磁盘冗余阵列 ..............................200 习题....................................................................202 第六章 文 件 管 理 6.1 文件和文件系统 ........................................ 203 6.1.1 文件、记录和数据项 .......................... 203 6.1.2 文件类型和文件系统模型................... 205 6.1.3 文件操作............................................. 206 6.2 文件的逻辑结构 ........................................ 208 6.2.1 文件逻辑结构的类型 .......................... 208 6.2.2 顺序文件............................................. 209 6.2.3 索引文件............................................. 210 6.2.4 索引顺序文件 ..................................... 211 6.2.5 直接文件和哈希文件 .......................... 212 6.3 外存分配方式............................................ 213 6.3.1 连续分配............................................. 213 6.3.2 链接分配............................................. 215 6.3.3 FAT 和 NTFS 技术...............................216 6.3.4 索引分配 .............................................221 6.4 目录管理....................................................223 6.4.1 文件控制块和索引结点.......................224 6.4.2 目录结构 .............................................226 6.4.3 目录查询技术......................................229 6.5 文件存储空间的管理 .................................231 6.5.1 空闲表法和空闲链表法.......................231 6.5.2 位示图法 .............................................232 6.5.3 成组链接法 .........................................233 6.6 文件共享与文件保护 .................................234 6.6.1 基于索引结点的共享方式...................234 6.6.2 利用符号链实现文件共享...................236 ·3· 6.6.3 磁盘容错技术 ..................................... 237 6.7 数据一致性控制 ........................................ 240 6.7.1 事务 .................................................... 241 6.7.2 检查点................................................. 242 6.7.3 并发控制 .............................................243 6.7.4 重复数据的数据一致性问题 ...............243 习题....................................................................246 第七章 操作系统接口 7.1 联机用户接口............................................ 248 7.1.1 联机用户接口 ..................................... 248 7.1.2 联机命令的类型.................................. 250 7.1.3 键盘终端处理程序.............................. 252 7.1.4 命令解释程序 ..................................... 254 7.2 Shell 命令语言........................................... 255 7.2.1 简单命令............................................. 255 7.2.2 重定向与管道命令.............................. 258 7.2.3 通信命令............................................. 259 7.2.4 后台命令............................................. 260 7.3 系统调用 ................................................... 260 7.3.1 系统调用的基本概念 .......................... 261 7.3.2 系统调用的类型.................................. 263 7.3.3 POSIX 标准 .........................................265 7.3.4 系统调用的实现..................................266 7.4 UNIX 系统调用..........................................268 7.4.1 UNIX 系统调用的类型........................269 7.4.2 被中断进程的环境保护.......................271 7.4.3 系统调用陷入后需处理的 公共问题 .............................................272 7.5 图形用户接口 ............................................273 7.5.1 图形化用户界面..................................273 7.5.2 桌面、图标和任务栏 ..........................274 7.5.3 窗口.....................................................276 7.5.4 对话框.................................................277 习题....................................................................279 第八章 网络操作系统 8.1 计算机网络概述 ........................................ 281 8.1.1 计算机网络的拓扑结构 ...................... 281 8.1.2 计算机广域网络.................................. 284 8.1.3 计算机局域网络.................................. 287 8.1.4 网络互连............................................. 288 8.2 网络体系结构............................................ 290 8.2.1 网络体系结构的基本概念................... 290 8.2.2 OSI/RM 中的低三层 ........................... 292 8.2.3 OSI/RM 中的高四层 ........................... 294 8.2.4 TCP/IP 网络体系结构 ........................ 295 8.2.5 LAN 网络体系结构............................. 297 8.3 Internet 与 Intranet ..................................... 299 8.3.1 Internet 简介........................................ 300 8.3.2 Internet 提供的传统信息服务 ............. 301 8.3.3 Web 服务............................................. 303 8.4 客户/服务器模式 ....................................... 304 ·4· 8.4.1 两层结构客户/服务器模式的 局限性 .................................................304 8.4.2 三层结构的客户/服务器模式 ..............305 8.4.3 两层客户/服务器与三层客户/服务器的 比较.....................................................306 8.4.4 浏览器/服务器(Browser/Server) 模式.....................................................307 8.5 网络操作系统的功能 .................................308 8.5.1 数据通信功能......................................308 8.5.2 网络资源共享功能 ..............................309 8.5.3 应用互操作功能..................................312 8.5.4 网络管理功能......................................314 8.6 网络操作系统提供的服务..........................315 8.6.1 域名系统(DNS) ...................................315 8.6.2 目录服务 .............................................317 8.6.3 支持 Internet 提供的服务 ....................319 习题 ................................................................... 320 第九章 系 统 安 全 性 9.1 系统安全的基本概念................................. 322 9.1.1 系统安全性的内容和性质................... 322 9.1.2 系统安全威胁的类型 .......................... 323 9.1.3 信息技术安全评价公共准则............... 324 9.2 数据加密技术............................................ 325 9.2.1 数据加密的基本概念 .......................... 325 9.2.2 对称加密算法与非对称加密算法 ....... 328 9.2.3 数字签名和数字证明书 ...................... 329 9.2.4 网络加密技术 ..................................... 331 9.3 认证技术 ................................................... 332 9.3.1 基于口令的身份认证 .......................... 333 9.3.2 基于物理标志的认证技术................... 335 9.3.3 基于生物标志的认证技术...................337 9.3.4 基于公开密钥的认证技术...................339 9.4 访问控制技术 ............................................340 9.4.1 访问矩阵 .............................................340 9.4.2 访问矩阵的修改..................................342 9.4.3 访问控制矩阵的实现 ..........................343 9.5 计算机病毒 ................................................345 9.5.1 计算机病毒的基本概念.......................345 9.5.2 计算机病毒的类型 ..............................346 9.5.3 病毒的隐藏方式..................................348 9.5.4 病毒的预防和检测 ..............................350 习题....................................................................351 第十章 UNIX 系统内核结构 10.1 UNIX 系统概述 ....................................... 353 10.1.1 UNIX 系统的发展史 ......................... 353 10.1.2 UNIX 系统的特征............................. 355 10.1.3 UNIX 系统的内核结构 ..................... 356 10.2 进程的描述和控制................................... 357 10.2.1 进程控制块 ....................................... 357 10.2.2 进程状态与进程映像 ........................ 359 10.2.3 进程控制........................................... 361 10.2.4 进程调度与切换................................ 363 10.3 进程的同步与通信................................... 364 10.3.1 sleep 与 wakeup 同步机制 ................. 364 10.3.2 信号机制........................................... 365 10.3.3 管道机制........................................... 365 10.3.4 消息机制........................................... 367 10.3.5 共享存储区机制................................ 368 10.3.6 信号量集机制 ................................... 369 10.4 存储器管理.............................................. 370 参考文献 ......................................................... 392 10.4.1 请求调页管理的数据结构 .................370 10.4.2 换页进程 ...........................................372 10.4.3 请求调页 ...........................................373 10.5 设备管理..................................................374 10.5.1 字符设备缓冲区管理.........................374 10.5.2 块设备缓冲区管理 ............................375 10.5.3 内核与驱动程序接口.........................377 10.5.4 磁盘驱动程序....................................379 10.5.5 磁盘读/写程序...................................380 10.6 文件管理..................................................381 10.6.1 UNIX 文件系统概述..........................381 10.6.2 文件的物理结构 ................................383 10.6.3 索引结点的管理 ................................385 10.6.4 空闲磁盘空间的管理.........................386 10.6.5 文件表的管理....................................388 10.6.6 目录管理 ...........................................389 习题....................................................................390 ·5· 第一章 操作系统引论 ·1· 第一章 操作系统引论 计算机系统由硬件和软件两部分组成。操作系统(OS,Operating System)是配置在计算 机硬件上的第一层软件,是对硬件系统的首次扩充。它在计算机系统中占据了特别重要的 地位;而其它的诸如汇编程序、编译程序、数据库管理系统等系统软件,以及大量的应用 软件,都将依赖于操作系统的支持,取得它的服务。操作系统已成为现代计算机系统(大、 中、小及微型机)、多处理机系统、计算机网络、多媒体系统以及嵌入式系统中都必须配置 的、最重要的系统软件。 1.1 操作系统的目标和作用 在计算机系统上配置操作系统的主要目标,首先与计算机系统的规模有关。通常对配 置在大、中型计算机系统中的 OS,由于计算机价格昂贵,因此都比较看重机器使用的有效 性,而且还希望 OS 具有非常强的功能;但对于配置在微机中的操作系统,由于微机价格相 对较便宜,此时机器使用的有效性也就显得不那么重要了,而人们更关注的是使用的方 便性。 影响操作系统的主要目标的另一个重要因素是操作系统的应用环境。例如,对于应用 在查询系统中的操作系统,应满足用户对响应时间的要求;又如对应用在实时工业控制和 武器控制环境下的 OS,则要求其 OS 具有实时性和高度可靠性。 1.1.1 操作系统的目标 目前存在着多种类型的 OS,不同类型的 OS,其目标各有所侧重。一般地说,在计算 机硬件上配置的 OS,其目标有以下几点。 1.有效性 在早期(20 世纪 50~60 年代),由于计算机系统非常昂贵,操作系统最重要的目标无疑 是有效性。事实上,那时有效性是推动操作系统发展最主要的动力。正因如此,现在的大 多数操作系统书籍,都着重于介绍如何提高计算机系统的资源利用率和系统的吞吐量问题。 操作系统的有效性可包含如下两方面的含意: (1) 提高系统资源利用率。在未配置 OS 的计算机系统中,诸如 CPU、I/O 设备等各种 资源,都会因它们经常处于空闲状态而得不到充分利用;内存及外存中所存放的数据太少 或者无序而浪费了大量的存储空间。配置了 OS 之后,可使 CPU 和 I/O 设备由于能保持忙 碌状态而得到有效的利用,且可使内存和外存中存放的数据因有序而节省了存储空间。 (2) 提高系统的吞吐量。操作系统还可以通过合理地组织计算机的工作流程,而进一步 改善资源的利用率,加速程序的运行,缩短程序的运行周期,从而提高系统的吞吐量。 ·2· 计算机操作系统 2.方便性 配置 OS 后可使计算机系统更容易使用。一个未配置 OS 的计算机系统是极难使用的, 因为计算机硬件只能识别 0 和 1 这样的机器代码。用户要直接在计算机硬件上运行自己所 编写的程序,就必须用机器语言书写程序;用户要想输入数据或打印数据,也都必须自己 用机器语言书写相应的输入程序或打印程序。如果我们在计算机硬件上配置了 OS,用户便 可通过 OS 所提供的各种命令来使用计算机系统。比如,用编译命令可方便地把用户用高级 语言书写的程序翻译成机器代码,大大地方便了用户,从而使计算机变得易学易用。 方便性和有效性是设计操作系统时最重要的两个目标。在过去的很长一段时间内,由 于计算机系统非常昂贵,因而其有效性显得比较重要。但是,近十多年来,随着硬件越来 越便宜,在设计配置在微机上的 OS 时,人们似乎更重视如何使用户能更为方便地使用计算 机,故在微机操作系统中都配置了受到用户广泛欢迎的图形用户界面,提供了大量的供程 序员使用的系统调用。 3.可扩充性 随着 VLSI 技术和计算机技术的迅速发展,计算机硬件和体系结构也随之得到迅速发 展,相应地,它们也对 OS 提出了更高的功能和性能要求。此外,多处理机系统、计算机网 络,特别是 Internet 的发展,又对 OS 提出了一系列更新的要求。因此,OS 必须具有很好的 可扩充性,方能适应计算机硬件、体系结构以及应用发展的要求。这就是说,现代 OS 应采 用新的 OS 结构,如微内核结构和客户服务器模式,以便于方便地增加新的功能和模块,并 能修改老的功能和模块。关于新的 OS 结构将在本章最后一节中介绍。 4.开放性 自 20 世纪 80 年代以来,由于计算机网络的迅速发展,特别是 Internet 的应用的日益普 及,使计算机操作系统的应用环境已由单机封闭环境转向开放的网络环境。为使来自不同 厂家的计算机和设备能通过网络加以集成化,并能正确、有效地协同工作,实现应用的可 移植性和互操作性,要求操作系统必须提供统一的开放环境,进而要求 OS 具有开放性。 开放性是指系统能遵循世界标准规范,特别是遵循开放系统互连(OSI)国际标准。凡遵 循国际标准所开发的硬件和软件,均能彼此兼容,可方便地实现互连。开放性已成为 20 世 纪 90 年代以后计算机技术的一个核心问题,也是一个新推出的系统或软件能否被广泛应用 的至关重要的因素。 1.1.2 操作系统的作用 可以从不同的观点(角度)来观察 OS 的作用。从一般用户的观点,可把 OS 看做是用户 与计算机硬件系统之间的接口;从资源管理的观点看,则可把 OS 视为计算机系统资源的管 理者。另外,OS 实现了对计算机资源的抽象,隐藏了对硬件操作的细节,使用户能更方便 地使用机器。 1.OS 作为用户与计算机硬件系统之间的接口 OS 作为用户与计算机硬件系统之间接口的含义是:OS 处于用户与计算机硬件系统之 间,用户通过 OS 来使用计算机系统。或者说,用户在 OS 帮助下,能够方便、快捷、安全、 可靠地操纵计算机硬件和运行自己的程序。应注意,OS 是一个系统软件,因而这种接口是 第一章 操作系统引论 ·3· 软件接口。图 1-1 是 OS 作为接口的示意图。由图 可看出,用户可通过以下三种方式使用计算机。 (1) 命令方式。这是指由 OS 提供了一组联机 命令接口,以允许用户通过键盘输入有关命令来 取得操作系统的服务,并控制用户程序的运行。 用户 应用程序 系统调用 命令 图标、窗口 操作系统 (2) 系统调用方式。OS 提供了一组系统调用, 计算机硬件 用户可在自己的应用程序中通过相应的系统调 用,来实现与操作系统的通信,并取得它的服务。 图 1-1 OS 作为接口的示意图 (3) 图形、窗口方式。这是当前使用最为方便、最为广泛的接口,它允许用户通过屏幕 上的窗口和图标来实现与操作系统的通信,并取得它的服务。 2.OS 作为计算机系统资源的管理者 在一个计算机系统中,通常都含有各种各样的硬件和软件资源。归纳起来可将资源分 为四类:处理器、存储器、I/O 设备以及信息(数据和程序)。相应地,OS 的主要功能也正是 针对这四类资源进行有效的管理,即:处理机管理,用于分配和控制处理机;存储器管理, 主要负责内存的分配与回收; I/O 设备管理,负责 I/O 设备的分配与操纵;文件管理,负责 文件的存取、共享和保护。可见,OS 的确是计算机系统资源的管理者。事实上,当今世界 上广为流行的一个关于 OS 作用的观点,正是把 OS 作为计算机系统的资源管理者。 值得进一步说明的是,当一个计算机系统同时供多个用户使用时,用户对系统中共享 资源的需求(包括数量和时间)可能发生冲突,为了管理好这些共享资源(包括硬件和信息)的 使用,操作系统必须记录下各种资源的使用情况,对使用资源的请求进行授权,协调诸用 户对共享资源的使用,避免发生冲突,并计算使用资源的费用等。 3.OS 实现了对计算机资源的抽象 对于一个完全无软件的计算机系统(即裸机),它向用户提供的是实际硬件接口(物理接 口),用户必须对物理接口的实现细节有充分的了解,并利用机器指令进行编程,因此该物 理机器必定是难以使用的。为了方便用户使用 I/O 设备,人们在裸机上覆盖上一层 I/O 设备 管理软件,如图 1-2 所示,由它来实现对 I/O 设备操作的细节,并向上提供一组 I/O 操作命 令,如 Read 和 Write 命令,用户可利用它来进行数据输入或输出,而无需关心 I/O 是如何 实现的。此时用户所看到的机器将是一台比裸机功能更强、使用更方便的机器。这就是说, 在裸机上铺设的 I/O 软件隐藏了对 I/O 设备操作的具体细节,向上提供了一组抽象的 I/O 设备。 用户 机器指令 物理接口 硬件 用户 I/O操作命令 (Read, Write) I/O软件 物理接口 硬件 虚机器 图 1-2 I/O 软件隐藏了 I/O 操作实现的细节 ·4· 计算机操作系统 通常把覆盖了上述软件的机器称为扩充机器或虚机器。它向用户(进程)提供了一个对硬 件操作的抽象模型,用户可利用抽象模型提供的接口使用计算机,而无需了解物理接口实 现的细节,从而使用户更容易地使用计算机硬件资源。由该层软件实现了对计算机硬件操 作的第一个层次的抽象。 为了方便用户使用文件系统,人们又在第一层软件上再覆盖上一层用于文件的管理软 件,同样由它来实现对文件操作的细节,并向上提供一组对文件进行存取操作的命令,用 户可利用这组命令进行文件的存取。此时,用户所看到的是一台功能更强、使用更方便的 虚机器。该层软件实现了对硬件资源操作的第二个层次的抽象。而当人们又在文件管理软 件上再覆盖一层面向用户的窗口软件后,用户便可在窗口环境下方便地使用计算机,形成 一台功能更强的虚机器。 由此可知,OS 是铺设在计算机硬件上的多层系统软件,它们不仅增强了系统的功能, 而且还隐藏了对硬件操作的细节,由它们实现了对计算机硬件操作的多个层次的抽象。值 得说明的是,对一个硬件在底层进行抽象后,在高层还可再次对该资源进行抽象,成为更 高层的抽象模型。随着抽象层次的提高,抽象接口所提供的功能就越来越强,用户使用起 来也更加方便。 1.1.3 推动操作系统发展的主要动力 在出现 OS 后的短短 50 多年中,操作系统取得了重大的发展,操作系统的功能已变得 非常强大,所提供的服务也十分有效,用户使用更加方便。相应地,操作系统的规模也由 早期的数十 KB 发展到如今的数千万行代码,甚至更多。推动操作系统发展的主要动力,可 归结为如下所述的四个方面。 1.不断提高计算机资源的利用率 在计算机发展的初期,计算机系统特别昂贵,人们必须千方百计地提高计算机系统中 各种资源的利用率,这就是 OS 最初发展的推动力。由此形成了能自动地对一批作业进行处 理的多道批处理系统。在 20 世纪 60 和 70 年代,又分别出现了能有效提高 I/O 设备和 CPU 利用率的 SPOOLing 系统和改善存储器系统利用率的虚拟存储器技术,以及在网络环境下, 在服务器上配置了允许所有网络用户访问的文件系统和数据库系统。 2.方便用户 当资源利用率不高的问题得到基本解决后,用户在上机、调试程序时的不方便性便又 成为主要矛盾。于是人们又想方设法改善用户上机、调试程序时的环境,这又成为继续推 动 OS 发展的主要因素。随之便形成了允许进行人机交互的分时系统,或称为多用户系统。 在 20 世纪 90 年代初出现了受到用户广泛欢迎的图形用户界面,极大地方便了用户使用计 算机,使中小学生都能很快地学会上机操作,这无疑会更加推动计算机的迅速普及。 3.器件的不断更新换代 微电子技术的迅猛发展,推动着计算机器件,特别是微机芯片的不断更新,使得计算 机的性能迅速提高,规模急剧扩大,从而推动了 OS 的功能和性能也迅速增强和提高。例如, 当微机芯片由 8 位发展到 16 位、32 位,进而又发展到 64 位时,相应的微机 OS 也就由 8 位发展到 16 位和 32 位,进而又发展到 64 位,此时相应 OS 的功能和性能也都有显著的增 第一章 操作系统引论 ·5· 强和提高。 在多处理机快速发展的同时,外部设备也在迅速发展。例如,早期的磁盘系统十分昂 贵,只能配置在大型机中。随着磁盘价格的不断降低且小型化,很快在中、小型机以及微 型机上也无一例外地配置了磁盘系统,而且其容量还远比早期配置在大型机上的大得多。 现在的微机操作系统(如 Windows XP)能支持种类非常多的外部设备,除了传统的外设外, 还可以支持光盘、移动硬盘、闪存盘、扫描仪等。 4.计算机体系结构的不断发展 计算机体系结构的发展,也不断推动着 OS 的发展并产生新的操作系统类型。例如,当 计算机由单处理机系统发展为多处理机系统时,相应地,操作系统也就由单处理机 OS 发展 为多处理机 OS。又如,当出现了计算机网络后,配置在计算机网络上的网络操作系统也就 应运而生,它不仅能有效地管理好网络中的共享资源,而且还向用户提供了许多网络服务。 1.2 操作系统的发展过程 OS 的形成迄今已有 50 多年的时间。在上世纪 50 年代中期出现了单道批处理操作系统; 60 年代中期产生了多道程序批处理系统;不久又出现了基于多道程序的分时系统,与此同 时也诞生了用于工业控制和武器控制的实时操作系统。20 世纪 80 年代开始至 21 世纪初, 是微型机、多处理机和计算机网络高速发展的年代,同时也是微机 OS、多处理机 OS 和网 络 OS 以及分布式 OS 的形成和大发展的年代。本节主要介绍早期的操作系统发展,在本章 1.5 节中再对微机 OS、多处理机 OS、网络 OS 和分布式 OS 等作简单的阐述。 1.2.1 无操作系统的计算机系统 1.人工操作方式 从第一台计算机诞生(1945 年)到 20 世纪 50 年代中期的计算机,属于第一代计算机。此 时的计算机是利用成千上万个真空管做成的,它的运行速度仅为每秒数千次,但体积却十 分庞大,且功耗也非常高。这时还未出现 OS。计算机操作是由用户(即程序员)采用人工操 作方式直接使用计算机硬件系统,即由程序员将事先已穿孔(对应于程序和数据)的纸带(或 卡片)装入纸带输入机(或卡片输入机),再启动它们将程序和数据输入计算机,然后启动计 算机运行。当程序运行完毕并取走计算结果之后,才让下一个用户上机。这种人工操作方 式有以下两方面的缺点: (1) 用户独占全机。此时,计算机及其全部资源只能由上机用户独占。 (2) CPU 等待人工操作。当用户进行装带(卡)、卸带(卡)等人工操作时,CPU 及内存等 资源是空闲的。 可见,人工操作方式严重降低了计算机资源的利用率,此即所谓的人机矛盾。随着 CPU 速度的提高和系统规模的扩大,人机矛盾变得日趋严重。此外,随着 CPU 速度的迅速提高 而 I/O 设备的速度却提高缓慢,这又使 CPU 与 I/O 设备之间速度不匹配的矛盾更加突出。 为了缓和此矛盾,曾先后出现了通道技术、缓冲技术,但都未能很好地解决上述矛盾,直 至后来又引入了脱机输入/输出技术,才获得了较为令人满意的结果。 ·6· 计算机操作系统 2.脱机输入/输出方式 为了解决人机矛盾及 CPU 和 I/O 设备之间速度不匹配的矛盾,20 世纪 50 年代末出现 了脱机输入/输出(Off-Line I/O)技术。该技术是事先将装有用户程序和数据的纸带(或卡片) 装入纸带输入机(或卡片机),在一台外围机的控制下,把纸带(卡片)上的数据(程序)输入到 磁带上。当 CPU 需要这些程序和数据时,再从磁带上将其高速地调入内存。 类似地,当 CPU 需要输出时,可由 CPU 直接高速地把数据从内存送到磁带上,然后再 在另一台外围机的控制下,将磁带上的结果通过相应的输出设备输出。图 1-3 示出了脱机输 入/输出过程。由于程序和数据的输入和输 出都是在外围机的控制下完成的,或者说, 它们是在脱离主机的情况下进行的,故称为 输入设备 外围机 磁盘 脱机输入/输出方式;反之,在主机的直 接控制下进行输入/输出的方式称为联机 输入/输出(On-Line I/O)方式。这种脱机 I/O 主机 方式的主要优点如下: (1) 减少了 CPU 的空闲时间。装带(卡)、 卸带(卡)以及将数据从低速 I/O 设备送到高 速磁带(或盘)上,都是在脱机情况下进行 外围机 输出设备 的,并不占用主机时间,从而有效地减少了 CPU 的空闲时间,缓和了人机矛盾。 图 1-3 脱机 I/O 示意图 (2) 提高了 I/O 速度。当 CPU 在运行中需要数据时,是直接从高速的磁带或磁盘上将 数据调入内存的,不再是从低速 I/O 设备上输入,极大地提高了 I/O 速度,从而缓和了 CPU 和 I/O 设备速度不匹配的矛盾,进一步减少了 CPU 的空闲时间。 1.2.2 单道批处理系统 1.单道批处理系统的处理过程 上世纪 50 年代中期发明了晶体管,人们开始用晶体管替代真空管来制作计算机,从而 出现了第二代计算机。它不仅使计算机的体积大大减小,功耗显著降低,同时可靠性也得 到大幅度提高,使计算机已具有推广应用的价值,但计算机系统仍非常昂贵。为了能充分 地利用它,应尽量让该系统连续运行,以减少空闲时间。为此,通常是把一批作业以脱机 方式输入到磁带上,并在系统中配上监督程序(Monitor),在它的控制下使这批作业能一个 接一个地连续处理。其自动处理过程是:首先,由监督程序将磁带上的第一个作业装入内 存,并把运行控制权交给该作业。当该作业处理完成时,又把控制权交还给监督程序,再 由监督程序把磁带(盘)上的第二个作业调入内存。计算机系统就这样自动地一个作业一个作 业地进行处理,直至磁带(盘)上的所有作业全部完成,这样便形成了早期的批处理系统。由 于系统对作业的处理都是成批地进行的,且在内存中始终只保持一道作业,故称此系统为 单道批处理系统(Simple Batch Processing System)。图 1-4 示出了单道批处理系统的处理 流程。 第一章 操作系统引论 ·7· 开始 还有下 一个作业? 否 停止 是 把下一个作业的 源程序转换为目 标程序 是 源程序 有错吗? 运行 目标程序 否 装配 目标程序 图 1-4 单道批处理系统的处理流程 由上所述不难看出,单道批处理系统是在解决人机矛盾以及 CPU 与 I/O 设备速度不匹 配问题的过程中形成的。换言之,批处理系统旨在提高系统资源的利用率和系统吞吐量。 但这种单道批处理系统仍然不能很好地利用系统资源,故现已很少使用。 2.单道批处理系统的特征 单道批处理系统是最早出现的一种 OS。严格地说,它只能算作是 OS 的前身而并非是 现在人们所理解的 OS。尽管如此,该系统比起人工操作方式的系统已有很大进步。该系统 的主要特征如下: (1) 自动性。在顺利情况下,在磁带上的一批作业能自动地逐个地依次运行,而无需人 工干预。 (2) 顺序性。磁带上的各道作业是顺序地进入内存,各道作业的完成顺序与它们进入内 存的顺序,在正常情况下应完全相同,亦即先调入内存的作业先完成。 (3) 单道性。内存中仅有一道程序运行,即监督程序每次从磁带上只调入一道程序进入 内存运行,当该程序完成或发生异常情况时,才换入其后继程序进入内存运行。 1.2.3 多道批处理系统 20 世纪 60 年代中期,人们开始利用小规模集成电路来制作计算机,生产出第三代计算 机。由 IBM 公司生产的第一台小规模集成电路计算机——360 机,较之于晶体管计算机, 无论在体积、功耗、速度和可靠性上,都有了显著的改善。虽然在开发 360 机器使用的操 作系统时,为能在机器上运行多道程序而遇到了极大的困难,但最终还是成功地开发出能 在一台机器中运行多道程序的操作系统 OS/360。 1.多道程序设计的基本概念 在单道批处理系统中,内存中仅有一道作业,它无法充分利用系统中的所有资源,致 使系统性能较差。为了进一步提高资源的利用率和系统吞吐量,在 20 世纪 60 年代中期又 引入了多道程序设计技术,由此而形成了多道批处理系统(Multiprogrammed Batch Processing System)。在该系统中,用户所提交的作业都先存放在外存上并排成一个队列,称为“后备 队列”;然后,由作业调度程序按一定的算法从后备队列中选择若干个作业调入内存,使它 ·8· 计算机操作系统 们共享 CPU 和系统中的各种资源。具体地说,在 OS 中引入多道程序设计技术可带来以下 好处: (1) 提高 CPU 的利用率。当内存中仅有一道程序时,每逢该程序在运行中发出 I/O 请 求后,CPU 空闲,必须在其 I/O 完成后 CPU 才继续运行;尤其因 I/O 设备的低速性,更使 CPU 的利用率显著降低。图 1-5(a)示出了单道程序的运行情况,从图中可以看出:在 t2~t3、 t6~t7 时间间隔内 CPU 空闲。在引入多道程序设计技术后,由于同时在内存中装有若干道程 序,并使它们交替地运行,这样,当正在运行的程序因 I/O 而暂停执行时,系统可调度另一 道程序运行,从而保持了 CPU 处于忙碌状态。图 1-5(b)示出了四道程序时的运行情况。 用户程序 监督程序 I/O 中断请求 启动 I/O I/O 操作 t1 t2 I/O 中断请求 I/O 完成 结束中断 启动 I/O t3 t4 t5 t6 I/O 完成 结束中断 t7 t8 (a) 单道程序运行情况 程序A 程序B 程序C 程序D 调度程序 程序A I/O请求 程序A I/O完成 程序A再被调度 程序A A完成 程序B I/O请求 程序B 程序B I/O完成 程序C I/O请求 C I/O完成 C 再被调度 程序C 程序D I/O请求 程序D (b) 四道程序运行情况 图 1-5 单道和多道程序运行情况 (2) 可提高内存和 I/O 设备利用率。为了能运行较大的作业,通常内存都具有较大容量, 但由于 80%以上的作业都属于中小型,因此在单道程序环境下,也必定造成内存的浪费。 类似地,对于系统中所配置的多种类型的 I/O 设备,在单道程序环境下也不能充分利用。如 果允许在内存中装入多道程序,并允许它们并发执行,则无疑会大大提高内存和 I/O 设备的 利用率。 (3) 增加系统吞吐量。在保持 CPU、I/O 设备不断忙碌的同时,也必然会大幅度地提高 系统的吞吐量,从而降低作业加工所需的费用。 2.多道批处理系统的优缺点 虽然早在 20 世纪 60 年代就已出现了多道批处理系统,但至今它仍是三大基本操作系 统类型之一。在大多数大、中、小型机中都配置了它,说明它具有其它类型 OS 所不具有的 优点。多道批处理系统的主要优缺点如下: (1) 资源利用率高。由于在内存中驻留了多道程序,它们共享资源,可保持资源处于忙 碌状态,从而使各种资源得以充分利用。 (2) 系统吞吐量大。系统吞吐量是指系统在单位时间内所完成的总工作量。能提高系统 第一章 操作系统引论 ·9· 吞吐量的主要原因可归结为:第一,CPU 和其它资源保持“忙碌”状态; 第二,仅当作业 完成时或运行不下去时才进行切换,系统开销小。 (3) 平均周转时间长。作业的周转时间是指从作业进入系统开始,直至其完成并退出系 统为止所经历的时间。在批处理系统中,由于作业要排队,依次进行处理,因而作业的周 转时间较长,通常需几个小时,甚至几天。 (4) 无交互能力。用户一旦把作业提交给系统后,直至作业完成,用户都不能与自己的 作业进行交互,这对修改和调试程序是极不方便的。 3.多道批处理系统需要解决的问题 多道批处理系统是一种有效、但十分复杂的系统。为使系统中的多道程序间能协调地 运行,必须解决下述一系列问题。 (1) 处理机管理问题。在多道程序之间,应如何分配被它们共享的处理机,使 CPU 既 能满足各程序运行的需要,又能提高处理机的利用率,以及一旦把处理机分配给某程序后, 又应在何时收回等一系列问题,属于处理机管理问题。 (2) 内存管理问题。应如何为每道程序分配必要的内存空间,使它们“各得其所”且不 致因相互重叠而丢失信息,以及应如何防止因某道程序出现异常情况而破坏其它程序等问 题,就是内存管理问题。 (3) I/O 设备管理问题。系统中可能具有多种类型的 I/O 设备供多道程序所共享,应如 何分配这些 I/O 设备,如何做到既方便用户对设备的使用,又能提高设备的利用率,这就是 I/O 设备管理问题。 (4) 文件管理问题。在现代计算机系统中,通常都存放着大量的程序和数据(以文件形 式存在),应如何组织这些程序和数据,才能使它们既便于用户使用,又能保证数据的安全 性和一致性,这些属于文件管理问题。 (5) 作业管理问题。对于系统中的各种应用程序,其中有的属于计算型,即以计算为主 的程序;有的属于 I/O 型,即以 I/O 为主的程序;又有些作业既重要又紧迫;而有的作业则 要求系统能及时响应,这时应如何组织这些作业,这便是作业管理问题。 为此,应在计算机系统中增加一组软件,用以对上述问题进行妥善、有效的处理。这 组软件应包括:能控制和管理四大资源的软件,合理地对各类作业进行调度的软件,以及 方便用户使用计算机的软件。正是这样一组软件构成了操作系统。据此,我们可把操作系 统定义为:操作系统是一组控制和管理计算机硬件和软件资源,合理地对各类作业进行调 度,以及方便用户使用的程序的集合。 1.2.4 分时系统 1.分时系统的产生 分时系统(Time Sharing System)与多道批处理系统之间有着截然不同的性能差别,它能 很好地将一台计算机提供给多个用户同时使用,提高计算机的利用率。它被经常应用于查 询系统中,满足许多查询用户的需要。用户的需求具体表现在以下几个方面: (1) 人-机交互。每当程序员写好一个新程序时,都需要上机进行调试。由于新编程序 难免有些错误或不当之处需要修改,因而希望能像早期使用计算机时一样对它进行直接控 ·10· 计算机操作系统 制,并能以边运行边修改的方式,对程序中的错误进行修改,亦即,希望能进行人-机交互。 (2) 共享主机。在 20 世纪 60 年代计算机非常昂贵,不可能像现在这样每人独占一台微 机,而只能是由多个用户共享一台计算机,但用户在使用机器时应能够像自己独占计算机 一样,不仅可以随时与计算机交互,而且应感觉不到其他用户也在使用该计算机。 (3) 便于用户上机。在多道批处理系统中,用户上机前必须把自己的作业邮寄或亲自送 到机房。这对于用户尤其是远地用户来说是十分不便的。用户希望能通过自己的终端直接 将作业传送到机器上进行处理,并能对自己的作业进行控制。 由上所述不难得知,分时系统是指,在一台主机上连接了多个带有显示器和键盘的终 端,同时允许多个用户通过自己的终端,以交互方式使用计算机,共享主机中的资源。 第一台真正的分时操作系统(CTSS,Compatable Time Sharing System)是由麻省理工学院 开发成功的。继 CTSS 成功后,麻省理工学院又和贝尔实验室、通用电气公司联合开发出多 用户多任务操作系统——MULTICS,该机器能支持数百用户。值得一提的是,参加 MULTICS 研制的贝尔实验室的 Ken Thempson,在 PDP-7 小型机上开发出一个简化的 MULTICS 版本, 它就是当今广为流行的 UNIX 操作系统的前身。 2.分时系统实现中的关键问题 为实现分时系统,必须解决一系列问题。其中最关键的问题是如何使用户能与自己的 作业进行交互,即当用户在自己的终端上键入命令时,系统应能及时接收并及时处理该命 令,再将结果返回给用户。此后,用户可继续键入下一条命令,此即人-机交互。应强调指 出,即使有多个用户同时通过自己的键盘键入命令,系统也应能全部地及时接收并处理这 些命令。 (1) 及时接收。要及时接收用户键入的命令或数据并不困难,为此,只需在系统中配置 一个多路卡。例如,当要在主机上连接 8 个终端时,须配置一个 8 用户的多路卡。多路卡 的作用是使主机能同时接收各用户从终端上输入的数据。此外,还须为每个终端配置一个 缓冲区,用来暂存用户键入的命令(或数据)。 (2) 及时处理。人机交互的关键,是使用户键入命令后能及时地控制自己作业的运行, 或修改自己的作业。为此,各个用户的作业都必须在内存中,且应能频繁地获得处理机而 运行;否则,用户键入的命令将无法作用到自己的作业上。前面介绍的批处理系统是无法 实现人机交互的。因为通常大多数作业都还驻留在外存上,即使是已调入内存的作业,也 经常要经过较长时间的等待后方能运行,因而使用户键入的命令很难及时作用到自己的作 业上。 由此可见,为实现人机交互,必须彻底地改变原来批处理系统的运行方式。首先,用 户作业不能先进入磁盘,然后再调入内存。因为作业在磁盘上不能运行,当然用户也无法 与机器交互,因此,作业应直接进入内存。其次,不允许一个作业长期占用处理机,直至 它运行结束或出现 I/O 请求后,方才调度其它作业运行。为此,应该规定每个作业只运行一 个很短的时间(例如 0.1 秒钟,通常把这段时间称为时间片),然后便暂停该作业的运行,并 立即调度下一个程序运行。如果在不长的时间(如 3 秒)内能使所有的用户作业都执行一次 (一个时间片的时间),便可使每个用户都能及时地与自己的作业交互,从而可使用户的请求 得到及时响应。 第一章 操作系统引论 ·11· 3.分时系统的特征 分时系统与多道批处理系统相比,具有非常明显的不同特征,由上所述可以归纳成以 下四个特点: (1) 多路性。允许在一台主机上同时联接多台联机终端,系统按分时原则为每个用户服 务。宏观上,是多个用户同时工作,共享系统资源;而微观上,则是每个用户作业轮流运 行一个时间片。多路性即同时性,它提高了资源利用率,降低了使用费用,从而促进了计 算机更广泛的应用。 (2) 独立性。每个用户各占一个终端,彼此独立操作,互不干扰。因此,用户所感觉到 的,就像是他一人独占主机。 (3) 及时性。用户的请求能在很短的时间内获得响应。此时间间隔是以人们所能接受的 等待时间来确定的,通常仅为 1~3 秒钟。 (4) 交互性。用户可通过终端与系统进行广泛的人机对话。其广泛性表现在:用户可以 请求系统提供多方面的服务,如文件编辑、数据处理和资源共享等。 1.2.5 实时系统 所谓“实时”,是表示“及时”,而实时系统(Real Time System)是指系统能及时(或即时) 响应外部事件的请求,在规定的时间内完成对该事件的处理,并控制所有实时任务协调一 致地运行。 1.应用需求 虽然多道批处理系统和分时系统已能获得较为令人满意的资源利用率和响应时间,从 而使计算机的应用范围日益扩大,但它们仍然不能满足以下某些应用领域的需要。 (1) 实时控制。当把计算机用于生产过程的控制,以形成以计算机为中心的控制系统时, 系统要求能实时采集现场数据,并对所采集的数据进行及时处理,进而自动地控制相应的 执行机构,使某些(个)参数(如温度、压力、方位等)能按预定的规律变化,以保证产品的质 量和提高产量。类似地,也可将计算机用于对武器的控制,如火炮的自动控制系统、飞机 的自动驾驶系统,以及导弹的制导系统等。此外,随着大规模集成电路的发展,已制作出 各种类型的芯片,并可将这些芯片嵌入到各种仪器和设备中,用来对设备的工作进行实时 控制,这就构成了所谓的智能仪器和设备。在这些设备中也需要配置某种类型的、能进行 实时控制的系统。通常把用于进行实时控制的系统称为实时系统。 (2) 实时信息处理。通常,人们把用于对信息进行实时处理的系统称为实时信息处理系 统。该系统由一台或多台主机通过通信线路连接到成百上千个远程终端上,计算机接收从 远程终端上发来的服务请求,根据用户提出的请求对信息进行检索和处理,并在很短的时 间内为用户做出正确的响应。典型的实时信息处理系统有早期的飞机或火车的订票系统、 情报检索系统等。 2.实时任务 在实时系统中必然存在着若干个实时任务,这些任务通常与某个(些)外部设备相关,能 反应或控制相应的外部设备,因而带有某种程度的紧迫性。可从不同的角度对实时任务加 以分类。 ·12· 计算机操作系统 1) 按任务执行时是否呈现周期性来划分 (1) 周期性实时任务。外部设备周期性地发出激励信号给计算机,要求它按指定周期循 环执行,以便周期性地控制某外部设备。 (2) 非周期性实时任务。外部设备所发出的激励信号并无明显的周期性,但都必须联系 着一个截止时间(Deadline)。它又可分为开始截止时间(某任务在某时间以前必须开始执行) 和完成截止时间(某任务在某时间以前必须完成)两部分。 2) 根据对截止时间的要求来划分 (1) 硬实时任务(Hard real-time Task)。系统必须满足任务对截止时间的要求,否则可能 出现难以预测的结果。 (2) 软实时任务(Soft real-time Task)。它也联系着一个截止时间,但并不严格,若偶尔 错过了任务的截止时间,对系统产生的影响也不会太大。 3.实时系统与分时系统特征的比较 实时系统有着与分时系统相似但并不完全相同的特点,下面从五个方面对这两种系统 加以比较。 (1) 多路性。实时信息处理系统也按分时原则为多个终端用户服务。实时控制系统的多 路性则主要表现在系统周期性地对多路现场信息进行采集,以及对多个对象或多个执行机 构进行控制。而分时系统中的多路性则与用户情况有关,时多时少。 (2) 独立性。实时信息处理系统中的每个终端用户在向实时系统提出服务请求时,是彼 此独立地操作,互不干扰;而实时控制系统中,对信息的采集和对对象的控制也都是彼此 互不干扰。 (3) 及时性。实时信息处理系统对实时性的要求与分时系统类似,都是以人所能接受的 等待时间来确定的;而实时控制系统的及时性,则是以控制对象所要求的开始截止时间或 完成截止时间来确定的,一般为秒级到毫秒级,甚至有的要低于 100 微秒。 (4) 交互性。实时信息处理系统虽然也具有交互性,但这里人与系统的交互仅限于访问 系统中某些特定的专用服务程序。它不像分时系统那样能向终端用户提供数据处理和资源 共享等服务。 (5) 可靠性。分时系统虽然也要求系统可靠,但相比之下,实时系统则要求系统具有高 度的可靠性。因为任何差错都可能带来巨大的经济损失,甚至是无法预料的灾难性后果, 所以在实时系统中,往往都采取了多级容错措施来保障系统的安全性及数据的安全性。 1.2.6 微机操作系统的发展 随着 VLSI 和计算机体系结构的发展,以及应用需求的不断扩大,操作系统仍在继续发 展。由此先后形成了微机操作系统、网络操作系统等。本小节将对微机操作系统的发展作 扼要的介绍。 配置在微型机上的操作系统称为微机操作系统。最早诞生的微机操作系统是配置在 8 位微机上的 CP/M。后来出现了 16 位微机,相应地,16 位微机操作系统也就应运而生。 当微机发展为 32 位、64 位时,32 位和 64 位微机操作系统也应运而生。可见,微机操作系 统可按微机的字长来分,但也可将它按运行方式分为如下几类: 第一章 操作系统引论 ·13· 1.单用户单任务操作系统 单用户单任务操作系统的含义是,只允许一个用户上机,且只允许用户程序作为一个 任务运行。这是最简单的微机操作系统,主要配置在 8 位和 16 位微机上。最有代表性的单 用户单任务微机操作系统是 CP/M 和 MS-DOS。 1) CP/M 1974 年第一代通用 8 位微处理机芯片 Intel 8080 出现后的第二年,Digital Research 公司 就开发出带有软盘系统的 8 位微机操作系统。1977 年 Digital Research 公司对 CP/M 进行了 重写,使其可配置在以 Intel 8080、8085、Z80 等 8 位芯片为基础的多种微机上。1979 年又 推出带有硬盘管理功能的 CP/M 2.2 版本。由于 CP/M 具有较好的体系结构,可适应性强, 且具有可移植性以及易学易用等优点,使之在 8 位微机中占据了统治地位。 2) MS-DOS 1981 年 IBM 公司首次推出了 IBM-PC 个人计算机(16 位微机),在微机中采用了微软公 司开发的 MS-DOS(Disk Operating System)操作系统,该操作系统在 CP/M 的基础上进行了较 大的扩充,使其在功能上有很大的增强。1983 年 IBM 推出 PC/AT(配有 Intel 80286 芯片), 相应地,微软又开发出 MS-DOS 2.0 版本,它不仅能支持硬盘设备,还采用了树形目录结构 的文件系统。1987 年又宣布了 MS-DOS 3.3 版本。从 MS-DOS 1.0 到 3.3 为止的 DOS 版本 都属于单用户单任务操作系统,内存被限制在 640 KB。从 1989 年到 1993 年又先后推出了 多个 MS-DOS 版本,它们都可以配置在 Intel 80386、80486 等 32 位微机上。从 20 世纪 80 年代到 90 年代初,由于 MS-DOS 性能优越而受到当时用户的广泛欢迎,成为事实上的 16 位单用户单任务操作系统标准。 2.单用户多任务操作系统 单用户多任务操作系统的含义是,只允许一个用户上机,但允许用户把程序分为若干 个任务,使它们并发执行,从而有效地改善了系统的性能。目前在 32 位微机上配置的操作 系统基本上都是单用户多任务操作系统,其中最有代表性的是由微软公司推出的 Windows。 1985 年和 1987 年微软公司先后推出了 Windows 1.0 和 Windows 2.0 版本操作系统,由于当 时的硬件平台还只是 16 位微机,对 1.0 和 2.0 版本不能很好的支持。1990 年微软公司又发 布了 Windows 3.0 版本,随后又宣布了 Windows 3.1 版本,它们主要是针对 386 和 486 等 32 位微机开发的,较之以前的操作系统有着重大的改进,引入了友善的图形用户界面,支持 多任务和扩展内存的功能,使计算机更好使用,从而成为 386 和 486 等微机的主流操作 系统。 1995 年微软公司推出了 Windows 95,它较之以前的 Windows 3.1 有许多重大改进,采 用了全 32 位的处理技术,并兼容以前的 16 位应用程序,在该系统中还集成了支持 Internet 的网络功能。1998 年微软公司又推出了 Windows 95 的改进版 Windows 98,它已是最后一 个仍然兼容以前的 16 位应用程序的 Windows,其最主要的改进是把微软公司自己开发的 Internet 浏览器整合到系统中,大大方便了用户上网浏览,另一个特点是增加了对多媒体的 支持。2001 年微软又发布了 32 位版本的 Windows XP,同时提供了家用和商业工作站两种 版本,它是当前使用最广泛的个人操作系统。2001 年还发布了 64 位版本的 Windows XP。 在开发上述 Windows 操作系统的同时,微软公司又开始开发网络操作系统 Windows ·14· 计算机操作系统 NT,它是针对网络开发的操作系统,在系统中融入了许多面向网络的功能,这里就不对它 进行详细介绍了。 3.多用户多任务操作系统 多用户多任务操作系统的含义是,允许多个用户通过各自的终端使用同一台机器,共 享主机系统中的各种资源,而每个用户程序又可进一步分为几个任务,使它们能并发执行, 从而可进一步提高资源利用率和系统吞吐量。在大、中和小型机中所配置的大多是多用户 多任务操作系统,而在 32 位微机上也有不少是配置的多用户多任务操作系统,其中最有代 表性的是 UNIX OS。 UNIX OS 是美国电报电话公司的 Bell 实验室在 1969~1970 年期间开发的,1979 年推 出来的 UNIX V.7 已被广泛应用于多种中、小型机上。随着微机性能的提高,人们又将 UNIX 移植到微机上。在 1980 年前后,将 UNIX 第 7 版本移植到 Motorola 公司的 MC 680xx 微机 上,后来又将 UNIX V7.0 版本进行简化后移植到 Intel 8080 上,把它称为 Xenix。现在最有 影响的两个能运行在微机上的 UNIX 操作系统的变型是 Solaris OS 和 Linux OS。 (1) Solaris OS:SUN 公司于 1982 年推出的 SUN OS 1.0 是一个运行在 Motorola 680x0 平台上的 UNIX OS。在 1988 年宣布的 SUN OS 4.0 把运行平台从早期的 Motorola 680x0 平 台迁移到 SPARC 平台,并开始支持 Intel 公司的 Intel 80x86;1992 年 SUN 发布了 Solaris 2.0。 从 1998 年开始,Sun 公司推出 64 位操作系统 Solaris 2.7 和 2.8,这几款操作系统在网络特 性、互操作性、兼容性以及易于配置和管理方面均有很大的提高。 (2) Linux OS:Linux 是 UNIX 的一个重要变种,最初是由芬兰学生 Linus Torvalds 针 对 Intel 80386 开发的。1991 年在 Internet 网上发布第一个 Linux 版本,由于源代码公开,因 此有很多人通过 Internet 与之合作,使 Linux 的性能迅速提高,其应用范围也日益扩大。相 应地,源代码也急剧膨胀,此时它已是具有全面功能的 UNIX 系统,大量在 UNIX 上运行 的软件(包括 1000 多种实用工具软件和大量的网络软件)被移植到 Linux 上,而且可以在主 要的微机上运行,如 Intel 80x86 Pentium 等。 1.3 操作系统的基本特性 前面所介绍的三种基本操作系统都各自有着自己的特征,如批处理系统具有能对多个 作业进行成批处理,以获得高的系统吞吐量的特征,分时系统具有允许用户和计算机进行 人机交互特征,实时系统具有实时特征,但它们也都具有并发、共享、虚拟和异步这四个 基本特征。其中,并发特征是操作系统最重要的特征,其它三个特征都是以并发特征为前 提的。 1.3.1 并发性 1.并行与并发 并行性和并发性(Concurrence)是既相似又有区别的两个概念,并行性是指两个或多个 事件在同一时刻发生;而并发性是指两个或多个事件在同一时间间隔内发生。在多道程序 环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中, 第一章 操作系统引论 ·15· 每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。倘若在计算 机系统中有多个处理机,则这些可以并发执行的程序便可被分配到多个处理机上,实现并 行执行,即利用每个处理机来处理一个可并发执行的程序,这样,多个程序便可同时执行。 2.引入进程 应当指出,通常的程序是静态实体(Passive Entity),在多道程序系统中,它们是不能独 立运行的,更不能和其它程序并发执行。在操作系统中引入进程的目的,就是为了使多个 程序能并发执行。例如,在一个未引入进程的系统中,在属于同一个应用程序的计算程序 和 I/O 程序之间,两者只能是顺序执行,即只有在计算程序执行告一段落后,才允许 I/O 程 序执行;反之,在程序执行 I/O 操作时,计算程序也不能执行,这意味着处理机处于空闲状 态 。但在引入进程后,若分别为计算程序和 I/O 程序各建立一个进程,则这两个进程便可 并发执行。由于在系统中具备使计算程序和 I/O 程序同时运行的硬件条件,因而可将系统中 的 CPU 和 I/O 设备同时开动起来,实现并行工作,从而有效地提高了系统资源的利用率和 系统吞吐量,并改善了系统的性能。引入进程的好处远不止于此,事实上可以在内存中存 放多个用户程序,分别为它们建立进程后,这些进程可以并发执行,亦即实现前面所说的 多道程序运行。这样便能极大地提高系统资源的利用率,增加系统的吞吐量。 为使多个程序能并发执行,系统必须分别为每个程序建立进程(Process)。简单说来,进 程是指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆 栈等组成的,是一个能独立运行的活动实体。多个进程之间可以并发执行和交换信息。一 个进程在运行时需要一定的资源,如 CPU、存储空间及 I/O 设备等。 OS 中程序的并发执行将使系统复杂化,以致在系统中必须增设若干新的功能模块,分 别用于对处理机、内存、I/O 设备以及文件系统等资源进行管理,并控制系统中作业的运行。 事实上,进程和并发是现代操作系统中最重要的基本概念,也是操作系统运行的基础,故 我们将在本书第二章中做详细阐述。 3.引入线程 长期以来,进程都是操作系统中可以拥有资源并作为独立运行的基本单位。当一个进 程因故不能继续运行时,操作系统便调度另一进程运行。由于进程拥有自己的资源,故使 调度付出的开销较大。直到 20 世纪 80 年代中期,人们才又提出了比进程更小的单位—— 线程(Threads)。 通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线 程的 OS 中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度 的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销 就会小得多,能更高效地提高系统内多个程序间并发执行的程度。因而近年来推出的通用 操作系统都引入了线程,以便进一步提高系统的并发性,并把它视作现代操作系统的一个 重要标致。 1.3.2 共享性 在操作系统环境下,所谓共享(Sharing),是指系统中的资源可供内存中多个并发执行 的进程(线程)共同使用,相应地,把这种资源共同使用称为资源共享,或称为资源复用。由 ·16· 计算机操作系统 于各种资源的属性不同,进程对资源复用的方式也不同,目前主要实现资源共享的方式有 如下两种。 1.互斥共享方式 系统中的某些资源,如打印机、磁带机,虽然它们可以提供给多个进程(线程)使用,但 为使所打印或记录的结果不致造成混淆,应规定在一段时间内只允许一个进程(线程)访问该 资源。为此,系统中应建立一种机制,以保证对这类资源的互斥访问。当一个进程 A 要访 问某资源时,必须先提出请求。如果此时该资源空闲,系统便可将之分配给请求进程 A 使 用。此后若再有其它进程也要访问该资源时(只要 A 未用完),则必须等待。仅当 A 进程访 问完并释放该资源后,才允许另一进程对该资源进行访问。我们把这种资源共享方式称为 互斥式共享,而把在一段时间内只允许一个进程访问的资源称为临界资源或独占资源。计 算机系统中的大多数物理设备,以及某些软件中所用的栈、变量和表格,都属于临界资源, 它们要求被互斥地共享。为此,在系统中必需配置某种机制来保证诸进程互斥地使用独占 资源。 2.同时访问方式 系统中还有另一类资源,允许在一段时间内由多个进程“同时”对它们进行访问。这 里所谓的“同时”,在单处理机环境下往往是宏观上的,而在微观上,这些进程可能是交替 地对该资源进行访问。典型的可供多个进程“同时”访问的资源是磁盘设备,一些用重入 码编写的文件也可以被“同时”共享,即若干个用户同时访问该文件。 并发和共享是操作系统的两个最基本的特征,它们又是互为存在的条件。一方面,资 源共享是以程序(进程)的并发执行为条件的,若系统不允许程序并发执行,自然不存在资源 共享问题;另一方面,若系统不能对资源共享实施有效管理,协调好诸进程对共享资源的 访问,也必然影响到程序并发执行的程度,甚至根本无法并发执行。 1.3.3 虚拟技术 操作系统中的所谓“虚拟”(Virtual),是指通过某种技术把一个物理实体变为若干个 逻辑上的对应物。物理实体(前者)是实的,即实际存在的,而后者是虚的,仅是用户感觉上 的东西。相应地,用于实现虚拟的技术称为虚拟技术。在操作系统中利用了两种方式实现 虚拟技术,即时分复用技术和空分复用技术。 1.时分复用技术 时分复用,亦即分时使用方式,它最早用于电信业中。为了提高信道的利用率,人们 利用时分复用方式,将一条物理信道虚拟为多条逻辑信道,将每条信道供一对用户通话。 在计算机领域中,广泛利用该技术来实现虚拟处理机、虚拟设备等,以提高资源的利用率。 1) 虚拟处理机技术 在虚拟处理机技术中,利用多道程序设计技术,为每道程序建立一个进程,让多道程 序并发地执行,以此来分时使用一台处理机。此时,虽然系统中只有一台处理机,但它却 能同时为多个用户服务,使每个终端用户都认为是有一个处理机在专门为他服务。亦即, 利用多道程序设计技术,把一台物理上的处理机虚拟为多台逻辑上的处理机,在每台逻辑 处理机上运行一道程序。我们把用户所感觉到的处理机称为虚拟处理器。 第一章 操作系统引论 ·17· 2) 虚拟设备技术 我们还可以通过虚拟设备技术,将一台物理 I/O 设备虚拟为多台逻辑上的 I/O 设备,并 允许每个用户占用一台逻辑上的 I/O 设备,这样便可使原来仅允许在一段时间内由一个用户 访问的设备(即临界资源),变为在一段时间内允许多个用户同时访问的共享设备。例如,原 来的打印机属于临界资源,而通过虚拟设备技术,可以把它变为多台逻辑上的打印机,供 多个用户“同时”打印。关于虚拟设备技术将在第五章中介绍。 2.空分复用技术 早在上世纪初,电信业中就使用频分复用技术来提高信道的利用率。它是将一个频率 范围非常宽的信道,划分成多个频率范围较窄的信道,其中的任何一个频带都只供一对用 户通话。早期的频分复用只能将一条物理信道划分为十几条到几十条话路,后来又很快发 展成上万条话路,每条话路也只供一对用户通话。之后,在计算机中也使用了空分复用技 术来提高存储空间的利用率。 1) 虚拟磁盘技术 通常在一台机器上只配置一台硬盘。我们可以通过虚拟磁盘技术将一台硬盘虚拟为多 台虚拟磁盘,这样使用起来既方便又安全。虚拟磁盘技术也是采用了空分复用方式,即它 将硬盘划分为若干个卷,例如 1、2、3、4 四个卷,再通过安装程序将它们分别安装在 C、 D、E、F 四个逻辑驱动器上,这样,机器上便有了四个虚拟磁盘。当用户要访问 D 盘中的 内容时,系统便会访问卷 2 中的内容。 2) 虚拟存储器技术 在单道程序环境下,处理机会有很多空闲时间,内存也会有很多空闲空间,显然,这 会使处理机和内存的效率低下。如果说时分复用技术是利用处理机的空闲时间来运行其它 的程序,使处理机的利用率得以提高,那么空分复用则是利用存储器的空闲空间来存放其 它的程序,以提高内存的利用率。 但是,单纯的空分复用存储器只能提高内存的利用率,并不能实现在逻辑上扩大存储 器容量的功能,必须引入虚拟存储技术才能达到此目地。而虚拟存储技术在本质上就是使 内存分时复用。它可以使一道程序通过时分复用方式,在远小于它的内存空间中运行。 例 如,一个 100 MB 的应用程序可以运行在 20 MB 的内存空间。下一节将要介绍的用于实现 内存扩充的“请求调入功能”和“置换功能”就是用于每次只把用户程序的一部分调入内 存运行,这样便实现了用户程序的各个部分分时进入内存运行的功能。 应当着重指出:如果虚拟的实现是通过时分复用的方法来实现的,即对某一物理设备 进行分时使用,设 N 是某物理设备所对应的虚拟的逻辑设备数,则每台虚拟设备的平均速 度必然等于或低于物理设备速度的 1/N。类似地,如果是利用空分复用方法来实现虚拟,此 时一台虚拟设备平均占用的空间必然也等于或低于物理设备所拥有空间的 1/N。 1.3.4 异步性 在多道程序环境下允许多个进程并发执行,但只有进程在获得所需的资源后方能执行。 在单处理机环境下,由于系统中只有一台处理机,因而每次只允许一个进程执行,其余进 程只能等待。当正在执行的进程提出某种资源要求时,如打印请求,而此时打印机正在为 ·18· 计算机操作系统 其它某进程打印,由于打印机属于临界资源,因此正在执行的进程必须等待,且放弃处理 机,直到打印机空闲,并再次把处理机分配给该进程时,该进程方能继续执行。可见,由 于资源等因素的限制,使进程的执行通常都不是“一气呵成”,而是以“停停走走”的方式 运行。 内存中的每个进程在何时能获得处理机运行,何时又因提出某种资源请求而暂停,以 及进程以怎样的速度向前推进,每道程序总共需多少时间才能完成,等等,这些都是不可 预知的。由于各用户程序性能的不同,比如,有的侧重于计算而较少需要 I/O,而有的程序 其计算少而 I/O 多,这样,很可能是先进入内存的作业后完成,而后进入内存的作业先完成。 或者说,进程是以人们不可预知的速度向前推进,此即进程的异步性(Asynchronism)。尽管 如此,但只要在操作系统中配置有完善的进程同步机制,且运行环境相同,作业经多次运 行都会获得完全相同的结果。因此,异步运行方式是允许的,而且是操作系统的一个重要 特征。 1.4 操作系统的主要功能 操作系统的主要任务,是为多道程序的运行提供良好的运行环境,以保证多道程序能 有条不紊地、高效地运行,并能最大程度地提高系统中各种资源的利用率和方便用户的使 用。为实现上述任务,操作系统应具有这样几方面的功能:处理机管理,存储器管理,设 备管理和文件管理。为了方便用户使用操作系统,还须向用户提供方便的用户接口。此外, 由于当今的网络已相当普及,已有愈来愈多的计算机接入网络中,为了方便计算机联网, 又在 OS 中增加了面向网络的服务功能。 1.4.1 处理机管理功能 在传统的多道程序系统中,处理机的分配和运行都是以进程为基本单位,因而对处理 机的管理可归结为对进程的管理;在引入了线程的 OS 中,也包含对线程的管理。处理机管 理的主要功能是创建和撤消进程(线程),对诸进程(线程)的运行进行协调,实现进程(线程) 之间的信息交换,以及按照一定的算法把处理机分配给进程(线程)。 1.进程控制 在传统的多道程序环境下,要使作业运行,必须先为它创建一个或几个进程,并为之 分配必要的资源。当进程运行结束时,立即撤消该进程,以便能及时回收该进程所占用的 各类资源。进程控制的主要功能是为作业创建进程,撤消已结束的进程,以及控制进程在 运行过程中的状态转换。在现代 OS 中,进程控制还应具有为一个进程创建若干个线程的功 能和撤消(终止)已完成任务的线程的功能。 2.进程同步 前已述及,进程是以异步方式运行的,并以人们不可预知的速度向前推进。为使多个 进程能有条不紊地运行,系统中必须设置进程同步机制。进程同步的主要任务是为多个进 程(含线程)的运行进行协调。有两种协调方式: (1) 进程互斥方式。这是指诸进程(线程)在对临界资源进行访问时,应采用互斥方式; 第一章 操作系统引论 ·19· (2) 进程同步方式。这是指在相互合作去完成共同任务的诸进程(线程)间,由同步机构 对它们的执行次序加以协调。 为了实现进程同步,系统中必须设置进程同步机制。最简单的用于实现进程互斥的机 制是为每一个临界资源配置一把锁 W,当锁打开时,进程(线程)可以对该临界资源进行访问; 而当锁关上时,则禁止进程(线程)访问该临界资源。而实现进程同步的最常用的机制则是信 号量机制,我们将在第二章中做详细介绍。 3.进程通信 在多道程序环境下,为了加速应用程序的运行,应在系统中建立多个进程,并且再为 一个进程建立若干个线程,由这些进程(线程)相互合作去完成一个共同的任务。而在这些进 程(线程)之间,又往往需要交换信息。例如,有三个相互合作的进程,它们是输入进程、计 算进程和打印进程。输入进程负责将所输入的数据传送给计算进程;计算进程利用输入数 据进行计算,并把计算结果传送给打印进程;最后,由打印进程把计算结果打印出来。进 程通信的任务就是用来实现在相互合作的进程之间的信息交换。 当相互合作的进程(线程)处于同一计算机系统时,通常在它们之间是采用直接通信方 式,即由源进程利用发送命令直接将消息(Message)挂到目标进程的消息队列上,以后由目 标进程利用接收命令从其消息队列中取出消息。 4.调度 在后备队列上等待的每个作业都需经过调度才能执行。在传统的操作系统中,包括作 业调度和进程调度两步。 (1) 作业调度。作业调度的基本任务是从后备队列中按照一定的算法,选择出若干个 作业,为它们分配运行所需的资源(首先是分配内存)。在将它们调入内存后,便分别为它 们建立进程,使它们都成为可能获得处理机的就绪进程,并按照一定的算法将它们插入就 绪队列。 (2) 进程调度。进程调度的任务是从进程的就绪队列中,按照一定的算法选出一个进程, 把处理机分配给它,并为它设置运行现场,使进程投入执行。值得提出的是,在多线程 OS 中,通常是把线程作为独立运行和分配处理机的基本单位,为此,须把就绪线程排成一个 队列,每次调度时,是从就绪线程队列中选出一个线程,把处理机分配给它。 1.4.2 存储器管理功能 存储器管理的主要任务是为多道程序的运行提供良好的环境,方便用户使用存储器, 提高存储器的利用率以及能从逻辑上扩充内存。为此,存储器管理应具有内存分配、内存 保护、地址映射和内存扩充等功能。 1.内存分配 内存分配的主要任务是为每道程序分配内存空间,使它们“各得其所”;提高存储器的 利用率,以减少不可用的内存空间;允许正在运行的程序申请附加的内存空间,以适应程 序和数据动态增长的需要。 OS 在实现内存分配时,可采取静态和动态两种方式。在静态分配方式中,每个作业的 内存空间是在作业装入时确定的;在作业装入后的整个运行期间,不允许该作业再申请新 ·20· 计算机操作系统 的内存空间,也不允许作业在内存中“移动”。在动态分配方式中,每个作业所要求的基本 内存空间也是在装入时确定的,但允许作业在运行过程中继续申请新的附加内存空间,以 适应程序和数据的动态增长,也允许作业在内存中“移动”。 为了实现内存分配,在内存分配的机制中应具有这样的结构和功能: (1) 内存分配数据结构。该结构用于记录内存空间的使用情况,作为内存分配的依据; (2) 内存分配功能。系统按照一定的内存分配算法为用户程序分配内存空间; (3) 内存回收功能。系统对于用户不再需要的内存,通过用户的释放请求去完成系统的 回收功能。 2.内存保护 内存保护的主要任务是确保每道用户程序都只在自己的内存空间内运行,彼此互不干 扰;绝不允许用户程序访问操作系统的程序和数据;也不允许用户程序转移到非共享的其 它用户程序中去执行。 为了确保每道程序都只在自己的内存区中运行,必须设置内存保护机制。一种比较简 单的内存保护机制是设置两个界限寄存器,分别用于存放正在执行程序的上界和下界。系 统须对每条指令所要访问的地址进行检查,如果发生越界,便发出越界中断请求,以停止 该程序的执行。如果这种检查完全用软件实现,则每执行一条指令,便须增加若干条指令 去进行越界检查,这将显著降低程序的运行速度。因此,越界检查都由硬件实现。当然, 对发生越界后的处理,还须与软件配合来完成。 3.地址映射 一个应用程序(源程序)经编译后,通常会形成若干个目标程序;这些目标程序再经过链 接便形成了可装入程序。这些程序的地址都是从“0”开始的,程序中的其它地址都是相对 于起始地址计算的。由这些地址所形成的地址范围称为“地址空间”,其中的地址称为“逻 辑地址”或“相对地址”。此外,由内存中的一系列单元所限定的地址范围称为“内存空间”, 其中的地址称为“物理地址”。 在多道程序环境下,每道程序不可能都从“0”地址开始装入(内存),这就致使地址空 间内的逻辑地址和内存空间中的物理地址不相一致。为使程序能正确运行,存储器管理必 须提供地址映射功能,以将地址空间中的逻辑地址转换为内存空间中与之对应的物理地址。 该功能应在硬件的支持下完成。 4.内存扩充 存储器管理中的内存扩充任务并非是去扩大物理内存的容量,而是借助于虚拟存储技 术,从逻辑上去扩充内存容量,使用户所感觉到的内存容量比实际内存容量大得多,以便 让更多的用户程序并发运行。这样,既满足了用户的需要,又改善了系统的性能。为此, 只需增加少量的硬件。为了能在逻辑上扩充内存,系统必须具有内存扩充机制,用于实现 下述各功能: (1) 请求调入功能。允许在装入一部分用户程序和数据的情况下,便能启动该程序运行。 在程序运行过程中,若发现要继续运行时所需的程序和数据尚未装入内存,可向 OS 发出请 求,由 OS 从磁盘中将所需部分调入内存,以便继续运行。 (2) 置换功能。若发现在内存中已无足够的空间来装入需要调入的程序和数据时,系统 第一章 操作系统引论 ·21· 应能将内存中的一部分暂时不用的程序和数据调至盘上,以腾出内存空间,然后再将所需 调入的部分装入内存。 1.4.3 设备管理功能 设备管理用于管理计算机系统中所有的外围设备,而设备管理的主要任务是:完成用 户进程提出的 I/O 请求;为用户进程分配其所需的 I/O 设备;提高 CPU 和 I/O 设备的利用 率;提高 I/O 速度;方便用户使用 I/O 设备。为实现上述任务,设备管理应具有缓冲管理、 设备分配和设备处理以及虚拟设备等功能。 1.缓冲管理 CPU 运行的高速性和 I/O 低速性间的矛盾自计算机诞生时起便已存在了。而随着 CPU 速度迅速提高,使得此矛盾更为突出,严重降低了 CPU 的利用率。如果在 I/O 设备和 CPU 之间引入缓冲,则可有效地缓和 CPU 与 I/O 设备速度不匹配的矛盾,提高 CPU 的利用率, 进而提高系统吞吐量。因此,在现代计算机系统中,都无一例外地在内存中设置了缓冲区, 而且还可通过增加缓冲区容量的方法来改善系统的性能。 对于不同的系统,可以采用不同的缓冲区机制。最常见的缓冲区机制有单缓冲机制、 能实现双向同时传送数据的双缓冲机制,以及能供多个设备同时使用的公用缓冲池机制。 上述这些缓冲区都将由 OS 中的缓冲管理机制将它们管理起来。 2.设备分配 设备分配的基本任务是根据用户进程的 I/O 请求、系统的现有资源情况以及按照某种设 备的分配策略,为之分配其所需的设备。如果在 I/O 设备和 CPU 之间还存在着设备控制器 和 I/O 通道时,还须为分配出去的设备分配相应的控制器和通道。 为了实现设备分配,系统中应设置设备控制表、控制器控制表等数据结构,用于记录 设备及控制器的标识符和状态。根据这些表格可以了解指定设备当前是否可用,是否忙碌, 以供进行设备分配时参考。在进行设备分配时,应针对不同的设备类型而采用不同的设备 分配方式。对于独占设备(临界资源)的分配,还应考虑到该设备被分配出去后系统是否安全。 在设备使用完后,应立即由系统回收。 3.设备处理 设备处理程序又称为设备驱动程序。其基本任务是用于实现 CPU 和设备控制器之间的 通信,即由 CPU 向设备控制器发出 I/O 命令,要求它完成指定的 I/O 操作;反之,由 CPU 接收从控制器发来的中断请求,并给予迅速的响应和相应的处理。 处理过程是:设备处理程序首先检查 I/O 请求的合法性,了解设备状态是否是空闲的, 了解有关的传递参数及设置设备的工作方式。然后,便向设备控制器发出 I/O 命令,启动 I/O 设备去完成指定的 I/O 操作。设备驱动程序还应能及时响应由控制器发来的中断请求,并根 据该中断请求的类型,调用相应的中断处理程序进行处理。对于设置了通道的计算机系统, 设备处理程序还应能根据用户的 I/O 请求,自动地构成通道程序。 1.4.4 文件管理功能 在现代计算机管理中,总是把程序和数据以文件的形式存储在磁盘和磁带上,供所有 ·22· 计算机操作系统 的或指定的用户使用。为此,在操作系统中必须配置文件管理机构。文件管理的主要任务 是对用户文件和系统文件进行管理,以方便用户使用,并保证文件的安全性。为此,文件 管理应具有对文件存储空间的管理、目录管理、文件的读/写管理,以及文件的共享与保护 等功能。 1.文件存储空间的管理 为了方便用户的使用,对于一些当前需要使用的系统文件和用户文件,都必须放在可 随机存取的磁盘上。在多用户环境下,若由用户自己对文件的存储进行管理,不仅非常困 难,而且也必然是十分低效的。因而,需要由文件系统对诸多文件及文件的存储空间实施 统一的管理。其主要任务是为每个文件分配必要的外存空间,提高外存的利用率,并能有 助于提高文件系统的存、取速度。 为此,系统应设置相应的数据结构,用于记录文件存储空间的使用情况,以供分配存 储空间时参考;系统还应具有对存储空间进行分配和回收的功能。为了提高存储空间的利 用率,对存储空间的分配,通常是采用离散分配方式,以减少外存零头,并以盘块为基本 分配单位。盘块的大小通常为 1~8 KB。 2.目录管理 为了使用户能方便地在外存上找到自己所需的文件,通常由系统为每个文件建立一个 目录项。目录项包括文件名、文件属性、文件在磁盘上的物理位置等。由若干个目录项又 可构成一个目录文件。目录管理的主要任务是为每个文件建立其目录项,并对众多的目录 项加以有效的组织,以实现方便的按名存取,即用户只须提供文件名便可对该文件进行存 取。其次,目录管理还应能实现文件共享,这样,只须在外存上保留一份该共享文件的副 本。此外,还应能提供快速的目录查询手段,以提高对文件的检索速度。 3.文件的读/写管理和保护 (1) 文件的读/写管理。该功能是根据用户的请求,从外存中读取数据,或将数据写入 外存。在进行文件读(写)时,系统先根据用户给出的文件名去检索文件目录,从中获得文件 在外存中的位置。然后,利用文件读(写)指针,对文件进行读(写)。一旦读(写)完成,便修 改读(写)指针,为下一次读(写)做好准备。由于读和写操作不会同时进行,故可合用一个 读/写指针。 (2) 文件保护。为了防止系统中的文件被非法窃取和破坏,在文件系统中必须提供有效 的存取控制功能,以实现下述目标: ① 防止未经核准的用户存取文件; ② 防止冒名顶替存取文件; ③ 防止以不正确的方式使用文件。 1.4.5 操作系统与用户之间的接口 为了方便用户使用操作系统,OS 又向用户提供了“用户与操作系统的接口”。该接口 通常可分为两大类: (1) 用户接口。它是提供给用户使用的接口,用户可通过该接口取得操作系统的服务; (2) 程序接口。它是提供给程序员在编程时使用的接口,是用户程序取得操作系统服务 第一章 操作系统引论 ·23· 的惟一途径。 1.用户接口 为了便于用户直接或间接地控制自己的作业,操作系统向用户提供了命令接口。用户 可通过该接口向作业发出命令以控制作业的运行。该接口又进一步分为联机用户接口和脱 机用户接口。 (1) 联机用户接口。这是为联机用户提供的,它由一组键盘操作命令及命令解释程序所 组成。当用户在终端或控制台上每键入一条命令后,系统便立即转入命令解释程序,对该 命令加以解释并执行该命令。在完成指定功能后,控制又返回到终端或控制台上,等待用 户键入下一条命令。这样,用户可通过先后键入不同命令的方式,来实现对作业的控制, 直至作业完成。 (2) 脱机用户接口。该接口是为批处理作业的用户提供的,故也称为批处理用户接口。 该接口由一组作业控制语言(JCL)组成。批处理作业的用户不能直接与自己的作业交互作用, 只能委托系统代替用户对作业进行控制和干预。这里的作业控制语言(JCL)便是提供给批处 理作业用户的、为实现所需功能而委托系统代为控制的一种语言。用户用 JCL 把需要对作 业进行的控制和干预事先写在作业说明书上,然后将作业连同作业说明书一起提供给系统。 当系统调度到该作业运行时,又调用命令解释程序,对作业说明书上的命令逐条地解释执 行。如果作业在执行过程中出现异常现象,系统也将根据作业说明书上的指示进行干预。 这样,作业一直在作业说明书的控制下运行,直至遇到作业结束语句时,系统才停止该作 业的运行。 (3) 图形用户接口。用户虽然可以通过联机用户接口来取得 OS 的服务,但这时要求用 户能熟记各种命令的名字和格式,并严格按照规定的格式输入命令。这既不方便又花时间, 于是,另一种形式的联机用户接口——图形用户接口便应运而生。图形用户接口采用了图 形化的操作界面,用非常容易识别的各种图标(Icon)来将系统的各项功能、各种应用程序和 文件,直观、逼真地表示出来。用户可用鼠标或通过菜单和对话框来完成对应用程序和文 件的操作。此时用户已完全不必像使用命令接口那样去记住命令名及格式,从而把用户从 繁琐且单调的操作中解脱出来。 图形用户接口可以方便地将文字、图形和图像集成在一个文件中。可以在文字型文件 中加入一幅或多幅彩色图画,也可以在图画中写入必要的文字,而且还可进一步将图画、 文字和声音集成在一起。20 世纪 90 年代以后推出的主流 OS 都提供了图形用户接口。 2.程序接口 该接口是为用户程序在执行中访问系统资源而设置的,是用户程序取得操作系统服务 的惟一途径。它是由一组系统调用组成,每一个系统调用都是一个能完成特定功能的子程 序,每当应用程序要求 OS 提供某种服务(功能)时,便调用具有相应功能的系统调用。早期 的系统调用都是用汇编语言提供的,只有在用汇编语言书写的程序中才能直接使用系统调 用;但在高级语言以及 C 语言中,往往提供了与各系统调用一一对应的库函数,这样,应 用程序便可通过调用对应的库函数来使用系统调用。但在近几年所推出的操作系统中,如 UNIX、OS/2 版本中,其系统调用本身已经采用 C 语言编写,并以函数形式提供,故在用 C 语言编制的程序中,可直接使用系统调用。 ·24· 计算机操作系统 1.5 OS 结构设计 早期 OS 的规模很小,如只有几十 KB,完全可以由一个人以手工方式,用几个月的时 间编制出一个 OS。此时,编制程序基本上是一种技巧,OS 是否是有结构的并不那么重要, 重要的是程序员的程序设计技巧。但随着 OS 规模的愈来愈大,其所具有的代码也愈来愈多, 往往需要由数十人或数百人甚至更多的人参与,分工合作,共同来完成操作系统的设计。 这意味着,应采用工程化的开发方法来对大型软件进行开发。由此,产生了“软件工程学”。 软件工程的目标是十分明确的,所开发出的软件产品应具有良好的软件质量和合理的 费用,整个费用应能为用户所接受。软件质量可用这样几个指标来评价:功能性,有效性, 可靠性,易使用性,可维护性和易移植性。为此,先后产生了多种操作系统的开发方法, 如模块化方法、结构化方法和面向对象的方法等。利用不同的开发方法所开发出的操作系 统将具有不同的操作系统结构。 1.5.1 传统的操作系统结构 软件开发技术的不断发展,促进了 OS 结构的更新换代。这里,我们把早期的无结构 OS(第一代)、模块化结构的 OS(第二代)和分层式结构的 OS(第三代),都统称为传统结构的 OS,而把微内核结构的 OS 称为现代结构的 OS。 1.无结构操作系统 在早期开发操作系统时,设计者只是把注意力放在功能的实现和获得高的效率上,缺 乏首尾一致的设计思想。此时的 OS 是为数众多的一组过程的集合,每个过程可以任意地相 互调用其它过程,致使操作系统内部既复杂又混乱。因此,这种 OS 是无结构的,也有人把 它称为整体系统结构。 此时程序设计的技巧,只是如何编制紧凑的程序,以便于有效地利用内存。当系统不 太大时,在一个人能够完全理解和掌握的情况下,问题还不是太大,但随着系统的不断扩 大,所设计出的操作系统就会变得既庞大又杂乱。这一方面会使所编制出的程序错误很多, 给调试工作带来很多困难;另一方面也使程序难以阅读和理解,增加了维护人员的负担。 2.模块化结构 OS 1) 模块化程序设计技术的基本概念 模块化程序设计技术是 20 世纪 60 年代出现的一种结构化程序设计技术。该技术是基 于“分解”和“模块化”原则来控制大型软件的复杂度。为使 OS 具有较清晰的结构,OS 不再是由众多的过程直接构成,而是将 OS 按其功能精心地划分为若干个具有一定独立性和 大小的模块;每个模块具有某方面的管理功能,如进程管理模块、存储器管理模块、I/O 设 备管理模块等,并仔细地规定好各模块间的接口,使各模块之间能通过该接口实现交互。 然后,再进一步将各模块细分为若干个具有一定功能的子模块,如把进程管理模块又分为 进程控制、进程同步等子模块,同样也要规定好各子模块之间的接口。若子模块较大时, 可再进一步将它细分。我们把这种设计方法称为模块―接口法,由此构成的操作系统就是 具有模块化结构的操作系统。图 1-6 示出了由模块、子模块等组成的模块化 OS 结构。 第一章 操作系统引论 操作系统 ·25· 模块 进程管理 … 存储器管理 … 文件管理 子模块 进程控制 … 进程调度 内存分配 内存保护 磁盘管理 目录管理 图 1-6 模块化结构的操作系统 2) 模块的独立性 在模块―接口法设计方法中,关键问题是模块的划分和规定好模块之间的接口。如果 我们在划分模块时,将模块划分得太小,虽然可以降低模块本身的复杂性,但会引起模块 之间的联系过多,而会造成系统比较混乱;如果将模块划分得过大,又会增加模块内部的 复杂性,使内部的联系增加。因此,在划分模块时,应在两者间进行权衡。 另外,在划分模块时,必须充分注意模块的独立性问题。因为模块的独立性越高,各 模块间的交互就越少,系统的结构也就越清晰。衡量模块的独立性有以下两个标准: (1) 内聚性,指模块内部各部分间联系的紧密程度。内聚性越高,模块的独立性越强。 (2) 耦合度,指模块间相互联系和相互影响的程度。显然,耦合度越低,模块的独立性 越好。 3) 模块接口法的优缺点 利用模块―接口法开发的 OS,较之无结构 OS 具有以下明显的优点: (1) 提高 OS 设计的正确性、可理解性和可维护性; (2) 增强 OS 的适应性; (3) 加速 OS 的开发过程。 模块化结构设计仍存在下述问题: (1) 在 OS 设计时,对各模块间的接口规定很难满足在模块完成后对接口的实际需求。 (2) 在 OS 设计阶段,设计者必须做出一系列的决定(决策),每一个决定必须建立在上 一个决定的基础上。但在模块化结构设计中,各模块的设计齐头并进,无法寻找到一个可 靠的决定顺序,造成各种决定的“无序性”,这将使程序设计人员很难做到“设计中的每一 步决定都是建立在可靠的基础上”,因此模块―接口法又被称为“无序模块法”。 3.分层式结构 OS 1) 分层式结构的基本概念 为了将模块―接口法中“决定顺序”的无序性变为有序性,引入了有序分层法。分层 法的设计任务是,在目标系统 An 和裸机系统(又称宿主系统)A0 之间,铺设若干个层次的软 件 A1、A2、A3、…、An-1,使 An 通过 An-1、An-2、…、A2、A1 层,最终能在 A0 上运行。 在操作系统中,常采用自底向上法来铺设这些中间层。 自底向上的分层设计的基本原则是:每一步设计都是建立在可靠的基础上。为此规定, 每一层仅能使用其底层所提供的功能和服务,这样可使系统的调试和验证都变得更容易。 ·26· 计算机操作系统 例如,在调试第一层软件 A1 时,由于它使用的是一个完全确定的物理机器(宿主系统)所提 供的功能,在对 A1 软件经过精心设计和几乎是穷尽无遗的测试后,可以认为 A1 是正确的, 而且它与其所有的高层软件 A2、…、An 无关;同样在调试第二层软件 A2 时,它也只使用 了软件 A1 和物理机器所提供的功能,而与其高层软件 A3、…、An 无关;如此一层一层地 自底向上增添软件层,每一层都实现若干功能,最后总能构成一个能满足需要的 OS。在用 这种方法构成操作系统时,已将一个操作系统分为若干个层次,每层又由若干个模块组成, 各层之间只存在着单向的依赖关系,即高层仅依赖于紧邻它的低层。 2) 分层结构的优缺点 分层结构的主要优点有: (1) 易保证系统的正确性。自下而上的设计方式,使所有设计中的决定都是有序的,或 者说是建立在较为可靠的基础上的,这样比较容易保证整个系统的正确性。 (2) 易扩充和易维护性。在系统中增加、修改或替换一个层次中的模块或整个层次,只 要不改变相应层次间的接口,就不会影响其它层次,这必将使系统维护和扩充变得更加 容易。 分层结构的主要缺点是:系统效率降低了。由于层次结构是分层单向依赖的,因此必 须在相邻层之间都要建立层次间的通信机制,OS 每执行一个功能,通常要自上而下地穿越 多个层次,这无疑会增加系统的通信开销,从而导致系统效率的降低。 1.5.2 客户/服务器模式 客户/服务器(Client/Server)模式可简称为 C/S 模式,在 20 世纪 90 年代已风靡全球,不 论是 LAN,还是企业网,以及 Internet 所提供的多种服务,都广泛采用了客户/服务器 模式。 1.客户/服务器模式的组成 客户/服务器系统主要由客户机、服务器和网络系统三个部分组成。 (1) 客户机:通常在一个 LAN 网络上连接有多台网络工作站(简称客户机),每台客户机 都是一个自主计算机,具有一定的处理能力,客户进程在其上运行,平时它处理一些本地 业务,也可发送一个消息给服务器,以请求某项服务。 (2) 服务器:通常是一台规模较大的机器,在其上驻留有网络文件系统或数据库系统等, 它应能为网上所有的用户提供一种或多种服务。平时它一直处于工作状态,被动地等待来 自客户机的请求,一旦检查到有客户提出服务请求,便去完成客户的请求,并将结果送回 客户。这样,工作站中的用户进程与服务器进程便形成了客户/服务器关系。 (3) 网络系统:用于连接所有客户机和服务器,实现它们之间通信和网络资源共享的 系统。 2.客户/服务器之间的交互 在采用客户/服务器的系统中,通常是客户机和服务器共同完成对应用(程序)的处理。 这时,在客户机和服务器之间就需要进行交互,即必须利用消息机制在这两者之间进行多 次通信。一次完整的交互过程可分成以下四步: (1) 客户发送请求消息。当客户机上的用户要请求服务器进行应用处理时,应输入相应 第一章 操作系统引论 ·27· 的命令和有关参数。由客户机上的发送进程先把这些信息装配成请求消息,然后把它发往 服务器;客户机上的接收进程则等待接收从服务器发回来的响应消息。 (2) 服务器接收消息。服务器中的接收进程平时处于等待状态,一旦有客户机发来请求, 接收进程便被激活,并根据请求信息的内容,将之提供给服务器上的相应软件进行处理。 (3) 服务器回送消息。服务器的软件根据请求进行处理,在完成指定的处理后,把处理 结果装配成一个响应消息,由服务器中的发送进程将之发往客户机。 (4) 客户机接收消息。客户机中的接收进程把收到的响应消息转交给客户机软件,再由 后者做出适当处理后提交给发送该请求的客户。 3.客户/服务器模式的优点 C/S 模式之所以能成为当前分布式系统和网络环境下软件的主要工作模式,是由于该模 式具有传统集中模式所无法比拟的一系列优点。 (1) 数据的分布处理和存储。由于客户机具有相当强的处理和存储能力,可进行本地处 理和数据的分布存储,从而摆脱了由于把一切数据都存放在主机中而造成的既不可靠又容 易产生瓶颈现象的困难局面。 (2) 便于集中管理。尽管 C/S 模式具有分布处理功能,但公司(单位)中的有关全局的重 要信息、机密资料、重要设备以及网络管理等,仍可采取集中管理方式。这样可较好地保 障系统的“可靠”与“安全”。 (3) 灵活性和可扩充性。C/S 模式非常灵活,极易扩充。理论上,客户机和服务器的数 量不受限制。其灵活性还表现在可以配置多种类型的客户机和服务器。 (4) 易于改编应用软件。在客户/服务器模式中,对于客户机程序的修改和增删,比传 统集中模式要容易得多,必要时也允许由客户进行修改。 基本客户/服务器模式的不足之处是存在着不可靠性和瓶颈问题。在系统仅有一个服务 器时,一旦服务器故障,将导致整个网络瘫痪。当服务器在重负荷下工作时,会因忙不过 来而显著地延长对用户请求的响应时间。如果在网络中配置多个服务器,并采取相应的安 全措施,则可加以改善。 1.5.3 面向对象的程序设计 1.面向对象技术的基本概念 面向对象技术是 20 世纪 80 年代初提出并很快流行起来的。该技术是基于“抽象”和 “隐蔽”原则来控制大型软件的复杂度的。所谓对象,是指在现实世界中具有相同属性、 服从相同规则的一系列事物(事物可以是一个物理实体、一个概念或一个软件模块等)的抽 象,而把其中的具体事物称为对象的实例。如果在 OS 中的各类实体如进程、线程、消息、 存储器和文件等,都使用了对象这一概念,相应地,便有了进程对象、线程对象、存储器 对象和文件对象等。 1) 对象 在面向对象的技术中,是利用被封装的数据结构(变量)和一组对它进行操作的过程(方 法),来表示系统中的某个对象的,如图 1-7 所示。对象中的变量(数据)也称为属性,它可以 是单个标量或一张表。面向对象中的方法是用于执行某种功能的过程,它可以改变对象的 ·28· 计算机操作系统 状态,更新对象中的某些数据值或作用于对象所要访问的外部 资源。如果把一个文件作为一个对象(见图 1-8),该对象的变 量便是文件类型、文件大小、文件的创建者等。对象中的方法 数据结构 过程1 包含对文件的操作,如创建文件、打开文件、读文件、写文件、 过程2 关闭文件等。 对象中的变量(数据)对外是隐蔽的,因而外界不能对它直 接进行访问,必须通过该对象中的一组方法(操作函数)对它进 过程3 图 1-7 一个对象的示意图 行访问。例如要想对上面的文件 A 执行打开操作,必须用该对象中的打开过程去打开它。 同样对象中的一组方法的实现细节也是隐蔽的,因此,对象中的变量可以得到很好的保护, 而不会允许未经受权者使用和不正确的操作。 2) 对象类 在实践中,有许多对象可能表示的是同一类事物,每个对象具有自己的变量集合,而 它们所具有的方法是相同的。如果为每一个相似的对象都定义一组变量和方法,显然是低 效的,由此产生了“对象类”的概念,利用“对象类”来定义一组大体相似的对象。一个 类同样定义了一组变量和针对该变量的一组方法,用它们来描述一组对象的共同属性和行 为。类是在对象上的抽象,对象则是类的实例。对象类中所定义的变量在实例中均有具体 的值。 例如,我们将文件设计成一个类,类的变量同样是文件类型、文件大小和文件的创建 者等。类中的方法是文件的创建、打开、读写、关闭等。图 1-8 示出了一个文件类,在类的 变量中,没有具体数值,一旦被赋予了具体数值就成了文件 A 对象。对象类的概念非常有 用,因为它极大地提高了创建多个相似对象的效率。 类 类名 属性 操作 类的实例 (类名)对象名 属性值 操作 文件 类型 大小 创建者 打开( ) 读( ) 写( ) 关闭( ) (文件)文件A 图表 20 K X用户创建,… 打开( ) 读( ) 写( ) 关闭( ) 图 1-8 类和对象的关系 3) 继承 在面向对象的技术中,可以根据已有类来定义一个新的类,新类被称为子类(B),原来 的类被称为父类(A),见图 1-9。继承是父类和子类之间共享变量和方法的机制,该机制规 定,子类自动继承父类中定义的变量和方法,并允许子类再 A 增加新的内容。继承特性可使定义子类变得更为容易。一个 从A继承 父类可以定义多个子类,它们分别是父类的某种特例,父类 描述了这些子类的公共变量和方法。类似地,这些子类又可 以定义自己的子类,通过此途经可以生成一个继承的层次。 另外,也允许一个子类有两个父类或多个父类,它可以从多 继承部分 B 增加部分 个父类获得继承,此时称为“多重继承”。 图 1-9 类的继承关系 第一章 操作系统引论 ·29· 2.面向对象技术的优点 在设计操作系统时,将计算机中的实体作为对象来处理,可带来如下好处: (1) 通过“重用”提高产品质量和生产率。 在面向对象技术中可通过“重用”以前项目中经过精心测试的对象,或由其他人编写、 测试和维护的对象类,来构建新的系统,这不仅可大大降低开发成本,而且能获得更好的 系统质量。 (2) 使系统具有更好的易修改性和易扩展性。 通过封装,可隐蔽对象中的变量和方法,因而当改变对象中的变量和方法时,不会影 响到其它部分,从而可方便地修改老的对象类。另外,继承是面向对象技术的重要特性, 在创建一个新对象类时,通过利用继承特性,可显著地减少开发的时空开销,使系统具有 更好的易扩展性和灵活性。 (3) 更易于保证系统的“正确性”和“可靠性”。 对象是构成操作系统的基本单元,由于可以独立地对它进行测试,易于保证每个对象 的正确性和可靠性,因此也就比较容易保证整个系统的正确性和可靠性。此外,封装对对 象类中的信息进行了隐蔽,这样又可有效地防止未经授权者的访问和用户不正确的使用, 有助于构建更为安全的系统。 1.5.4 微内核 OS 结构 微内核(Micro Kernel)操作系统结构,是 20 世纪 80 年代后期发展起来的。由于它能有 效地支持多处理机运行,故非常适用于分布式系统环境。当前比较流行的、能支持多处理 机运行的 OS,几乎全部都采用了微内核结构,如 Carngie Mellon 大学研制的 Mach OS, 便属于微内核结构 OS;又如当前广泛使用的 Windows 2000/XP 操作系统,也采用了微内核 结构。 1.微内核操作系统的基本概念 为了提高操作系统的“正确性”、“灵活性”、“易维护性”和”可扩充性”,在进行现代 操作系统结构设计时,即使在单处理机环境下,大多也采用基于客户/服务器模式的微内核 结构,将操作系统划分为两大部分:微内核和多个服务器。至于什么是微内核操作系统结 构,现在尚无一致公认的定义,但我们可以从下面四个方面,对微内核结构的操作系统进 行描述。 1) 足够小的内核 在微内核操作系统中,内核是指精心设计的、能实现现代 OS 最基本的核心功能的部分。 微内核并非是一个完整的 OS,而只是操作系统中最基本的部分,它通常用于:① 实现与 硬件紧密相关的处理;② 实现一些较基本的功能;③ 负责客户和服务器之间的通信。它 们只是为构建通用 OS 提供一个重要基础,这样就可以确保把操作系统内核做得很小。 2) 基于客户/服务器模式 由于客户/服务器模式具有非常多的优点,故在单机微内核操作系统中几乎无一例外地 都采用客户/服务器模式,将操作系统中最基本的部分放入内核中,而把操作系统的绝大部 分功能都放在微内核外面的一组服务器(进程)中实现。例如用于提供对进程(线程)进行管理 ·30· 计算机操作系统 的进程(线程)服务器,提供虚拟存储器管理功能的虚拟存储器服务器,提供 I/O 设备管理的 I/O 设备管理服务器等,它们都是被作为进程来实现的,运行在用户态,客户与服务器之 间是借助微内核提供的消息传递机制来实现信息交互的。图 1-10 示出了在单机环境下的 客户/服务器模式。 客户 进程 客户 进程 终端 进程 服务器 服务器 … 文件 存储器 服务器 服务器 用户方式 请求 核心 回答 核心方式 图 1-10 在单机环境下的客户/服务器模式 3) 应用“机制与策略分离”原理 在现代操作系统的结构设计中,经常利用“机制与策略分离”的原理来构造 OS 结构。 所谓机制,是指实现某一功能的具体执行机构。而策略,则是在机制的基础上,借助于某 些参数和算法来实现该功能的优化,或达到不同的功能目标。通常,机制处于一个系统的 基层,而策略则处于系统的高层。在传统的 OS 中,将机制放在 OS 的内核的较低层,把策 略放在内核的较高层次中。而在微内核操作系统中,通常将机制放在 OS 的微内核中。正因 为如此,才有可能将内核做得很小。 4) 采用面向对象技术 操作系统是一个极其复杂的大型软件系统,我们不仅可以通过结构设计来分解操作系 统的复杂度,还可以基于面向对象技术中的“抽象”和“隐蔽”原则控制系统的复杂性, 再进一步利用“对象”、“封装”和“继承”等概念来确保操作系统的“正确性”、“可靠性”、 “易修改性”、“易扩展性”等,并提高操作系统的设计速度。正因为面向对象技术能带来 如此多的好处,故面向对象技术被广泛应用于现代操作系统的设计中。 2.微内核的基本功能 微内核应具有哪些功能,或者说哪些功能应放在微内核内,哪些应放在微内核外,目 前尚无明确的规定。现在一般都采用“机制与策略分离”的原理,将机制部分,以及与硬 件紧密相关的部分放入微内核中。由此可知微内核通常具有如下几方面的功能: 1) 进程(线程)管理 大多数的微内核 OS,对于进程管理功能的实现,都采用“机制与策略分离”的原理。 例如,为实现进程(线程)调度功能,须在进程管理中设置一个或多个进程(线程)优先级队列; 能将指定优先级进程(线程)从所在队列中取出,并将其投入执行。由于这一部分属于调度功 能的机制部分,应将它放入微内核中。应如何确定每类用户(进程)的优先级,以及应如何修 改它们的优先级等,都属于策略问题,可将它们放入微内核外的进程(线程)管理服务器中。 由于进程(线程)之间的通信功能是微内核 OS 最基本的功能,被频繁使用,因此几乎所 有的微内核 OS 都是将进程(线程)之间的通信功能放入微内核中。此外,还将进程的切换、线 程的调度,以及多处理机之间的同步等功能也放入微内核中。 2) 低级存储器管理 通常在微内核中,只配置最基本的低级存储器管理机制。如用于实现将用户空间的逻 辑地址变换为内存空间的物理地址的页表机制和地址变换机制,这一部分是依赖于机器的, 第一章 操作系统引论 ·31· 因此放入微内核。而实现虚拟存储器管理的策略,则包含应采取何种页面置换算法,采用 何种内存分配与回收策略等,应将这部分放在微内核外的存储器管理服务器中去实现。 3) 中断和陷入处理 大多数微内核操作系统都是将与硬件紧密相关的一小部分放入微内核中处理。此时微 内核的主要功能,是捕获所发生的中断和陷入事件,并进行相应的前期处理。如进行中断 现场保护,识别中断和陷入的类型,然后将有关事件的信息转换成消息后,把它发送给相 关的服务器。由服务器根据中断或陷入的类型,调用相应的处理程序来进行后期处理。 在微内核 OS 中是将进程管理、存储器管理以及 I/O 管理这些功能一分为二,属于机制 的很小一部分放入微内核中,另外绝大部分放在微内核外的各种服务器中来实现。事实上, 其中大多数服务器都比微内核大。这进一步说明了为什么能在采用客户/服务器模式后,还 能把微内核做得很小的原因。 3.微内核操作系统的优点 由于微内核 OS 结构是建立在模块化、层次化结构的基础上的,并采用了客户/服务器 模式和面向对象的程序设计技术,由此可见,微内核结构的 OS 是集各种技术优点之大成, 因而使之具有如下优点: 1) 提高了系统的可扩展性 由于微内核 OS 的许多功能是由相对独立的服务器软件来实现的,当开发了新的硬件和 软件时,微内核 OS 只须在相应的服务器中增加新的功能,或再增加一个专门的服务器。与 此同时,也必然改善系统的灵活性,不仅可在操作系统中增加新的功能,还可修改原有功 能,以及删除已过时的功能,以形成一个更为精干有效的操作系统。 2) 增强了系统的可靠性 这一方面是由于微内核是出于精心设计和严格测试的,容易保证其正确性;另一方面 是它提供了规范而精简的应用程序接口(API),为微内核外部的程序编制高质量的代码创造 了条件。此外,由于所有服务器都是运行在用户态,服务器与服务器之间采用的是消息传 递通信机制,因此,当某个服务器出现错误时,不会影响内核,也不会影响其它服务器。 3) 可移植性 随着硬件的快速发展,出现了各种各样的硬件平台,作为一个好的操作系统,必须具 备可移植性,使其能较容易地运行在不同的计算机硬件平台上。在微内核结构的操作系统 中,所有与特定 CPU 和 I/O 设备硬件有关的代码,均放在内核和内核下面的硬件隐藏层中, 而操作系统其它绝大部分(即各种服务器)均与硬件平台无关,因而,把操作系统移植到另一 个计算机硬件平台上所需作的修改是比较小的。 4) 提供了对分布式系统的支持 由于在微内核 OS 中,客户和服务器之间以及服务器和服务器之间的通信,是采用消息 传递通信机制进行的,致使微内核 OS 能很好地支持分布式系统和网络系统。事实上,只要 在分布式系统中赋予所有进程和服务器惟一的标识符,在微内核中再配置一张系统映射表 (即进程和服务器的标识符与它们所驻留的机器之间的对应表),在进行客户与服务器通信 时,只需在所发送的消息中标上发送进程和接收进程的标识符,微内核便可利用系统映射 表,将消息发往目标,而无论目标是驻留在哪台机器上。 ·32· 计算机操作系统 5) 融入了面向对象技术 在设计微内核 OS 时,采用了面向对象的技术,其中的“封装”,“继承”,“对象类”和 “多态性”,以及在对象之间采用消息传递机制等,都十分有利于提高系统的“正确性”、“可 靠性”、“易修改性”、“易扩展性”等,而且还能显著地减少开发系统所付出的开销。 4.微内核操作系统存在的问题 应当指出,在微内核 OS 中,由于采用了非常小的内核,以及客户/服务器模式和消息 传递机制,这些虽给微内核 OS 带来了许多优点,但由此也使微内核 OS 存在着潜在的缺点。 其中最主要的是,较之早期 OS,微内核 OS 的运行效率有所降低。 效率降低的最主要的原因是,在完成一次客户对 OS 提出的服务请求时,需要利用消息 实现多次交互和进行用户/内核模式及上下文的多次切换。然而,在早期的 OS 中,用户进 程在请求取得 OS 服务时,一般只需进行两次上下文的切换:一次是在执行系统调用后,由 用户态转向系统态时;另一次是在系统完成用户请求的服务后,由系统态返回用户态时。 在微内核 OS 中,由于客户和服务器及服务器和服务器之间的通信,都需通过微内核,致使 同样的服务请求至少需要进行四次上下文切换。第一次是发生在客户发送请求消息给内核, 以请求取得某服务器特定的服务时;第二次是发生在由内核把客户的请求消息发往服务器 时;第三次是当服务器完成客户请求后,把响应消息发送到内核时;第四次是在内核将响 应消息发送给客户时。 实际情况是往往还会引起更多的上下文切换。例如,当某个服务器自身尚无能力完成 客户请求,而需要其它服务器的帮助时,如图 1-11 中所示,其中的文件服务器还需要磁盘 服务器的帮助,这时就需要进行八次上下文的切换。 客户机应用 OS内核 返回 (a) 在整体式内核文件操作中的上下文切换 客户机应用 文件服务器 磁盘设备驱动器 微内核 (b) 在微内核中等价操作的上下文切换 图 1-11 在传统 OS 和微内核 OS 中的上下文切换 为了改善运行效率,可以重新把一些常用的操作系统基本功能,由服务器移入微内核 中。这样可使客户对常用操作系统功能的请求所发生的用户/内核模式和上下文的切换的次 数,由四次或八次降为两次。但这又会使微内核的容量明显地增大,在小型接口定义和适 应性方面的优点也有所下降,同时也提高了微内核的设计代价。 第一章 操作系统引论 ·33· 习题 1. 设计现代 OS 的主要目标是什么? 2. OS 的作用可表现在哪几个方面? 3. 为什么说 OS 实现了对计算机资源的抽象? 4. 试说明推动多道批处理系统形成和发展的主要动力是什么。 5. 何谓脱机 I/O 和联机 I/O? 6. 试说明推动分时系统形成和发展的主要动力是什么。 7. 实现分时系统的关键问题是什么?应如何解决? 8. 为什么要引入实时 OS? 9. 什么是硬实时任务和软实时任务?试举例说明。 10. 在 8 位微机和 16 位微机中,占据了统治地位的是什么操作系统? 11. 试列出 Windows OS 中五个主要版本,并说明它们分别较之前一个版本有何改进。 12. 试从交互性、及时性以及可靠性方面,将分时系统与实时系统进行比较。 13. OS 有哪几大特征?其最基本的特征是什么? 14. 处理机管理有哪些主要功能?它们的主要任务是什么? 15. 内存管理有哪些主要功能?它们的主要任务是什么? 16. 设备管理有哪些主要功能?其主要任务是什么? 17. 文件管理有哪些主要功能?其主要任务是什么? 18. 是什么原因使操作系统具有异步性特征? 19. 模块接口法存在着哪些问题?可通过什么样的途径来解决? 20. 在微内核 OS 中,为什么要采用客户/服务器模式? 21. 试描述什么是微内核 OS。 22. 在基于微内核结构的 OS 中,应用了哪些新技术? 23. 何谓微内核技术?在微内核中通常提供了哪些功能? 24. 微内核操作系统具有哪些优点?它为何能有这些优点? ·34· 计算机操作系统 第二章 进 程 管 理 在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单位都 是进程。操作系统所具有的四大特征也都是基于进程而形成的,并可从进程的观点来研究 操作系统。显然,在操作系统中,进程是一个极其重要的概念。因此,本章专门来讨论 进程。 2.1 进程的基本概念 在未配置 OS 的系统中,程序的执行方式是顺序执行,即必须在一个程序执行完后,才 允许另一个程序执行;在多道程序环境下,则允许多个程序并发执行。程序的这两种执行 方式间有着显著的不同。也正是程序并发执行时的这种特征,才导致了在操作系统中引入 进程的概念。因此,这里有必要先对程序的顺序执行和并发执行方式做简单的描述。 2.1.1 程序的顺序执行及其特征 1. 程序的顺序执行 通常可以把一个应用程序分成若干个程序段,在各程序段之间,必须按照某种先后次 序顺序执行,仅当前一操作(程序段)执行完后,才能执行后继操作。例如,在进行计算时, 总须先输入用户的程序和数据,然后进行计算,最后才能打印计算结果。这里,我们用结 点(Node)代表各程序段的操作(在图 2-1 中用圆圈表示),其中,I 代表输入操作,C 代表计算 操作,P 为打印操作;另外,用箭头指示操作的先后次序。这样,上述的三个程序段的执行 顺序可示于图 2-1(a)中。对一个程序段中的多条语句来说,也有一个执行顺序问题,例如对 于下述三条语句的程序段: S1: a:=x+y; S2: b:=a-5; S3: c:=b+1; 其中,语句 S2 必须在语句 S1 之后(即 a 被赋值)才能执行;同样,语句 S3 也只能在 b 被赋 值后才能执行。因此,这三条语句应按图 2-1(b)所示的顺序执行。 I1 C1 P1 I2 C2 P2 S1 S2 S3 (a) 程序的顺序执行 (b) 三条语句的顺序执行 图 2-1 程序的顺序执行 2. 程序顺序执行时的特征 (1) 顺序性:处理机的操作严格按照程序所规定的顺序执行,即每一操作必须在上一个 第二章 进 程 管 理 ·35· 操作结束之后开始。 (2) 封闭性:程序是在封闭的环境下执行的,即程序运行时独占全机资源,资源的状态 (除初始状态外)只有本程序才能改变它。程序一旦开始执行,其执行结果不受外界因素影响。 (3) 可再现性:只要程序执行时的环境和初始条件相同,当程序重复执行时,不论它是 从头到尾不停顿地执行,还是“停停走走”地执行,都将获得相同的结果。 程序顺序执行时的特性,为程序员检测和校正程序的错误带来了很大的方便。 2.1.2 前趋图 前趋图(Precedence Graph)是一个有向无循环图,记为 DAG(Directed Acyclic Graph),用 于描述进程之间执行的前后关系。图中的每个结点可用于描述一个程序段或进程,乃至一 条语句;结点间的有向边则用于表示两个结点之间存在的偏序(Partial Order,亦称偏序关系) 或前趋关系(Precedence Relation)“→”。 →={(Pi,Pj)|Pi must complete before Pj may start},如果(Pi,Pj)∈→,可写成 Pi→Pj,称 Pi 是 Pj 的直接前趋,而称 Pj 是 Pi 的直接后继。在前趋图中,把没有前趋的结点称为初始结 点(Initial Node),把没有后继的结点称为终止结点(Final Node)。此外,每个结点还具有一个 重量(Weight),用于表示该结点所含有的程序量或结点的执行时间。在图 2-1(a)和 2-1(b)中 分别存在着这样的前趋关系: Ii→Ci→Pi 和 S1→S2→S3 对于图 2-2(a)所示的前趋图,存在下述前趋关系: P1→P2,P1→P3,P1→P4,P2→P5,P3→P5,P4→P6,P4→P7,P5→P8,P6→P8,P7→P9,P8→P9 或表示为: P={P1,P2,P3,P4,P5,P6,P7,P8,P9} →={(P1,P2),(P1,P3),(P1,P4),(P2,P5),(P3,P5),(P4,P6),(P4,P7),(P5,P8),(P6,P8), (P7,P9),(P8,P9)} 应当注意,前趋图中必须不存在循环,但在图 2-2(b)中却有着下述的前趋关系: S2→S3,S3→S2 显然,这种前趋关系是不可能满足的。 P2 P5 S1 P1 P3 P8 P9 P6 S2 P4 S3 P7 (a) 具有九个结点的前趋图 图 2-2 前趋图 (b) 具有循环的图 ·36· 计算机操作系统 2.1.3 程序的并发执行及其特征 1.程序的并发执行 在图 2-1 中的输入程序、计算程序和打印程序三者之间,存在着 Ii→Ci→Pi 这样的前趋 关系,以至对一个作业的输入、计算和打印三个操作,必须顺序执行,但并不存在 Pi→Ii+1 的关系,因而在对一批程序进行处理时,可使它们并发执行。例如,输入程序在输入第一 个程序后,在计算程序对该程序进行计算的同时,可由输入程序再输入第二个程序,从而 使第一个程序的计算操作可与第二个程序的输入操作并发执行。一般来说,输入程序在输入 第 i+1 个程序时,计算程序可能正在对第 i 个程序进行计算,而打印程序正在打印第 i-1 个程 序的计算结果。图 2-3 示出了输入、计算和打印这三个程序对一批作业进行处理的情况。 I1 I2 I3 I4 C1 C2 C3 C4 P1 P2 P3 P4 图 2-3 并发执行时的前趋图 在该例中存在下述前趋关系: Ii→Ci,Ii→Ii+1,Ci→Pi,Ci→Ci+1,Pi→Pi+1 而 Ii+1 和 Ci 及 Pi-1 是重迭的,亦即在 Pi-1 和 Ci 以 及 Ii+1 之间,可以并发执行。对于具有下述四条语句 S1 的程序段: S1: a:=x+2 S3 S4 S2: b:=y+4 S2 S3: c:=a+b S4: d:=c+b 图 2-4 四条语句的前趋关系 可画出图 2-4 所示的前趋关系。可以看出:S3 必须在 a 和 b 被赋值后方能执行;S4 必须在 S3 之后执行;但 S1 和 S2 则可以并发执行,因为它们彼此互不依赖。 2.程序并发执行时的特征 程序的并发执行,虽然提高了系统吞吐量,但也产生了下述一些与程序顺序执行时不 同的特征。 1) 间断性 程序在并发执行时,由于它们共享系统资源,以及为完成同一项任务而相互合作,致 使在这些并发执行的程序之间,形成了相互制约的关系。例如,图 2-3 中的 I、C 和 P 是三 个相互合作的程序,当计算程序完成 Ci-1 的计算后,如果输入程序 I 尚未完成 Ii 的处理,则 计算程序就无法进行 Ci 的处理,致使计算程序必须暂停运行。又如,当打印程序完成 Pi 的 打印后,若计算程序尚未完成 Ci+1 的计算,则打印程序就无法对 Ci+1 的计算结果进行打印。 一旦使程序暂停的因素消失后(如 Ii 已处理完成),计算程序便可恢复执行对 Ci 的处理。简而 第二章 进 程 管 理 ·37· 言之,相互制约将导致并发程序具有“执行—暂停—执行”这种间断性的活动规律。 2) 失去封闭性 程序在并发执行时,是多个程序共享系统中的各种资源,因而这些资源的状态将由多 个程序来改变,致使程序的运行失去了封闭性。这样,某程序在执行时,必然会受到其它 程序的影响。例如,当处理机这一资源已被某个程序占有时,另一程序必须等待。 3) 不可再现性 程序在并发执行时,由于失去了封闭性,也将导致其再失去可再现性。例如,有两个 循环程序 A 和 B,它们共享一个变量 N。程序 A 每执行一次时,都要做 N:=N+1 操作;程 序 B 每执行一次时,都要执行 Print(N)操作,然后再将 N 置成“0”。程序 A 和 B 以不同的 速度运行。这样,可能出现下述三种情况(假定某时刻变量 N 的值为 n)。 (1) N:=N+1 在 Print(N)和 N:=0 之前,此时得到的 N 值分别为 n+1,n+1,0。 (2) N:=N+1 在 Print(N)和 N:=0 之后,此时得到的 N 值分别为 n,0,1。 (3) N:=N+1 在 Print(N)和 N:=0 之间,此时得到的 N 值分别为 n,n+1,0。 上述情况说明,程序在并发执行时,由于失去了封闭性,其计算结果已与并发程序的 执行速度有关,从而使程序的执行失去了可再现性,亦即,程序经过多次执行后,虽然它 们执行时的环境和初始条件相同,但得到的结果却各不相同。 2.1.4 进程的特征与状态 1. 进程的特征和定义 在多道程序环境下,程序的执行属于并发执行,此时它们将失去其封闭性,并具有间 断性及不可再现性的特征。这决定了通常的程序是不能参与并发执行的,因为程序执行的 结果是不可再现的。这样,程序的运行也就失去了意义。为使程序能并发执行,且为了对 并发执行的程序加以描述和控制,人们引入了“进程”的概念。为了能较深刻地了解什么 是进程,我们将先对进程的特征加以描述。 1) 结构特征 通常的程序是不能并发执行的。为使程序(含数据)能独立运行,应为之配置一进程控制 块,即 PCB(Process Control Block);而由程序段、相关的数据段和 PCB 三部分便构成了进 程实体。在早期的 UNIX 版本中,把这三部分总称为“进程映像”。值得指出的是,在许多 情况下所说的进程,实际上是指进程实体,例如,所谓创建进程,实质上是创建进程实体 中的 PCB;而撤消进程,实质上是撤消进程的 PCB,本教材中也是如此。 2) 动态性 进程的实质是进程实体的一次执行过程,因此,动态性是进程的最基本的特征。动态 性还表现在:“它由创建而产生,由调度而执行,由撤消而消亡”。可见,进程实体有一定 的生命期,而程序则只是一组有序指令的集合,并存放于某种介质上,其本身并不具有运 动的含义,因而是静态的。 3) 并发性 这是指多个进程实体同存于内存中,且能在一段时间内同时运行。并发性是进程的重 要特征,同时也成为 OS 的重要特征。引入进程的目的也正是为了使其进程实体能和其它进 ·38· 计算机操作系统 程实体并发执行;而程序(没有建立 PCB)是不能并发执行的。 4) 独立性 在传统的 OS 中,独立性是指进程实体是一个能独立运行、独立分配资源和独立接受调 度的基本单位。凡未建立 PCB 的程序都不能作为一个独立的单位参与运行。 5) 异步性 这是指进程按各自独立的、 不可预知的速度向前推进,或说进程实体按异步方式运行。 现在我们再来讨论进程的定义。曾有许多人从不同的角度对进程下过定义,其中较典 型的进程定义有: (1) 进程是程序的一次执行。 (2) 进程是一个程序及其数据在处理机上顺序执行时所发生的活动。 (3) 进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独 立单位。 在引入了进程实体的概念后,我们可以把传统 OS 中的进程定义为:“进程是进程实体 的运行过程,是系统进行资源分配和调度的一个独立单位”。 2. 进程的三种基本状态 进程执行时的间断性决定了进程可能具有多种状态。事实上,运行中的进程可能具有 以下三种基本状态。 1) 就绪(Ready)状态 当进程已分配到除 CPU 以外的所有必要资源后,只要再获得 CPU,便可立即执行,进 程这时的状态称为就绪状态。在一个系统中处于就绪状态的进程可能有多个,通常将它们 排成一个队列,称为就绪队列。 2) 执行状态 进程已获得 CPU,其程序正在执行。在单处理机系统中,只有一个进程处于执行状态; 在多处理机系统中,则有多个进程处于执行状态。 3) 阻塞状态 正在执行的进程由于发生某事件而暂时无法继续执行时,便放弃处理机而处于暂停状态, 亦即进程的执行受到阻塞,把这种暂停状态称为阻塞状态,有时也称为等待状态或封锁状态。 致使进程阻塞的典型事件有:请求 I/O,申请缓冲空间等。通常将这种处于阻塞状态的进程也 排成一个队列。有的系统则根据阻塞原因的不同而把处于阻塞状态的进程排成多个队列。 处于就绪状态的进程,在调度程序为之分配了处理 机之后,该进程便可执行,相应地,它就由就绪状态转 变为执行状态。正在执行的进程也称为当前进程,如果 因分配给它的时间片已完而被暂停执行时,该进程便由 执行状态又回复到就绪状态;如果因发生某事件而使进 I/O完成 就绪 时间片完 进程调度 程的执行受阻(例如,进程请求访问某临界资源,而该 资源正被其它进程访问时),使之无法继续执行,该进 程将由执行状态转变为阻塞状态。图 2-5 示出了进程的 三种基本状态以及各状态之间的转换关系。 阻塞 执行 I/O请求 图 2-5 进程的三种基本状态及其转换 第二章 进 程 管 理 ·39· 3. 挂起状态 1) 引入挂起状态的原因 在不少系统中进程只有上述三种状态,但在另一些系统中,又增加了一些新状态,最 重要的是挂起状态。引入挂起状态的原因有: (1) 终端用户的请求。当终端用户在自己的程序运行期间发现有可疑问题时,希望暂时 使自己的程序静止下来。亦即,使正在执行的进程暂停执行;若此时用户进程正处于就绪 状态而未执行,则该进程暂不接受调度,以便用户研究其执行情况或对程序进行修改。我 们把这种静止状态称为挂起状态。 (2) 父进程请求。有时父进程希望挂起自己的某个子进程,以便考查和修改该子进程, 或者协调各子进程间的活动。 (3) 负荷调节的需要。当实时系统中的工作负荷较重,已可能影响到对实时任务的控制 时,可由系统把一些不重要的进程挂起,以保证系统能正常运行。 (4) 操作系统的需要。操作系统有时希望挂起某些进程,以便检查运行中的资源使用情 况或进行记账。 2) 进程状态的转换 在引入挂起状态后,又将增加从挂起状态(又称为静止状态)到非挂起状态(又称为活动 状态)的转换;或者相反。可有以下几种情况: (1) 活动就绪→静止就绪。当进程处于未被挂起的就绪状态时,称此为活动就绪状态, 表示为 Readya。当用挂起原语 Suspend 将该进程挂起后,该进程便转变为静止就绪状态, 表示为 Readys,处于 Readys 状态的进程不再被调度执行。 (2) 活动阻塞→静止阻塞。当进程处于未被挂起的阻塞状态时,称它是处于活动阻塞状 态,表示为 Blockeda。当用 Suspend 原语将它挂 起后,进程便转变为静止阻塞状态,表示为 Blockeds。处于该状态的进程在其所期待的事件 执行 挂 起 调度 请求 I/O 出现后,将从静止阻塞变为静止就绪。 (3) 静止就绪→活动就绪。处于 Readys 状 态的进程,若用激活原语 Active 激活后,该进 活动 就绪 激活 挂起 静止 就绪 释放 释放 程将转变为 Readya 状态。 (4) 静止阻塞→活动阻塞。处于 Blockeds 状 态的进程,若用激活原语 Active 激活后,该进 活动 阻塞 激活 挂起 静止 阻塞 程将转变为 Blockeda 状态。图 2-6 示出了具有 挂起状态的进程状态图。 图 2-6 具有挂起状态的进程状态图 4.创建状态和终止状态 在目前实际的系统中,为了管理的需要,还存在着两种比较常见的进程状态,即创建 状态和终止状态。 1) 创建状态 创建一个进程一般要通过两个步骤:首先,为一个新进程创建 PCB,并填写必要的管 理信息;其次,把该进程转入就绪状态并插入就绪队列之中。当一个新进程被创建时,系 ·40· 计算机操作系统 统已为其分配了 PCB,填写了进程标识等信息,但由于该进程所必需的资源或其它信息, 如主存资源尚未分配等,一般而言,此时的进程已拥有了自己的 PCB,但进程自身还未进 入主存,即创建工作尚未完成,进程还不能被调度运行,其所处的状态就是创建状态。 引入创建状态,是为了保证进程的调度必须在创建工作完成后进行,以确保对进程控 制块操作的完整性。同时,创建状态的引入,也增加了管理的灵活性,操作系统可以根据 系统性能或主存容量的限制,推迟创建状态进程的提交。对于处于创建状态的进程,获得 了其所必需的资源,以及对其PCB初始化工作完成后,进程状态便可由创建状态转入就绪 状态。 2) 终止状态 进程的终止也要通过两个步骤:首先等待操作系统进行善后处理,然后将其 PCB 清零, 并将 PCB 空间返还系统。当一个进程到达了自然结束点,或是出现了无法克服的错误,或 是被操作系统所终结,或是被其他有终止权的进程所终结,它将进入终止状态。进入终止 态的进程以后不能再执行,但在操作系统中依然保留一个记录,其中保存状态码和一些计 时统计数据,供其它进程收集。一旦其它进程完成了对终止状态进程的信息提取之后,操 作系统将删除该进程。 图 2-7 示出了增加了创建状态和终止状态后,进程的三种基本状态及转换图衍变为五种 状态及转换关系图。 创建 许可 就绪 I/O完成 时间进片程完调度 阻塞 释放 执行 I/O请求 终止 图 2-7 进程的五种基本状态及转换 图 2-8 示出了增加了创建状态和终止状态后,具有挂起状态的进程状态及转换图。 请求I/O 释 放 创建 许可 许可 活动 就绪 放 释 活动 激活 阻塞 挂起 执行 释放 挂 起 激活 静止 挂起 就绪 静止 阻塞 终止 图 2-8 具有创建、终止和挂起状态的进程状态图 如图 2-8 所示,引进创建和终止状态后,在进程状态转换时,相比较图 2-7 所示的进程 五状态转换而言,需要增加考虑下面的几种情况。 (1) NULL→创建:一个新进程产生时,该进程处于创建状态。 (2) 创建→活动就绪:在当前系统的性能和内存的容量均允许的情况下,完成对进程创 建的必要操作后,相应的系统进程将进程的状态转换为活动就绪状态。 第二章 进 程 管 理 ·41· (3) 创建→静止就绪:考虑到系统当前资源状况和性能要求,并不分配给新建进程所需 资源,主要是主存资源,相应的系统进程将进程状态转为静止就绪状态,对换到外存,不 再参与调度,此时进程创建工作尚未完成。 (4) 执行→终止:当一个进程到达了自然结束点,或是出现了无法克服的错误,或是被 操作系统所终结,或是被其他有终止权的进程所终结,进程即进终止状态。 2.1.5 进程控制块 1.进程控制块的作用 为了描述和控制进程的运行,系统为每个进程定义了一个数据结构——进程控制块 PCB(Process Control Block),它是进程实体的一部分,是操作系统中最重要的记录型数据结 构。PCB 中记录了操作系统所需的、用于描述进程的当前情况以及控制进程运行的全部信 息。进程控制块的作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个 能独立运行的基本单位,一个能与其它进程并发执行的进程。或者说,OS 是根据 PCB 来对 并发执行的进程进行控制和管理的。例如,当 OS 要调度某进程执行时,要从该进程的 PCB 中查出其现行状态及优先级;在调度到某进程后,要根据其 PCB 中所保存的处理机状态信 息,设置该进程恢复运行的现场,并根据其 PCB 中的程序和数据的内存始址,找到其程序 和数据;进程在执行过程中,当需要和与之合作的进程实现同步、通信或访问文件时,也 都需要访问 PCB;当进程由于某种原因而暂停执行时,又须将其断点的处理机环境保存在 PCB 中。可见,在进程的整个生命期中,系统总是通过 PCB 对进程进行控制的,亦即,系 统是根据进程的 PCB 而不是任何别的什么而感知到该进程的存在的。所以说,PCB 是进程 存在的惟一标志。 当系统创建一个新进程时,就为它建立了一个 PCB;进程结束时又回收其 PCB,进程 于是也随之消亡。PCB 可以被操作系统中的多个模块读或修改,如被调度程序、资源分配 程序、中断处理程序以及监督和分析程序等读或修改。因为 PCB 经常被系统访问,尤其是 被运行频率很高的进程及分派程序访问,故 PCB 应常驻内存。系统将所有的 PCB 组织成若 干个链表(或队列),存放在操作系统中专门开辟的 PCB 区内。例如在 Linux 系统中用 task_struct 数据结构来描述每个进程的进程控制块,在 Windows 操作系统中则使用一个执行 体进程块(EPROCESS)来表示进程对象的基本属性。 2.进程控制块中的信息 在进程控制块中,主要包括下述四方面的信息。 1) 进程标识符 进程标识符用于惟一地标识一个进程。一个进程通常有两种标识符: (1) 内部标识符。在所有的操作系统中,都为每一个进程赋予了一个惟一的数字标识符, 它通常是一个进程的序号。设置内部标识符主要是为了方便系统使用。 (2) 外部标识符。它由创建者提供,通常是由字母、数字组成,往往是由用户(进程)在 访问该进程时使用。为了描述进程的家族关系,还应设置父进程标识及子进程标识。此外, 还可设置用户标识,以指示拥有该进程的用户。 ·42· 计算机操作系统 2) 处理机状态 处理机状态信息主要是由处理机的各种寄存器中的内容组成的。处理机在运行时,许 多信息都放在寄存器中。当处理机被中断时,所有这些信息都必须保存在 PCB 中,以便在 该进程重新执行时,能从断点继续执行。这些寄存器包括:① 通用寄存器,又称为用户可 视寄存器,它们是用户程序可以访问的,用于暂存信息,在大多数处理机中,有 8~32 个 通用寄存器,在 RISC 结构的计算机中可超过 100 个;② 指令计数器,其中存放了要访问 的下一条指令的地址;③ 程序状态字 PSW,其中含有状态信息,如条件码、执行方式、中 断屏蔽标志等;④ 用户栈指针,指每个用户进程都有一个或若干个与之相关的系统栈,用 于存放过程和系统调用参数及调用地址,栈指针指向该栈的栈顶。 3) 进程调度信息 在 PCB 中还存放一些与进程调度和进程对换有关的信息,包括:① 进程状态,指明进 程的当前状态,作为进程调度和对换时的依据;② 进程优先级,用于描述进程使用处理机 的优先级别的一个整数,优先级高的进程应优先获得处理机;③ 进程调度所需的其它信息, 它们与所采用的进程调度算法有关,比如,进程已等待 CPU 的时间总和、进程已执行的时 间总和等;④ 事件,指进程由执行状态转变为阻塞状态所等待发生的事件,即阻塞原因。 4) 进程控制信息 进程控制信息包括:① 程序和数据的地址,指进程的程序和数据所在的内存或外存地 (首)址,以便再调度到该进程执行时,能从 PCB 中找到其程序和数据;② 进程同步和通信 机制,指实现进程同步和进程通信时必需的机制,如消息队列指针、信号量等,它们可能 全部或部分地放在 PCB 中;③ 资源清单,即一张列出了除 CPU 以外的、进程所需的全部 资源及已经分配到该进程的资源的清单;④ 链接指针,它给出了本进程(PCB)所在队列中 的下一个进程的 PCB 的首地址。 3. 进程控制块的组织方式 在一个系统中,通常可拥有数十个、 数百个乃至数千个 PCB。为了能对它们加以有效 的管理,应该用适当的方式将这些 PCB 组织起来。目前常用的组织方式有以下两种。 1) 链接方式 这是把具有同一状态的 PCB,用其中 的链接字链接成一个队列。这样,可以形 成就绪队列、若干个阻塞队列和空白队列 等。对其中的就绪队列常按进程优先级的 高低排列,把优先级高的进程的 PCB 排在 队列前面。此外,也可根据阻塞原因的不 同而把处于阻塞状态的进程的 PCB 排成 等待 I/O 操作完成的队列和等待分配内存 的队列等。图 2-9 示出了一种链接队列的 组织方式。 执行指针 就绪队列指针 阻塞队列指针 空闲队列指针 PCB1 4 PCB2 3 PCB3 0 PCB4 8 PCB5 PCB6 7 PCB7 9 PCB8 0 PCB9 1 … 图 2-9 PCB 链接队列示意图 2) 索引方式 系统根据所有进程的状态建立几张索引表。例如,就绪索引表、阻塞索引表等,并把 第二章 进 程 管 理 ·43· 各索引表在内存的首地址记录在内存的一些专用单元中。在每个索引表的表目中,记录具 有相应状态的某个 PCB 在 PCB 表中的地址。图 2-10 示出了索引方式的 PCB 组织。 执行指针 就绪表指针 阻塞表指针 就绪索引表 阻塞索引表 PCB1 PCB2 PCB3 PCB4 PCB5 PCB6 PCB7 图 2-10 按索引方式组织 PCB 2.2 进 程 控 制 进程控制是进程管理中最基本的功能。它用于创建一个新进程,终止一个已完成的进 程,或终止一个因出现某事件而使其无法运行下去的进程,还可负责进程运行中的状态转 换。如当一个正在执行的进程因等待某事件而暂时不能继续执行时,将其转换为阻塞状态, 而当该进程所期待的事件出现时,又将该进程转换为就绪状态等等。进程控制一般是由 OS 的内核中的原语来实现的。 原语(Primitive)是由若干条指令组成的,用于完成一定功能的一个过程。它与一般过程 的区别在于:它们是“原子操作(Action Operation)”。所谓原子操作,是指一个操作中的所 有动作要么全做,要么全不做。换言之,它是一个不可分割的基本单位,因此,在执行过 程中不允许被中断。原子操作在管态下执行,常驻内存。 原语的作用是为了实现进程的通信和控制,系统对进程的控制如不使用原语,就会造 成其状态的不确定性,从而达不到进程控制的目的。 2.2.1 进程的创建 1.进程图(Process Graph) 进程图是用于描述一个进程的家族关系的有向 树,如图 2-11 所示。图中的结点(圆圈)代表进程。 在进程 D 创建了进程 I 之后,称 D 是 I 的父进程 (Parent Process),I 是 D 的子进程(Progeny Process)。 这里可用一条由父进程指向子进程的有向边来 描述它们之间的父子关系。创建父进程的进程称为 祖先进程,这样便形成了一棵进程树,把树的根结 点作为进程家族的祖先(Ancestor)。 A B C D E F G H I J K L M 图 2-11 进程树 ·44· 计算机操作系统 了解进程间的这种关系是十分重要的。因为子进程可以继承父进程所拥有的资源,例 如,继承父进程打开的文件,继承父进程所分配到的缓冲区等。当子进程被撤消时,应将 其从父进程那里获得的资源归还给父进程。此外,在撤消父进程时,也必须同时撤消其所 有的子进程。为了标识进程之间的家族关系,在 PCB 中都设置了家族关系表项,以标明自 己的父进程及所有的子进程。 2.引起创建进程的事件 在多道程序环境中,只有(作为)进程(时)才能在系统中运行。因此,为使程序能运行, 就必须为它创建进程。导致一个进程去创建另一个进程的典型事件,可有以下四类: (1) 用户登录。在分时系统中,用户在终端键入登录命令后,如果是合法用户,系统将 为该终端建立一个进程,并把它插入就绪队列中。 (2) 作业调度。在批处理系统中,当作业调度程序按一定的算法调度到某作业时,便将 该作业装入内存,为它分配必要的资源,并立即为它创建进程,再插入就绪队列中。 (3) 提供服务。当运行中的用户程序提出某种请求后,系统将专门创建一个进程来提供 用户所需要的服务,例如,用户程序要求进行文件打印,操作系统将为它创建一个打印进 程,这样,不仅可使打印进程与该用户进程并发执行,而且还便于计算出为完成打印任务 所花费的时间。 (4) 应用请求。在上述三种情况下,都是由系统内核为它创建一个新进程;而第 4 类事 件则是基于应用进程的需求,由它自己创建一个新进程,以便使新进程以并发运行方式完 成特定任务。例如,某应用程序需要不断地从键盘终端输入数据,继而又要对输入数据进 行相应的处理,然后,再将处理结果以表格形式在屏幕上显示。该应用进程为使这几个操 作能并发执行,以加速任务的完成,可以分别建立键盘输入进程、表格输出进程。 3.进程的创建(Creation of Process) 一旦操作系统发现了要求创建新进程的事件后,便调用进程创建原语 Creat( )按下述步 骤创建一个新进程。 (1) 申请空白 PCB。为新进程申请获得惟一的数字标识符,并从 PCB 集合中索取一个 空白 PCB。 (2) 为新进程分配资源。为新进程的程序和数据以及用户栈分配必要的内存空间。显然, 此时操作系统必须知道新进程所需内存的大小。对于批处理作业,其大小可在用户提出创建 进程要求时提供。若是为应用进程创建子进程,也应是在该进程提出创建进程的请求中给出 所需内存的大小。对于交互型作业,用户可以不给出内存要求而由系统分配一定的空间。如 果新进程要共享某个已在内存的地址空间(即已装入内存的共享段),则必须建立相应的链接。 (3) 初始化进程控制块。PCB 的初始化包括:① 初始化标识信息,将系统分配的标识 符和父进程标识符填入新 PCB 中;② 初始化处理机状态信息,使程序计数器指向程序的入 口地址,使栈指针指向栈顶;③ 初始化处理机控制信息,将进程的状态设置为就绪状态或 静止就绪状态,对于优先级,通常是将它设置为最低优先级,除非用户以显式方式提出高 优先级要求。 (4) 将新进程插入就绪队列,如果进程就绪队列能够接纳新进程,便将新进程插入就绪 队列。 第二章 进 程 管 理 ·45· 2.2.2 进程的终止 1.引起进程终止的事件 1) 正常结束 在任何计算机系统中,都应有一个用于表示进程已经运行完成的指示。例如,在批处 理系统中,通常在程序的最后安排一条 Holt 指令或终止的系统调用。当程序运行到 Holt 指 令时,将产生一个中断,去通知 OS 本进程已经完成。在分时系统中,用户可利用 Logs off 去表示进程运行完毕,此时同样可产生一个中断,去通知 OS 进程已运行完毕。 2) 异常结束 在进程运行期间,由于出现某些错误和故障而迫使进程终止(Termination of Process)。 这类异常事件很多,常见的有下述几种: (1) 越界错误。这是指程序所访问的存储区已越出该进程的区域。 (2) 保护错。这是指进程试图去访问一个不允许访问的资源或文件,或者以不适当的方 式进行访问,例如,进程试图去写一个只读文件。 (3) 非法指令。这是指程序试图去执行一条不存在的指令。出现该错误的原因,可能是 程序错误地转移到数据区,把数据当成了指令。 (4) 特权指令错。这是指用户进程试图去执行一条只允许 OS 执行的指令。 (5) 运行超时。这是指进程的执行时间超过了指定的最大值。 (6) 等待超时。这是指进程等待某事件的时间超过了规定的最大值。 (7) 算术运算错。这是指进程试图去执行一个被禁止的运算,例如被 0 除。 (8) I/O 故障。这是指在 I/O 过程中发生了错误等。 3) 外界干预 外界干预并非指在本进程运行中出现了异常事件,而是指进程应外界的请求而终止运 行。这些干预有: (1) 操作员或操作系统干预。由于某种原因,例如,发生了死锁,由操作员或操作系统 终止该进程。 (2) 父进程请求。由于父进程具有终止自己的任何子孙进程的权力,因而当父进程提出 请求时,系统将终止该进程。 (3) 父进程终止。当父进程终止时,OS 也将它的所有子孙进程终止。 2.进程的终止过程 如果系统中发生了上述要求终止进程的某事件,OS 便调用进程终止原语,按下述过程 去终止指定的进程。 (1) 根据被终止进程的标识符,从 PCB 集合中检索出该进程的 PCB,从中读出该进程 的状态。 (2) 若被终止进程正处于执行状态,应立即终止该进程的执行,并置调度标志为真,用 于指示该进程被终止后应重新进行调度。 (3) 若该进程还有子孙进程,还应将其所有子孙进程予以终止,以防它们成为不可控的 进程。 ·46· 计算机操作系统 (4) 将被终止进程所拥有的全部资源,或者归还给其父进程,或者归还给系统。 (5) 将被终止进程(PCB)从所在队列(或链表)中移出,等待其他程序来搜集信息。 2.2.3 进程的阻塞与唤醒 1. 引起进程阻塞和唤醒的事件 有下述几类事件会引起进程阻塞或被唤醒。 1) 请求系统服务 当正在执行的进程请求操作系统提供服务时,由于某种原因,操作系统并不立即满足 该进程的要求时,该进程只能转变为阻塞状态来等待。例如,一进程请求使用某资源,如 打印机,由于系统已将打印机分配给其他进程而不能分配给请求进程,这时请求者进程只 能被阻塞,仅在其他进程在释放出打印机的同时,才将请求进程唤醒。 2) 启动某种操作 当进程启动某种操作后,如果该进程必须在该操作完成之后才能继续执行,则必须先 使该进程阻塞,以等待该操作完成。例如,进程启动了某 I/O 设备,如果只有在 I/O 设备完 成了指定的 I/O 操作任务后进程才能继续执行,则该进程在启动了 I/O 操作后,便自动进入 阻塞状态去等待。在 I/O 操作完成后,再由中断处理程序或中断进程将该进程唤醒。 3) 新数据尚未到达 对于相互合作的进程,如果其中一个进程需要先获得另一(合作)进程提供的数据后才能 对数据进行处理,则只要其所需数据尚未到达,该进程只有(等待)阻塞。例如,有两个进程, 进程 A 用于输入数据,进程 B 对输入数据进行加工。假如 A 尚未将数据输入完毕,则进程 B 将因没有所需的处理数据而阻塞;一旦进程 A 把数据输入完毕,便可去唤醒进程 B。 4) 无新工作可做 系统往往设置一些具有某特定功能的系统进程,每当这种进程完成任务后,便把自己 阻塞起来以等待新任务到来。例如,系统中的发送进程,其主要工作是发送数据,若已有 的数据已全部发送完成而又无新的发送请求,这时(发送)进程将使自己进入阻塞状态; 仅 当又有进程提出新的发送请求时,才将发送进程唤醒。 2.进程阻塞过程 正在执行的进程,当发现上述某事件时,由于无法继续执行,于是进程便通过调用阻 塞原语 block 把自己阻塞。可见,进程的阻塞是进程自身的一种主动行为。进入 block 过程 后,由于此时该进程还处于执行状态,所以应先立即停止执行,把进程控制块中的现行状 态由“执行”改为“阻塞”,并将 PCB 插入阻塞队列。如果系统中设置了因不同事件而阻塞 的多个阻塞队列,则应将本进程插入到具有相同事件的阻塞(等待)队列。最后,转调度程序 进行重新调度,将处理机分配给另一就绪进程并进行切换,亦即,保留被阻塞进程的处理 机状态(在 PCB 中),再按新进程的 PCB 中的处理机状态设置 CPU 的环境。 3.进程唤醒过程 当被阻塞进程所期待的事件出现时,如 I/O 完成或其所期待的数据已经到达,则由有关 进程(比如用完并释放了该 I/O 设备的进程)调用唤醒原语 wakeup( ),将等待该事件的进程唤 第二章 进 程 管 理 ·47· 醒。唤醒原语执行的过程是:首先把被阻塞的进程从等待该事件的阻塞队列中移出,将其 PCB 中的现行状态由阻塞改为就绪,然后再将该 PCB 插入到就绪队列中。 应当指出,block 原语和 wakeup 原语是一对作用刚好相反的原语。因此,如果在某进 程中调用了阻塞原语,则必须在与之相合作的另一进程中或其他相关的进程中安排唤醒原 语,以能唤醒阻塞进程;否则,被阻塞进程将会因不能被唤醒而长久地处于阻塞状态,从 而再无机会继续运行。 2.2.4 进程的挂起与激活 1.进程的挂起 当出现了引起进程挂起的事件时,比如,用户进程请求将自己挂起,或父进程请求将 自己的某个子进程挂起,系统将利用挂起原语 suspend( )将指定进程或处于阻塞状态的进程 挂起。挂起原语的执行过程是:首先检查被挂起进程的状态,若处于活动就绪状态,便将 其改为静止就绪; 对于活动阻塞状态的进程,则将之改为静止阻塞。为了方便用户或父进 程考查该进程的运行情况而把该进程的 PCB 复制到某指定的内存区域。最后,若被挂起的 进程正在执行,则转向调度程序重新调度。 2.进程的激活过程 当发生激活进程的事件时,例如,父进程或用户进程请求激活指定进程,若该进程驻 留在外存而内存中已有足够的空间时,则可将在外存上处于静止就绪状态的该进程换入内 存。这时,系统将利用激活原语 active( )将指定进程激活。激活原语先将进程从外存调入内 存,检查该进程的现行状态,若是静止就绪,便将之改为活动就绪;若为静止阻塞,便将 之改为活动阻塞。假如采用的是抢占调度策略,则每当有新进程进入就绪队列时,应检查 是否要进行重新调度,即由调度程序将被激活进程与当前进程进行优先级的比较,如果被 激活进程的优先级更低,就不必重新调度;否则,立即剥夺当前进程的运行,把处理机分 配给刚被激活的进程。 2.3 进 程 同 步 在 OS 中引入进程后,虽然提高了资源的利用率和系统的吞吐量,但由于进程的异步性, 也会给系统造成混乱,尤其是在他们争用临界资源时。例如,当多个进程去争用一台打印 机时,有可能使多个进程的输出结果交织在一起,难于区分;而当多个进程去争用共享变 量、表格、链表时,有可能致使数据处理出错。进程同步的主要任务是对多个相关进程在 执行次序上进行协调,以使并发执行的诸进程之间能有效地共享资源和相互合作,从而使 程序的执行具有可再现性。 2.3.1 进程同步的基本概念 1.两种形式的制约关系 在多道程序环境下,当程序并发执行时,由于资源共享和进程合作,使同处于一个系 统中的诸进程之间可能存在着以下两种形式的制约关系。 ·48· 计算机操作系统 (1) 间接相互制约关系。同处于一个系统中的进程,通常都共享着某种系统资源,如共 享 CPU、共享 I/O 设备等。所谓间接相互制约即源于这种资源共享,例如,有两个进程 A 和 B,如果在 A 进程提出打印请求时,系统已将惟一的一台打印机分配给了进程 B,则此 时进程 A 只能阻塞;一旦进程 B 将打印机释放,则 A 进程才能由阻塞改为就绪状态。 (2) 直接相互制约关系。这种制约主要源于进程间的合作。例如,有一输入进程 A 通过 单缓冲向进程 B 提供数据。当该缓冲空时,计算进程因不能获得所需数据而阻塞,而当进 程 A 把数据输入缓冲区后,便将进程 B 唤醒;反之,当缓冲区已满时,进程 A 因不能再向 缓冲区投放数据而阻塞,当进程 B 将缓冲区数据取走后便可唤醒 A。 2. 临界资源 在第一章中我们曾经介绍过,许多硬件资源如打印机、磁带机等,都属于临界资源 (Critical Resouce),诸进程间应采取互斥方式,实现对这种资源的共享。下面我们将通过一 个简单的例子来说明这一过程。 生产者-消费者(producer-consumer)问题是一个著名的进程同步问题。它描述的是:有一 群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消 费者进程能并发执行,在两者之间设置了一个具有 n 个缓冲区的缓冲池,生产者进程将它 所生产的产品放入一个缓冲区中;消费者进程可从一个缓冲区中取走产品去消费。尽管所 有的生产者进程和消费者进程都是以异步方式运行的,但它们之间必须保持同步,即不允 许消费者进程到一个空缓冲区去取产品,也不允许生产者进程向一个已装满产品且尚未被 取走的缓冲区中投放产品。 我们可利用一个数组来表示上述的具有 n 个(0,1,…,n-1)缓冲区的缓冲池。用输入 指针 in 来指示下一个可投放产品的缓冲区,每当生产者进程生产并投放一个产品后,输入 指针加 1;用一个输出指针 out 来指示下一个可从中获取产品的缓冲区,每当消费者进程取 走一个产品后,输出指针加 1。由于这里的缓冲池是组织成循环缓冲的,故应把输入指针加 1 表示成 in:= (in+1)mod n; 输出指针加 1 表示成 out:= (out+1) mod n。当 (in+1) mod n=out 时表示缓冲池满;而 in=out 则表示缓冲池空。此外,还引入了一个整型变量 counter,其初 始值为 0。每当生产者进程向缓冲池中投放一个产品后,使 counter 加 1;反之,每当消费 者进程从中取走一个产品时,使 counter 减 1。生产者和消费者两进程共享下面的变量: Var n,integer; type item=…; var buffer: array[0,1,…,n-1] of item; in,out: 0,1,…,n-1; counter: 0,1,…,n; 指针 in 和 out 初始化为 1。在生产者和消费者进程的描述中,noop 是一条空操作指令, while condition do no-op 语句表示重复的测试条件(condication),重复测试应进行到该条件变 为 false(假),即到该条件不成立时为止。在生产者进程中使用一局部变量 nextp,用于暂时 存放每次刚生产出来的产品;而在消费者进程中,则使用一个局部变量 nextc,用于存放每 次要消费的产品。 producer: repeat 第二章 进 程 管 理 ·49· M produce an item in nextp; M while counter=n do no-op; buffer[in]:=nextp; in:=in+1 mod n; counter:=counter+1; until false; consumer: repeat while counter=0 do no-op; nextc:=buffer[out]; out:=(out+1) mod n; counter:=counter-1; consumer the item in nextc; until false; 虽然上面的生产者程序和消费者程序在分别看时都是正确的,而且两者在顺序执行时 其结果也会是正确的,但若并发执行时就会出现差错,问题就在于这两个进程共享变量 counter。生产者对它做加 1 操作,消费者对它做减 1 操作,这两个操作在用机器语言实现 时, 常可用下面的形式描述: register1:=counter; register2:=counter; register1:=register1+1; register2:=register2-1; counter:=register1; counter:=register2; 假设 counter 的当前值是 5。如果生产者进程先执行左列的三条机器语言语句,然后消 费者进程再执行右列的三条语句,则最后共享变量 counter 的值仍为 5; 反之,如果让消费 者进程先执行右列的三条语句,然后再让生产者进程执行左列的三条语句,则 counter 值也 还是 5,但是,如果按下述顺序执行: register1:=counter; (register1=5) register1:=register1+1; (register1=6) register2:=counter; (register2=5) register2:=register2-1; (register2=4) counter:=register1; (counter=6) counter:=register2; (counter=4) 正确的 counter 值应当是 5,但现在是 4。读者可以自己试试,倘若再将两段程序中各语句 交叉执行的顺序改变,将可看到又可能得到 counter=6 的答案,这表明程序的执行已经失去 了再现性。为了预防产生这种错误,解决此问题的关键是应把变量 counter 作为临界资源处 理,亦即,令生产者进程和消费者进程互斥地访问变量 counter。 3.临界区 由前所述可知,不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它 ·50· 计算机操作系统 进行访问。人们把在每个进程中访问临界资源的那段代码称为临界区(critical section)。显然, 若能保证诸进程互斥地进入自己的临界区,便可实现诸进程对临界资源的互斥访问。为此, 每个进程在进入临界区之前,应先对欲访问的临界资源进行检查,看它是否正被访问。如 果此刻该临界资源未被访问,进程便可进入临界区对该资源进行访问,并设置它正被访问 的标志;如果此刻该临界资源正被某进程访问,则本进程不能进入临界区。因此,必须在 临界区前面增加一段用于进行上述检查的代码,把这段代码称为进入区(entry section)。相应 地,在临界区后面也要加上一段称为退出区(exit section)的代码,用于将临界区正被访问的 标志恢复为未被访问的标志。进程中除上述进入区、临界区及退出区之外的其它部分的代 码,在这里都称为剩余区。这样,可把一个访问临界资源的循环进程描述如下: repeat entry section critical section; exit section remainder section; until false; 4.同步机制应遵循的规则 为实现进程互斥地进入自已的临界区,可用软件方法,更多的是在系统中设置专门的 同步机构来协调各进程间的运行。所有同步机制都应遵循下述四条准则: (1) 空闲让进。当无进程处于临界区时,表明临界资源处于空闲状态,应允许一个请求 进入临界区的进程立即进入自己的临界区,以有效地利用临界资源。 (2) 忙则等待。当已有进程进入临界区时,表明临界资源正在被访问,因而其它试图进 入临界区的进程必须等待,以保证对临界资源的互斥访问。 (3) 有限等待。对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区, 以免陷入“死等”状态。 (4) 让权等待。当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入“忙 等”状态。 2.3.2 信号量机制 1965 年,荷兰学者 Dijkstra 提出的信号量(Semaphores)机制是一种卓有成效的进程同步 工具。在长期且广泛的应用中,信号量机制又得到了很大的发展,它从整型信号量经记录 型信号量,进而发展为“信号量集”机制。现在,信号量机制已被广泛地应用于单处理机 和多处理机系统以及计算机网络中。 1.整型信号量 最初由 Dijkstra 把整型信号量定义为一个用于表示资源数目的整型量 S,它与一般整型 量不同,除初始化外,仅能通过两个标准的原子操作(Atomic Operation) wait(S)和 signal(S) 来访问。很长时间以来,这两个操作一直被分别称为 P、V 操作。Wait(S)和 signal(S)操作可 描述为: wait(S): while S<=0 do no-op; 第二章 进 程 管 理 ·51· S:=S-1; signal(S): S:=S+1; wait(S)和 signal(S)是两个原子操作,因此,它们在执行时是不可中断的。亦即,当一个 进程在修改某信号量时,没有其他进程可同时对该信号量进行修改。此外,在 wait 操作中, 对 S 值的测试和做 S:=S-1 操作时都不可中断。 2.记录型信号量 在整型信号量机制中的 wait 操作,只要是信号量 S≤0,就会不断地测试。因此,该机 制并未遵循“让权等待”的准则,而是使进程处于“忙等”的状态。记录型信号量机制则 是一种不存在“忙等”现象的进程同步机制。但在采取了“让权等待”的策略后,又会出 现多个进程等待访问同一临界资源的情况。为此,在信号量机制中,除了需要一个用于代 表资源数目的整型变量 value 外,还应增加一个进程链表指针 L,用于链接上述的所有等待 进程。记录型信号量是由于它采用了记录型的数据结构而得名的。它所包含的上述两个数 据项可描述为: type semaphore=record value: integer; L: list of process; end 相应地,wait(S)和 signal(S)操作可描述为: procedure wait(S) var S:semaphore; begin S.value:=S.value-1; if S.value<0 then block(S.L); end procedure signal(S) var S: semaphore; begin S.value:=S.value+1; if S.value<=0 then wakeup(S.L); end 在记录型信号量机制中,S.value 的初值表示系统中某类资源的数目,因而又称为资源 信号量。对它的每次 wait 操作,意味着进程请求一个单位的该类资源,使系统中可供分配 的该类资源数减少一个,因此描述为 S.value:=S.value-1;当 S.value<0 时,表示该类资源已 分配完毕,因此进程应调用 block 原语,进行自我阻塞,放弃处理机,并插入到信号量链表 S.L 中。可见,该机制遵循了“让权等待”准则。此时 S.value 的绝对值表示在该信号量链 表中已阻塞进程的数目。对信号量的每次 signal 操作,表示执行进程释放一个单位资源,使 系统中可供分配的该类资源数增加一个,故 S.value:=S.value+1 操作表示资源数目加 1。若 加 1 后仍是 S.value≤0,则表示在该信号量链表中,仍有等待该资源的进程被阻塞,故还应 调用 wakeup 原语,将 S.L 链表中的第一个等待进程唤醒。如果 S.value 的初值为 1,表示只 ·52· 计算机操作系统 允许一个进程访问临界资源,此时的信号量转化为互斥信号量,用于进程互斥。 3.AND 型信号量 上述的进程互斥问题,是针对各进程之间只共享一个临界资源而言的。在有些应用场 合,是一个进程需要先获得两个或更多的共享资源后方能执行其任务。假定现有两个进程 A 和 B,他们都要求访问共享数据 D 和 E。当然,共享数据都应作为临界资源。为此,可为 这两个数据分别设置用于互斥的信号量 Dmutex 和 Emutex,并令它们的初值都是 1。相应地, 在两个进程中都要包含两个对 Dmutex 和 Emutex 的操作,即 process A: process B: wait(Dmutex); wait(Emutex); wait(Emutex); wait(Dmutex); 若进程 A 和 B 按下述次序交替执行 wait 操作: process A: wait(Dmutex); 于是 Dmutex=0 process B: wait(Emutex); 于是 Emutex=0 process A: wait(Emutex); 于是 Emutex=-1 A 阻塞 process B: wait(Dmutex); 于是 Dmutex=-1 B 阻塞 最后,进程 A 和 B 处于僵持状态。在无外力作用下,两者都将无法从僵持状态中解脱 出来。我们称此时的进程 A 和 B 已进入死锁状态。显然,当进程同时要求的共享资源愈多 时,发生进程死锁的可能性也就愈大。 AND 同步机制的基本思想是:将进程在整个运行过程中需要的所有资源,一次性全部 地分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其它所 有可能为之分配的资源也不分配给它。亦即,对若干个临界资源的分配,采取原子操作方 式:要么把它所请求的资源全部分配到进程,要么一个也不分配。由死锁理论可知,这样 就可避免上述死锁情况的发生。为此,在 wait 操作中,增加了一个“AND”条件,故称为 AND 同步,或称为同时 wait 操作,即 Swait(Simultaneous wait)定义如下: Swait(S1,S2,…,Sn) if Si>=1 and … and Sn>=1 then for i:=1 to n do Si:=Si-1; endfor else place the process in the waiting queue associated with the first Si found with Si<1,and set the program count of this process to the beginning of Swait operation endif Ssignal(S1,S2,…,Sn) for i:=1 to n do Si:=Si+1; Remove all the process waiting in the queue associated with Si into the ready queue. endfor; 第二章 进 程 管 理 ·53· 4.信号量集 在记录型信号量机制中,wait(S)或 signal(S)操作仅能对信号量施以加 1 或减 1 操作,意 味着每次只能获得或释放一个单位的临界资源。而当一次需要 N 个某类临界资源时,便要 进行 N 次 wait(S)操作,显然这是低效的。此外,在有些情况下,当资源数量低于某一下限 值时,便不予以分配。因而,在每次分配之前,都必须测试该资源的数量,看其是否大于 其下限值。基于上述两点,可以对 AND 信号量机制加以扩充,形成一般化的“信号量集” 机制。Swait 操作可描述如下,其中 S 为信号量,d 为需求值,而 t 为下限值。 Swait(S1,t1,d1,…,Sn,tn,dn) if Si>=t1 and … and Sn>=tn then for i:=1 to n do Si:=Si-di; endfor else Place the executing process in the waiting queue of the first Si with Si1 时)或互斥信号 量(S=1 时)。 (3) Swait(S,1,0)。这是一种很特殊且很有用的信号量操作。当 S≥1 时,允许多个进 程进入某特定区;当 S 变为 0 后,将阻止任何进程进入特定区。换言之,它相当于一个可 控开关。 2.3.3 信号量的应用 1.利用信号量实现进程互斥 为使多个进程能互斥地访问某临界资源,只须为该资源设置一互斥信号量 mutex,并设 其初始值为 1,然后将各进程访问该资源的临界区 CS 置于 wait(mutex)和 signal(mutex)操作 之间即可。这样,每个欲访问该临界资源的进程在进入临界区之前,都要先对 mutex 执行 wait 操作,若该资源此刻未被访问,本次 wait 操作必然成功,进程便可进入自己的临界区, 这时若再有其他进程也欲进入自己的临界区,此时由于对 mutex 执行 wait 操作定会失败, ·54· 计算机操作系统 因而该进程阻塞,从而保证了该临界资源能被互斥地访问。当访问临界资源的进程退出临 界区后,又应对 mutex 执行 signal 操作,以便释放该临界资源。利用信号量实现进程互斥的 进程可描述如下: Var mutex: semaphore:=1; begin parbegin process 1: begin repeat wait(mutex); critical section signal(mutex); remainder seetion until false; end process 2: begin repeat wait(mutex); critical section signal(mutex); remainder section until false; end parend 在利用信号量机制实现进程互斥时应注意,wait(mutex)和 signal(mutex)必须成对地出现。 缺少 wait(mutex)将会导致系统混乱,不能保证对临界资源的互斥访问;而缺少 signal(mutex) 将会使临界资源永远不被释放,从而使因等待该资源而阻塞的进程不能被唤醒。 2.利用信号量实现前趋关系 还可利用信号量来描述程序或语句之间的前趋关系。设有两个并发执行的进程 P1 和 P2。 P1 中有语句 S1;P2 中有语句 S2。我们希望在 S1 执行后再执行 S2。为实现这种前趋关系, 我们只须使进程 P1 和 P2 共享一个公用信号量 S,并赋予其初值为 0,将 signal(S)操作放在 语句 S1 后面;而在 S2 语句前面插入 wait(S)操作,即 在进程 P1 中,用 S1;signal(S); S1 在进程 P2 中,用 wait(S);S2; S2 由于 S 被初始化为 0,这样,若 P2 先执行必定阻塞, 只有在进程 P1 执行完 S1;signal(S);操作后使 S 增为 1 时, S4 S5 S3 P2 进程方能执行语句 S2 成功。同样,我们可以利用信号 量,按照语句间的前趋关系(见图 2-12),写出一个更为复 S6 杂的可并发执行的程序。 图 2-12 前趋图举例 第二章 进 程 管 理 ·55· 图 2-12 示出了一个前趋图,其中 S1,S2,S3,…,S6 是最简单的程序段(只有一条语句)。 为使各程序段能正确执行,应设置若干个初始值为“0”的信号量。如为保证 S1→S2,S1→S3 的前趋关系,应分别设置信号量 a 和 b,同样,为了保证 S2→S4,S2→S5,S3→S6,S4→S6 和 S5→S6,应设置信号量 c,d,e,f,g。 Var a,b,c,d,e,f,g:semaphore: =0,0,0,0,0,0,0; begin parbegin begin S1; signal(a); signal(b); end; begin wait(a); S2; signal(c); signal(d); end; begin wait(b); S3; signal(e); end; begin wait(c); S4; signal(f); end; begin wait(d); S5; signal(g); end; begin wait(e); wait(f); wait(g); S6; end; parend end 2.3.4 管程机制 虽然信号量机制是一种既方便、又有效的进程同步机制,但每个要访问临界资源的进 程都必须自备同步操作 wait(S)和 signal(S)。这就使大量的同步操作分散在各个进程中。这 不仅给系统的管理带来了麻烦,而且还会因同步操作的使用不当而导致系统死锁。这样, 在解决上述问题的过程中,便产生了一种新的进程同步工具——管程(Monitors)。 1.管程的定义 系统中的各种硬件资源和软件资源,均可用数据结构抽象地描述其资源特性,即用少 量信息和对该资源所执行的操作来表征该资源,而忽略了它们的内部结构和实现细节。例 如,对一台电传机,可用与分配该资源有关的状态信息(busy 或 free)和对它执行请求与释放 的操作,以及等待该资源的进程队列来描述。又如,一个 FIFO 队列,可用其队长、队首和 队尾以及在该队列上执行的一组操作来描述。 利用共享数据结构抽象地表示系统中的共享资源,而把对该共享数据结构实施的操作 定义为一组过程,如资源的请求和释放过程 request 和 release。进程对共享资源的申请、释 放和其它操作,都是通过这组过程对共享数据结构的操作来实现的,这组过程还可以根据 资源的情况,或接受或阻塞进程的访问,确保每次仅有一个进程使用共享资源,这样就可 以统一管理对共享资源的所有访问,实现进程互斥。 代表共享资源的数据结构,以及由对该共享数据结构实施操作的一组过程所组成的资源 管理程序,共同构成了一个操作系统的资源管理模块,我们称之为管程。管程被请求和释放 资源的进程所调用。Hansan 为管程所下的定义是:“一个管程定义了一个数据结构和能为并 发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据”。 由上述的定义可知,管程由四部分组成:① 管程的名称;② 局部于管程内部的共享 数据结构说明;③ 对该数据结构进行操作的一组过程;④ 对局部于管程内部的共享数据 设置初始值的语句。图 2-13 是一个管程的示意图。 ·56· 计算机操作系统 条件(不忙)队列 共享数据 进入队列 … 一组操作过程 初始化代码 图 2-13 管程的示意图 管程的语法描述如下: type monitor_name = MONITOR; <共享变量说明>; define <(能被其他模块引用的)过程名列表>; use <(要调用的本模块外定义的)过程名列表>; procedure <过程名>(<形式参数表>); begin M end; M function <函数名>(<形式参数表>):值类型; begin M end; M begin <管程的局部数据初始化语句序列>; end 需要指出的是,局部于管程内部的数据结构,仅能被局部于管程内部的过程所访问, 任何管程外的过程都不能访问它;反之,局部于管程内部的过程也仅能访问管程内的数据 结构。由此可见,管程相当于围墙,它把共享变量和对它进行操作的若干过程围了起来, 所有进程要访问临界资源时,都必须经过管程(相当于通过围墙的门)才能进入,而管程每次 只准许一个进程进入管程,从而实现了进程互斥。 管程是一种程序设计语言结构成分,它和信号量有同等的表达能力,从语言的角度看, 管程主要有以下特性: (1) 模块化。管程是一个基本程序单位,可以单独编译。 第二章 进 程 管 理 ·57· (2) 抽象数据类型。管程中不仅有数据,而且有对数据的操作。 (3) 信息掩蔽。管程中的数据结构只能被管程中的过程访问,这些过程也是在管程内部 定义的,供管程外的进程调用,而管程中的数据结构以及过程(函数)的具体实现外部不可见。 管程和进程不同,主要体现在以下几个方面: (1) 虽然二者都定义了数据结构,但进程定义的是私有数据结构 PCB,管程定义的是公 共数据结构,如消息队列等; (2) 二者都存在对各自数据结构上的操作,但进程是由顺序程序执行有关的操作,而管 程主要是进行同步操作和初始化操作; (3) 设置进程的目的在于实现系统的并发性,而管程的设置则是解决共享资源的互斥使 用问题; (4) 进程通过调用管程中的过程对共享数据结构实行操作,该过程就如通常的子程序一 样被调用,因而管程为被动工作方式,进程则为主动工作方式; (5) 进程之间能并发执行,而管程则不能与其调用者并发; (6) 进程具有动态性,由“创建”而诞生,由“撤销”而消亡,而管程则是操作系统中 的一个资源管理模块,供进程调用。 2.条件变量 在利用管程实现进程同步时,必须设置同步工具,如两个同步操作原语 wait 和 signal。 当某进程通过管程请求获得临界资源而未能满足时,管程便调用 wait 原语使该进程等待, 并将其排在等待队列上,如图 2-13 所示。仅当另一进程访问完成并释放该资源之后,管程 才又调用 signal 原语,唤醒等待队列中的队首进程。 但是仅仅有上述的同步工具是不够的。考虑一种情况:当一个进程调用了管程,在管 程中时被阻塞或挂起,直到阻塞或挂起的原因解除,而在此期间,如果该进程不释放管程, 则其它进程无法进入管程,被迫长时间地等待。为了解决这个问题,引入了条件变量 condition。通常,一个进程被阻塞或挂起的条件(原因)可有多个,因此在管程中设置了多个 条件变量,对这些条件变量的访问,只能在管程中进行。 管程中对每个条件变量都须予以说明,其形式为:Var x,y:condition。对条件变量的 操作仅仅是 wait 和 signal,因此条件变量也是一种抽象数据类型,每个条件变量保存了一个 链表,用于记录因该条件变量而阻塞的所有进程,同时提供的两个操作即可表示为 x.wait 和 x.signal,其含义为: ① x.wait:正在调用管程的进程因 x 条件需要被阻塞或挂起,则调用 x.wait 将自己插 入到 x 条件的等待队列上,并释放管程,直到 x 条件变化。此时其它进程可以使用该管程。 ② x.signal:正在调用管程的进程发现 x 条件发生了变化,则调用 x.signal,重新启动 一个因 x 条件而阻塞或挂起的进程。如果存在多个这样的进程,则选择其中的一个,如果 没有,则继续执行原进程,而不产生任何结果。这与信号量机制中的 signal 操作不同,因为 后者总是要执行 s:=s+1 操作,因而总会改变信号量的状态。 如果有进程 Q 因 x 条件处于阻塞状态,当正在调用管程的进程 P 执行了 x.signal 操作后, 进程 Q 被重新启动,此时两个进程 P 和 Q,如何确定哪个执行,哪个等待,可采用下述两 种方式之一进行处理: ·58· 计算机操作系统 (1) P 等待,直至 Q 离开管程或等待另一条件。 (2) Q 等待,直至 P 离开管程或等待另一条件。 采用哪种处理方式,当然是各执一词。Hoare 采用了第一种处理方式,而 Hansan 选择 了两者的折衷,他规定管程中的过程所执行的 signal 操作是过程体的最后一个操作,于是, 进程 P 执行 signal 操作后立即退出管程,因而进程 Q 马上被恢复执行。 2.4 经典进程的同步问题 在多道程序环境下,进程同步问题十分重要,也是相当有趣的问题,因而吸引了不少 学者对它进行研究,由此而产生了一系列经典的进程同步问题,其中较有代表性的是“生 产者—消费者”问题、“读者—写者问题”、“哲学家进餐问题”等等。通过对这些问题的研 究和学习,可以帮助我们更好地理解进程同步的概念及实现方法。 2.4.1 生产者—消费者问题 前面我们已经对生产者—消费者问题(The proceducer-consumer problem)做了一些描述, 但未考虑进程的互斥与同步问题,因而造成了数据(Counter)的不定性。由于生产者—消费者 问题是相互合作的进程关系的一种抽象,例如,在输入时,输入进程是生产者,计算进程 是消费者;而在输出时,计算进程是生产者,而打印进程是消费者。因此,该问题有很大 的代表性及实用价值。本小节将利用信号量机制来解决生产者—消费者问题。 1.利用记录型信号量解决生产者—消费者问题 假定在生产者和消费者之间的公用缓冲池中,具有 n 个缓冲区,这时可利用互斥信号 量 mutex 实现诸进程对缓冲池的互斥使用。利用信号量 empty 和 full 分别表示缓冲池中空缓 冲区和满缓冲区的数量。又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者 便可将消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。对生产 者—消费者问题可描述如下: Var mutex,empty,full: semaphore:=1,n,0; buffer:array[0,…,n-1] of item; in,out: integer:=0,0; begin parbegin proceducer: begin repeat M producer an item nextp; M wait(empty); wait(mutex); buffer(in):=nextp; 第二章 进 程 管 理 ·59· in:=(in+1) mod n; signal(mutex); signal(full); until false; end consumer: begin repeat wait(full); wait(mutex); nextc:=buffer(out); out:=(out+1) mod n; signal(mutex); signal(empty); consumer the item in nextc; until false; end parend end 在生产者—消费者问题中应注意:首先,在每个程序中用于实现互斥的 wait(mutex)和 signal(mutex)必须成对地出现;其次,对资源信号量 empty 和 full 的 wait 和 signal 操作,同 样需要成对地出现,但它们分别处于不同的程序中。例如,wait(empty)在计算进程中,而 signal(empty)则在打印进程中,计算进程若因执行 wait(empty)而阻塞,则以后将由打印进程 将它唤醒;最后,在每个程序中的多个 wait 操作顺序不能颠倒,应先执行对资源信号量的 wait 操作,然后再执行对互斥信号量的 wait 操作,否则可能引起进程死锁。 2.利用 AND 信号量解决生产者—消费者问题 对于生产者—消费者问题,也可利用 AND 信号量来解决,即用 Swait(empty,mutex) 来代替 wait(empty)和 wait(mutex);用 Ssignal(mutex,full)来代替 signal(mutex)和 signal(full); 用 Swait(full,mutex)来代替 wait(full)和 wait(mutex),以及用 Ssignal(mutex,empty)代替 Signal(mutex)和 Signal(empty)。利用 AND 信号量来解决生产者—消费者问题的算法描述 如下: Var mutex,empty,full: semaphore:=1,n,0; buffer:array[0,…,n-1] of item; in out: integer:=0,0; begin parbegin producer: begin repeat M ·60· 计算机操作系统 produce an item in nextp; M Swait(empty,mutex); buffer(in):=nextp; in:=(in+1)mod n; Ssignal(mutex,full); until false; end consumer:begin repeat Swait(full,mutex); Nextc:=buffer(out); Out:=(out+1) mod n; Ssignal(mutex,empty); consumer the item in nextc; until false; end parend end 3.利用管程解决生产者—消费者问题 在利用管程方法来解决生产者—消费者问题时,首先便是为它们建立一个管程,并命 名为 ProclucerConsumer,或简称为 PC。其中包括两个过程: (1) put(item)过程。生产者利用该过程将自己生产的产品投放到缓冲池中,并用整型变 量 count 来表示在缓冲池中已有的产品数目,当 count≥n 时,表示缓冲池已满,生产者须 等待。 (2) get(item)过程。消费者利用该过程从缓冲池中取出一个产品,当 count≤0 时,表示 缓冲池中已无可取用的产品,消费者应等待。 PC 管程可描述如下: type producer-consumer=monitor Var in,out,count: integer; buffer: array[0, …, n-1] of item; notfull,notempty:condition; procedure entry put(item) begin if count>=n then notfull.wait; buffer(in):=nextp; in:=(in+1) mod n; 第二章 进 程 管 理 ·61· count:=count+1; if notempty.queue then notempty.signal; end procedure entry get(item) begin if count<=0 then notempty.wait; nextc:=buffer(out); out:=(out+1) mod n; count:=count-1; if notfull.quene then notfull.signal; end begin in:=out:=0; count:=0 end 在利用管程解决生产者—消费者问题时,其中的生产者和消费者可描述为: producer: begin repeat produce an item in nextp; PC.put(item); until false; end consumer: begin repeat PC.get(item); consume the item in nextc; until false; end 2.4.2 哲学家进餐问题 由 Dijkstra 提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同 步问题。该问题是描述有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌 上有五个碗和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进 行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。 进餐完毕,放下筷子继续思考。 1.利用记录型信号量解决哲学家进餐问题 经分析可知,放在桌子上的筷子是临界资源,在一段时间内只允许一位哲学家使用。 为了实现对筷子的互斥使用,可以用一个信号量表示一只筷子,由这五个信号量构成信号 量数组。其描述如下: ·62· 计算机操作系统 Var chopstick: array[0,…,4] of semaphore; 所有信号量均被初始化为 1,第 i 位哲学家的活动可描述为: repeat wait(chopstick[i]); wait(chopstick[(i+1)mod 5]); M eat; M signal(chopstick[i]); signal(chopstick[(i+1)mod 5]); M think; until false; 在以上描述中,当哲学家饥饿时,总是先去拿他左边的筷子,即执行 wait(chopstick[i]); 成功后,再去拿他右边的筷子,即执行 wait(chopstick[(i+1)mod 5]);又成功后便可进餐。进 餐完毕,又先放下他左边的筷子,然后再放右边的筷子。虽然,上述解法可保证不会有两 个相邻的哲学家同时进餐,但有可能引起死锁。假如五位哲学家同时饥饿而各自拿起左边 的筷子时,就会使五个信号量 chopstick 均为 0; 当他们再试图去拿右边的筷子时,都将因 无筷子可拿而无限期地等待。对于这样的死锁问题,可采取以下几种解决方法: (1) 至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够 进餐,并在用毕时能释放出他用过的两只筷子,从而使更多的哲学家能够进餐。 (2) 仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐。 (3) 规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子,而偶数号哲学家则 相反。按此规定,将是 1、2 号哲学家竞争 1 号筷子;3、4 号哲学家竞争 3 号筷子。即五位 哲学家都先竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一位哲学家能获 得两只筷子而进餐。 2.利用 AND 信号量机制解决哲学家进餐问题 在哲学家进餐问题中,要求每个哲学家先获得两个临界资源(筷子)后方能进餐,这在本 质上就是前面所介绍的 AND 同步问题,故用 AND 信号量机制可获得最简洁的解法。描述 如下: Var chopsiick array of semaphore:=(1,1,1,1,1); processi repeat think; Sswait(chopstick[(i+1)mod 5],chopstick[i]); eat; Ssignat(chopstick[(i+1)mod 5],chopstick[i]); until false; 第二章 进 程 管 理 ·63· 2.4.3 读者—写者问题 一个数据文件或记录,可被多个进程共享,我们把只要求读该文件的进程称为“Reader 进程”,其他进程则称为“Writer 进程”。允许多个进程同时读一个共享对象,因为读操作不 会使数据文件混乱。但不允许一个 Writer 进程和其他 Reader 进程或 Writer 进程同时访问共 享对象,因为这种访问将会引起混乱。所谓“读者—写者问题(Reader-Writer Problem)”是 指保证一个 Writer 进程必须与其他进程互斥地访问共享对象的同步问题。读者—写者问题 常被用来测试新同步原语。 1.利用记录型信号量解决读者—写者问题 为实现 Reader 与 Writer 进程间在读或写时的互斥而设置了一个互斥信号量 Wmutex。 另外,再设置一个整型变量 Readcount 表示正在读的进程数目。由于只要有一个 Reader 进 程在读,便不允许 Writer 进程去写。因此,仅当 Readcount=0,表示尚无 Reader 进程在读 时,Reader 进程才需要执行 Wait(Wmutex)操作。若 Wait(Wmutex)操作成功,Reader 进程便 可去读,相应地,做 Readcount+1 操作。同理,仅当 Reader 进程在执行了 Readcount 减 1 操作后其值为 0 时,才须执行 signal(Wmutex)操作,以便让 Writer 进程写。又因为 Readcount 是一个可被多个 Reader 进程访问的临界资源,因此,也应该为它设置一个互斥信号量 rmutex。 读者—写者问题可描述如下: Var rmutex,wmutex: semaphore:=1,1; Readcount: integer:=0; begin parbegin Reader: begin repeat wait(rmutex); if readcount=0 then wait(wmutex); Readcount:=Readcount+1; signal(rmutex); M perform read operation; M wait(rmutex); readcount:=readcount-1; if readcount=0 then signal(wmutex); signal(rmutex); until false; end writer: begin ·64· 计算机操作系统 repeat wait(wmutex); perform write operation; signal(wmutex); until false; end parend end 2.利用信号量集机制解决读者—写者问题 这里的读者—写者问题与前面的略有不同,它增加了一个限制,即最多只允许 RN 个读 者同时读。为此,又引入了一个信号量 L,并赋予其初值为 RN,通过执行 wait(L,1,1) 操作,来控制读者的数目。每当有一个读者进入时,就要先执行 wait(L,1,1)操作,使 L 的值减 1。当有 RN 个读者进入读后,L 便减为 0,第 RN+1 个读者要进入读时,必然会因 wait(L,1,1)操作失败而阻塞。对利用信号量集来解决读者—写者问题的描述如下: Var RN integer; L,mx: semaphore:=RN,1; begin parbegin reader: begin repeat Swait(L,1,1); Swait(mx,1,0); M perform read operation; M Ssignal(L,1); until false; end writer: begin repeat Swait(mx,1,1;L,RN,0); perform write operation; Ssignal(mx,1); until false; end parend end 其中,Swait(mx,1,0)语句起着开关的作用。只要无 writer 进程进入写,mx=1,reader 进 程就都可以进入读。但只要一旦有 writer 进程进入写时,其 mx=0,则任何 reader 进程就都 第二章 进 程 管 理 ·65· 无法进入读。Swait(mx,1,1;L,RN,0)语句表示仅当既无 writer 进程在写(mx=1),又无 reader 进程在读(L=RN)时,writer 进程才能进入临界区写。 2.5 进 程 通 信 进程通信,是指进程之间的信息交换,其所交换的信息量少者是一个状态或数值,多 者则是成千上万个字节。进程之间的互斥和同步,由于其所交换的信息量少而被归结为低 级通信。在进程互斥中,进程通过只修改信号量来向其他进程表明临界资源是否可用。在 生产者—消费者问题中,生产者通过缓冲池将所生产的产品传送给消费者。 应当指出,信号量机制作为同步工具是卓有成效的,但作为通信工具,则不够理想, 主要表现在下述两方面: (1) 效率低,生产者每次只能向缓冲池投放一个产品(消息),消费者每次只能从缓冲区 中取得一个消息; (2) 通信对用户不透明。 可见,用户要利用低级通信工具实现进程通信是非常不方便的。因为共享数据结构的 设置、数据的传送、进程的互斥与同步等,都必须由程序员去实现,操作系统只能提供共 享存储器。 本节所要介绍的是高级进程通信,是指用户可直接利用操作系统所提供的一组通信命 令高效地传送大量数据的一种通信方式。操作系统隐藏了进程通信的实现细节。或者说, 通信过程对用户是透明的。这样就大大减少了通信程序编制上的复杂性。 2.5.1 进程通信的类型 随着 OS 的发展,用于进程之间实现通信的机制也在发展,并已由早期的低级进程通信 机制发展为能传送大量数据的高级通信工具机制。目前,高级通信机制可归结为三大类: 共享存储器系统、消息传递系统以及管道通信系统。 1.共享存储器系统 在共享存储器系统(Shared-Memory System)中,相互通信的进程共享某些数据结构或共 享存储区,进程之间能够通过这些空间进行通信。据此,又可把它们分成以下两种类型: (1) 基于共享数据结构的通信方式。在这种通信方式中,要求诸进程公用某些数据结构, 借以实现诸进程间的信息交换。如在生产者—消费者问题中,就是用有界缓冲区这种数据 结构来实现通信的。这里,公用数据结构的设置及对进程间同步的处理,都是程序员的职 责。这无疑增加了程序员的负担,而操作系统却只须提供共享存储器。因此,这种通信方 式是低效的,只适于传递相对少量的数据。 (2) 基于共享存储区的通信方式。为了传输大量数据,在存储器中划出了一块共享存储 区,诸进程可通过对共享存储区中数据的读或写来实现通信。这种通信方式属于高级通信。 进程在通信前,先向系统申请获得共享存储区中的一个分区,并指定该分区的关键字;若 系统已经给其他进程分配了这样的分区,则将该分区的描述符返回给申请者,继之,由申 请者把获得的共享存储分区连接到本进程上;此后,便可像读、写普通存储器一样地读、 ·66· 计算机操作系统 写该公用存储分区。 2.消息传递系统 消息传递系统(Message passing system)是当前应用最为广泛的一种进程间的通信机制。 在该机制中,进程间的数据交换是以格式化的消息(message)为单位的;在计算机网络中, 又把 message 称为报文。程序员直接利用操作系统提供的一组通信命令(原语),不仅能实现 大量数据的传递,而且还隐藏了通信的实现细节,使通信过程对用户是透明的,从而大大 减化了通信程序编制的复杂性,因而获得了广泛的应用。 特别值得一提的是,在当今最为流行的微内核操作系统中,微内核与服务器之间的通 信,无一例外地都采用了消息传递机制。又由于它能很好地支持多处理机系统、分布式系 统和计算机网络,因此它也成为这些领域最主要的通信工具。消息传递系统的通信方式属 于高级通信方式。又因其实现方式的不同而进一步分成直接通信方式和间接通信方式两种。 3.管道通信 所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享 文件,又名 pipe 文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将 大量的数据送入管道;而接受管道输出的接收进程(即读进程),则从管道中接收(读)数据。 由于发送进程和接收进程是利用管道进行通信的,故又称为管道通信。这种方式首创于 UNIX 系统,由于它能有效地传送大量数据,因而又被引入到许多其它的操作系统中。 为了协调双方的通信,管道机制必须提供以下三方面的协调能力: (1) 互斥,即当一个进程正在对 pipe 执行读/写操作时,其它(另一)进程必须等待。 (2) 同步,指当写(输入)进程把一定数量(如 4 KB)的数据写入 pipe,便去睡眠等待,直 到读(输出)进程取走数据后,再把它唤醒。当读进程读一空 pipe 时,也应睡眠等待,直至写 进程将数据写入管道后,才将之唤醒。 (3) 确定对方是否存在,只有确定了对方已存在时,才能进行通信。 2.5.2 消息传递通信的实现方法 在进程之间通信时,源进程可以直接或间接地将消息传送给目标进程,由此可将进程 通信分为直接通信和间接通信两种通信方式。 1.直接通信方式 这是指发送进程利用 OS 所提供的发送命令,直接把消息发送给目标进程。此时,要求 发送进程和接收进程都以显式方式提供对方的标识符。通常,系统提供下述两条通信命令 (原语): Send(Receiver,message); 发送一个消息给接收进程; Receive(Sender,message); 接收 Sender 发来的消息; 例如,原语 Send(P2,m1)表示将消息 m1 发送给接收进程 P2;而原语 Receive(P1,m1) 则表示接收由 P1 发来的消息 m1。 在某些情况下,接收进程可与多个发送进程通信,因此,它不可能事先指定发送进程。 例如,用于提供打印服务的进程,它可以接收来自任何一个进程的“打印请求”消息。对 于这样的应用,在接收进程接收消息的原语中,表示源进程的参数,也是完成通信后的返 第二章 进 程 管 理 ·67· 回值,接收原语可表示为: Receive (id,message); 我们还可以利用直接通信原语来解决生产者—消费者问题。当生产者生产出一个产品 (消息)后,便用 Send 原语将消息发送给消费者进程;而消费者进程则利用 Receive 原语来得 到一个消息。如果消息尚未生产出来,消费者必须等待,直至生产者进程将消息发送过来。 生产者—消费者的通信过程可分别描述如下: repeat M produce an item in nextp; M send(consumer,nextp); until false; repeat receive(producer,nextc); M consume the item in nextc; until false; 2.间接通信方式 间接通信方式指进程之间的通信需要通过作为共享数据结构的实体。该实体用来暂存 发送进程发送给目标进程的消息;接收进程则从该实体中取出对方发送给自己的消息。通 常把这种中间实体称为信箱。消息在信箱中可以安全地保存,只允许核准的目标用户随时 读取。因此,利用信箱通信方式,既可实现实时通信,又可实现非实时通信。 系统为信箱通信提供了若干条原语,分别用于信箱的创建、撤消和消息的发送、接收等。 (1) 信箱的创建和撤消。进程可利用信箱创建原语来建立一个新信箱。创建者进程应给 出信箱名字、信箱属性(公用、私用或共享);对于共享信箱,还应给出共享者的名字。当进 程不再需要读信箱时,可用信箱撤消原语将之撤消。 (2) 消息的发送和接收。当进程之间要利用信箱进行通信时,必须使用共享信箱,并利 用系统提供的下述通信原语进行通信: Send(mailbox,message); 将一个消息发送到指定信箱; Receive(mailbox,message); 从指定信箱中接收一个消息; 信箱可由操作系统创建,也可由用户进程创建,创建者是信箱的拥有者。据此,可把 信箱分为以下三类。 1) 私用信箱 用户进程可为自己建立一个新信箱,并作为该进程的一部分。信箱的拥有者有权从信 箱中读取消息,其他用户则只能将自己构成的消息发送到该信箱中。这种私用信箱可采用 单向通信链路的信箱来实现。当拥有该信箱的进程结束时,信箱也随之消失。 2) 公用信箱 它由操作系统创建,并提供给系统中的所有核准进程使用。核准进程既可把消息发送 ·68· 计算机操作系统 到该信箱中,也可从信箱中读取发送给自己的消息。显然,公用信箱应采用双向通信链路 的信箱来实现。通常,公用信箱在系统运行期间始终存在。 3) 共享信箱 它由某进程创建,在创建时或创建后指明它是可共享的,同时须指出共享进程(用户) 的名字。信箱的拥有者和共享者都有权从信箱中取走发送给自己的消息。 在利用信箱通信时,在发送进程和接收进程之间存在以下四种关系: (1) 一对一关系。这时可为发送进程和接收进程建立一条两者专用的通信链路,使两者 之间的交互不受其他进程的干扰。 (2) 多对一关系。允许提供服务的进程与多个用户进程之间进行交互,也称为客户/服 务器交互(client/server interaction)。 (3) 一对多关系。允许一个发送进程与多个接收进程进行交互,使发送进程可用广播方 式向接收者(多个)发送消息。 (4) 多对多关系。允许建立一个公用信箱,让多个进程都能向信箱中投递消息;也可从 信箱中取走属于自己的消息。 2.5.3 消息传递系统实现中的若干问题 在单机和计算机网络环境下,高级进程通信广泛采用消息传递系统。故本小节将对这 种通信中的几个主要问题做扼要的阐述。 1.通信链路 为使在发送进程和接收进程之间能进行通信,必须在两者之间建立一条通信链路 (communication link)。有两种方式建立通信链路。第一种方式是由发送进程在通信之前用显 式的“建立连接”命令(原语)请求系统为之建立一条通信链路;在链路使用完后,也用显式 方式拆除链路。这种方式主要用于计算机网络中。第二种方式是发送进程无须明确提出建 立链路的请求,只须利用系统提供的发送命令(原语),系统会自动地为之建立一条链路。这 种方式主要用于单机系统中。 根据通信链路的连接方法,又可把通信链路分为两类: (1) 点—点连接通信链路,这时的一条链路只连接两个结点(进程); (2) 多点连接链路,指用一条链路连接多个(n>2)结点(进程)。 而根据通信方式的不同,则又可把链路分成两种: (1) 单向通信链路,只允许发送进程向接收进程发送消息,或者相反; (2) 双向链路,既允许由进程 A 向进程 B 发送消息,也允许进程 B 同时向进程 A 发送 消息。 还可根据通信链路容量的不同而把链路分成两类:一是无容量通信链路,在这种通信 链路上没有缓冲区,因而不能暂存任何消息;再者就是有容量通信链路,指在通信链路中 设置了缓冲区,因而能暂存消息。缓冲区数目愈多,通信链路的容量愈大。 2.消息的格式 在消息传递系统中所传递的消息,必须具有一定的消息格式。在单机系统环境中,由 于发送进程和接收进程处于同一台机器中,有着相同的环境,故其消息格式比较简单;但 第二章 进 程 管 理 ·69· 在计算机网络环境下,不仅源和目标进程所处的环境不同,而且信息的传输距离很远,可 能要跨越若干个完全不同的网络,致使所用的消息格式比较复杂。通常,可把一个消息分 成消息头和消息正文两部分。消息头包括消息在传输时所需的控制信息,如源进程名、目 标进程名、消息长度、消息类型、消息编号及发送的日期和时间;而消息正文则是发送进 程实际上所发送的数据。 在某些 OS 中,消息采用比较短的定长消息格式,这便减少了对消息的处理和存储开销。 这种方式可用于办公自动化系统中,为用户提供快速的便笺式通信;但这对要发送较长消 息的用户是不方便的。在有的 OS 中,采用变长的消息格式,即进程所发送消息的长度是可 变的。系统无论在处理还是在存储变长消息时,都可能会付出更多的开销,但这方便了用 户。这两种消息格式各有其优缺点,故在很多系统(包括计算机网络)中,是同时都用的。 3.进程同步方式 在进程之间进行通信时,同样需要有进程同步机制,以使诸进程间能协调通信。不论 是发送进程,还是接收进程,在完成消息的发送或接收后,都存在两种可能性,即进程或 者继续发送(接收),或者阻塞。由此,我们可得到以下三种情况: (1) 发 送进 程 阻 塞 ,接 收 进 程 阻塞 。 这 种 情 况主要 用 于 进 程 之 间紧 密 同 步(tight synchronization),发送进程和接收进程之间无缓冲时。这两个进程平时都处于阻塞状态,直 到有消息传递时。这种同步方式称为汇合(rendezrous)。 (2) 发送进程不阻塞,接收进程阻塞。这是一种应用最广的进程同步方式。平时,发送 进程不阻塞,因而它可以尽快地把一个或多个消息发送给多个目标; 而接收进程平时则处 于阻塞状态,直到发送进程发来消息时才被唤醒。例如,在服务器上通常都设置了多个服 务进程,它们分别用于提供不同的服务,如打印服务。平时,这些服务进程都处于阻塞状 态,一旦有请求服务的消息到达时,系统便唤醒相应的服务进程,去完成用户所要求的服 务。处理完后,若无新的服务请求,服务进程又阻塞。 (3) 发送进程和接收进程均不阻塞。这也是一种较常见的进程同步形式。平时,发送进 程和接收进程都在忙于自己的事情,仅当发生某事件使它无法继续运行时,才把自己阻塞 起来等待。例如,在发送进程和接收进程之间联系着一个消息队列时,该消息队列最多能 接纳 n 个消息,这样,发送进程便可以连续地向消息队列中发送消息而不必等待;接收进 程也可以连续地从消息队列中取得消息,也不必等待。只有当消息队列中的消息数已达到 n 个时,即消息队列已满,发送进程无法向消息队列中发送消息时才会阻塞;类似地,只有 当消息队列中的消息数为 0,接收进程已无法从消息队列中取得消息时才会阻塞。 2.5.4 消息缓冲队列通信机制 消息缓冲队列通信机制首先由美国的 Hansan 提出,并在 RC 4000 系统上实现,后来被 广泛应用于本地进程之间的通信中。在这种通信机制中,发送进程利用 Send 原语将消息直 接发送给接收进程;接收进程则利用 Receive 原语接收消息。 1.消息缓冲队列通信机制中的数据结构 1) 消息缓冲区 在消息缓冲队列通信方式中,主要利用的数据结构是消息缓冲区。它可描述如下: ·70· 计算机操作系统 type message buffer=record sender size text next ;发送者进程标识符 ; 消息长度 ; 消息正文 ; 指向下一个消息缓冲区的指针 end 2) PCB 中有关通信的数据项 在操作系统中采用了消息缓冲队列通信机制时,除了需要为进程设置消息缓冲队列外, 还应在进程的 PCB 中增加消息队列队首指针,用于对消息队列进行操作,以及用于实现同 步的互斥信号量 mutex 和资源信号量 sm。在 PCB 中应增加的数据项可描述如下: type processcontrol block=record M mq ; 消息队列队首指针 mutex sm M ; 消息队列互斥信号量 ; 消息队列资源信号量 end 2.发送原语 发送进程在利用发送原语发送消息之前,应先在自己的内存空间设置一发送区 a,见图 2-14 所示。把待发送的消息正文、发送进程标识符、消息长度等信息填入其中,然后调用 发送原语,把消息发送给目标(接收)进程。发送原语首先根据发送区 a 中所设置的消息长度 a.size 来申请一缓冲区 i,接着把发送区 a 中的信息复制到缓冲区 i 中。为了能将 i 挂在接收 进程的消息队列 mq 上,应先获得接收进程的内部标识符 j,然后将 i 挂在 j.mq 上。由于该 队列属于临界资源,故在执行 insert 操作的前后,都要执行 wait 和 signal 操作。 发送原语可描述如下: procedure send(receiver,a) begin getbuf(a.size,i); i.sender:= a.sender; i.size:=a.size; i.text:=a.text; 根据 a.size 申请缓冲区; 将发送区 a 中的信息复制到消息缓冲区 i 中; i.next:=0; getid(PCB set,receiver.j); 获得接收进程内部标识符; wait(j.mutex); insert(j.mq,i); signal(j.mutex); signal(j.sm); 将消息缓冲区插入消息队列; end 进程A send (B, a) 第二章 进 程 管 理 PCB(B) mq mutex sm 进程B receive (b) ·71· a sender:A 发 size:5 送 区 text:Hello a 第一消息缓冲区 sender:A size:5 b text:Hello next:0 sender:A 接 size:5 收 text:Hello 区 b 图 2-14 消息缓冲通信 3.接收原语 接收进程调用接收原语 receive(b),从自己的消息缓冲队列 mq 中摘下第一个消息缓冲 区 i,并将其中的数据复制到以 b 为首址的指定消息接收区内。接收原语描述如下: procedure receive(b) begin j:= internal name; j 为接收进程内部的标识符; wait(j.sm); wait(j.mutex); remove(j.mq,i); 将消息队列中第一个消息移出; signal(j.mutex); b.sender:=i.sender; 将消息缓冲区 i 中的信息复制到接收区 b; b.size:=i.size; b.text:=i.text; end 2.6 线 程 自从在 20 世纪 60 年代人们提出了进程的概念后,在 OS 中一直都是以进程作为能拥有 资源和独立运行的基本单位的。直到 20 世纪 80 年代中期,人们又提出了比进程更小的能 独立运行的基本单位——线程(Threads),试图用它来提高系统内程序并发执行的程度,从而 可进一步提高系统的吞吐量。特别是在进入 20 世纪 90 年代后,多处理机系统得到迅速发 展,线程能比进程更好地提高程序的并行执行程度,充分地发挥多处理机的优越性,因而 在近几年所推出的多处理机 OS 中也都引入了线程,以改善 OS 的性能。 ·72· 计算机操作系统 2.6.1 线程的基本概念 1.线程的引入 如果说,在操作系统中引入进程的目的,是为了使多个程序能并发执行,以提高资源 利用率和系统吞吐量,那么,在操作系统中再引入线程,则是为了减少程序在并发执行时 所付出的时空开销,使 OS 具有更好的并发性。为了说明这一点,我们首先来回顾进程的两 个基本属性: ① 进程是一个可拥有资源的独立单位;② 进程同时又是一个可独立调度和分 派的基本单位。正是由于进程有这两个基本属性,才使之成为一个能独立运行的基本单位, 从而也就构成了进程并发执行的基础。然而,为使程序能并发执行,系统还必须进行以下 的一系列操作。 1) 创建进程 系统在创建一个进程时,必须为它分配其所必需的、除处理机以外的所有资源,如内 存空间、I/O 设备,以及建立相应的 PCB。 2) 撤消进程 系统在撤消进程时,又必须先对其所占有的资源执行回收操作,然后再撤消 PCB。 3) 进程切换 对进程进行切换时,由于要保留当前进程的 CPU 环境和设置新选中进程的 CPU 环境, 因而须花费不少的处理机时间。 换言之,由于进程是一个资源的拥有者,因而在创建、撤消和切换中,系统必须为之 付出较大的时空开销。正因如此,在系统中所设置的进程,其数目不宜过多,进程切换的 频率也不宜过高,这也就限制了并发程度的进一步提高。 如何能使多个程序更好地并发执行同时又尽量减少系统的开销,已成为近年来设计操 作系统时所追求的重要目标。有不少研究操作系统的学者们想到,若能将进程的上述两个 属性分开,由操作系统分开处理,亦即对于作为调度和分派的基本单位,不同时作为拥有 资源的单位,以做到“轻装上阵”;而对于拥有资源的基本单位,又不对之进行频繁的切换。 正是在这种思想的指导下,形成了线程的概念。 随着 VLSI 技术和计算机体系结构的发展,出现了对称多处理机(SMP)计算机系统。它 为提高计算机的运行速度和系统吞吐量提供了良好的硬件基础。但要使多个 CPU 很好地协 调运行,充分发挥它们的并行处理能力,以提高系统性能,还必须配置性能良好的多处理 机 OS。但利用传统的进程概念和设计方法,已难以设计出适合于 SMP 结构的计算机系统 的 OS。这是因为进程“太重”,致使实现多处理机环境下的进程调度、分派和切换时,都 需花费较大的时间和空间开销。如果在 OS 中引入线程,以线程作为调度和分派的基本单位, 则可以有效地改善多处理机系统的性能。因此,一些主要的 OS(UNIX、OS/2、Windows)厂 家都又进一步对线程技术做了开发,使之适用于 SMP 的计算机系统。 2.线程与进程的比较 线程具有许多传统进程所具有的特征,所以又称为轻型进程(Light-Weight Process)或进 程元,相应地把传统进程称为重型进程(Heavy-Weight Process),传统进程相当于只有一个线 程的任务。在引入了线程的操作系统中,通常一个进程都拥有若干个线程,至少也有一个 第二章 进 程 管 理 ·73· 线程。下面我们从调度性、并发性、系统开销和拥有资源等方面对线程和进程进行比较。 1) 调度 在传统的操作系统中,作为拥有资源的基本单位和独立调度、分派的基本单位都是进 程。而在引入线程的操作系统中,则把线程作为调度和分派的基本单位,而进程作为资源 拥有的基本单位,把传统进程的两个属性分开,使线程基本上不拥有资源,这样线程便能 轻装前进,从而可显著地提高系统的并发程度。在同一进程中,线程的切换不会引起进程 的切换,但从一个进程中的线程切换到另一个进程中的线程时,将会引起进程的切换。 2) 并发性 在引入线程的操作系统中,不仅进程之间可以并发执行,而且在一个进程中的多个线 程之间亦可并发执行,使得操作系统具有更好的并发性,从而能更加有效地提高系统资源 的利用率和系统的吞吐量。例如,在一个未引入线程的单 CPU 操作系统中,若仅设置一个 文件服务进程,当该进程由于某种原因而被阻塞时,便没有其它的文件服务进程来提供服 务。在引入线程的操作系统中,则可以在一个文件服务进程中设置多个服务线程。当第一 个线程等待时,文件服务进程中的第二个线程可以继续运行,以提供文件服务;当第二个 线程阻塞时,则可由第三个继续执行,提供服务。显然,这样的方法可以显著地提高文件 服务的质量和系统的吞吐量。 3) 拥有资源 不论是传统的操作系统,还是引入了线程的操作系统,进程都可以拥有资源,是系统 中拥有资源的一个基本单位。一般而言,线程自己不拥有系统资源(也有一点必不可少的资 源),但它可以访问其隶属进程的资源,即一个进程的代码段、数据段及所拥有的系统资源, 如已打开的文件、I/O 设备等,可以供该进程中的所有线程所共享。 4) 系统开销 在创建或撤消进程时,系统都要为之创建和回收进程控制块,分配或回收资源,如内 存空间和 I/O 设备等,操作系统所付出的开销明显大于线程创建或撤消时的开销。类似地, 在进程切换时,涉及到当前进程 CPU 环境的保存及新被调度运行进程的 CPU 环境的设置, 而线程的切换则仅需保存和设置少量寄存器内容,不涉及存储器管理方面的操作,所以就 切换代价而言,进程也是远高于线程的。此外,由于一个进程中的多个线程具有相同的地 址空间,在同步和通信的实现方面线程也比进程容易。在一些操作系统中,线程的切换、 同步和通信都无须操作系统内核的干预。 3.线程的属性 在多线程 OS 中,通常是在一个进程中包括多个线程,每个线程都是作为利用 CPU 的 基本单位,是花费最小开销的实体。线程具有下述属性。 (1) 轻型实体。线程中的实体基本上不拥有系统资源,只是有一点必不可少的、 能保 证其独立运行的资源,比如,在每个线程中都应具有一个用于控制线程运行的线程控制块 TCB,用于指示被执行指令序列的程序计数器,保留局部变量、少数状态参数和返回地址 等的一组寄存器和堆栈。 (2) 独立调度和分派的基本单位。在多线程 OS 中,线程是能独立运行的基本单位,因 而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小。 ·74· 计算机操作系统 (3) 可并发执行。在一个进程中的多个线程之间可以并发执行,甚至允许在一个进程中 的所有线程都能并发执行;同样,不同进程中的线程也能并发执行。 (4) 共享进程资源。在同一进程中的各个线程都可以共享该进程所拥有的资源,这首先 表现在所有线程都具有相同的地址空间(进程的地址空间)。这意味着线程可以访问该地址空 间中的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构 等。 4.线程的状态 (1) 状态参数。在 OS 中的每一个线程都可以利用线程标识符和一组状态参数进行描述。 状态参数通常有这样几项:① 寄存器状态,它包括程序计数器 PC 和堆栈指针中的内容; ② 堆栈,在堆栈中通常保存有局部变量和返回地址;③ 线程运行状态,用于描述线程正 处于何种运行状态;④ 优先级,描述线程执行的优先程度;⑤ 线程专有存储器,用于保 存线程自己的局部变量拷贝;⑥ 信号屏蔽,即对某些信号加以屏蔽。 (2) 线程运行状态。如同传统的进程一样,在各线程之间也存在着共享资源和相互合作 的制约关系,致使线程在运行时也具有间断性。相应地,线程在运行时也具有下述三种基 本状态: ① 执行状态,表示线程正获得处理机而运行;② 就绪状态,指线程已具备了各种 执行条件,一旦获得 CPU 便可执行的状态;③ 阻塞状态,指线程在执行中因某事件而受 阻,处于暂停执行时的状态。 5.线程的创建和终止 在多线程 OS 环境下,应用程序在启动时,通常仅有一个线程在执行,该线程被人们称 为“初始化线程”。它可根据需要再去创建若干个线程。在创建新线程时,需要利用一个线 程创建函数(或系统调用),并提供相应的参数,如指向线程主程序的入口指针、堆栈的大 小,以及用于调度的优先级等。在线程创建函数执行完后,将返回一个线程标识符供以后 使用。 如同进程一样,线程也是具有生命期的。终止线程的方式有两种:一种是在线程完成 了自己的工作后自愿退出;另一种是线程在运行中出现错误或由于某种原因而被其它线程 强行终止。但有些线程(主要是系统线程),在它们一旦被建立起来之后,便一直运行下去而 不再被终止。在大多数的 OS 中,线程被中止后并不立即释放它所占有的资源,只有当进程 中的其它线程执行了分离函数后,被终止的线程才与资源分离,此时的资源才能被其它线 程利用。 虽已被终止但尚未释放资源的线程,仍可以被需要它的线程所调用,以使被终止线程 重新恢复运行。为此,调用者线程须调用一条被称为“等待线程终止”的连接命令,来与 该线程进行连接。如果在一个调用者线程调用“等待线程终止”的连接命令试图与指定线 程相连接时,若指定线程尚未被终止,则调用连接命令的线程将会阻塞,直至指定线程被 终止后才能实现它与调用者线程的连接并继续执行;若指定线程已被终止,则调用者线程 不会被阻塞而是继续执行。 6.多线程 OS 中的进程 在多线程 OS 中,进程是作为拥有系统资源的基本单位,通常的进程都包含多个线程并 为它们提供资源,但此时的进程就不再作为一个执行的实体。多线程 OS 中的进程有以下 第二章 进 程 管 理 ·75· 属性: (1) 作为系统资源分配的单位。在多线程 OS 中,仍是将进程作为系统资源分配的基本 单位,在任一进程中所拥有的资源包括受到分别保护的用户地址空间、用于实现进程间和 线程间同步和通信的机制、已打开的文件和已申请到的 I/O 设备,以及一张由核心进程维护 的地址映射表,该表用于实现用户程序的逻辑地址到其内存物理地址的映射。 (2) 可包括多个线程。通常,一个进程都含有多个相对独立的线程,其数目可多可少, 但至少也要有一个线程,由进程为这些(个)线程提供资源及运行环境,使这些线程可并发执 行。在 OS 中的所有线程都只能属于某一个特定进程。 (3) 进程不是一个可执行的实体。在多线程 OS 中,是把线程作为独立运行的基本单位, 所以此时的进程已不再是一个可执行的实体。虽然如此,进程仍具有与执行相关的状态。 例如,所谓进程处于“执行”状态,实际上是指该进程中的某线程正在执行。此外,对进 程所施加的与进程状态有关的操作,也对其线程起作用。例如,在把某个进程挂起时,该 进程中的所有线程也都将被挂起;又如,在把某进程激活时,属于该进程的所有线程也都 将被激活。 2.6.2 线程间的同步和通信 为使系统中的多线程能有条不紊地运行,在系统中必须提供用于实现线程间同步和通 信的机制。为了支持不同频率的交互操作和不同程度的并行性,在多线程 OS 中通常提供多 种同步机制,如互斥锁、条件变量、计数信号量以及多读、单写锁等。 1.互斥锁(mutex) 互斥锁是一种比较简单的、用于实现线程间对资源互斥访问的机制。由于操作互斥锁 的时间和空间开销都较低,因而较适合于高频度使用的关键共享数据和程序段。互斥锁可 以有两种状态,即开锁(unlock)和关锁(lock)状态。相应地,可用两条命令(函数)对互斥锁进 行操作。其中的关锁 lock 操作用于将 mutex 关上,开锁操作 unlock 则用于打开 mutex。 当一个线程需要读/写一个共享数据段时,线程首先应为该数据段所设置的 mutex 执行 关锁命令。命令首先判别 mutex 的状态,如果它已处于关锁状态,则试图访问该数据段的 线程将被阻塞;而如果 mutex 是处于开锁状态,则将 mutex 关上后便去读/写该数据段。在 线程完成对数据的读/写后,必须再发出开锁命令将 mutex 打开,同时还须将阻塞在该互斥 锁上的一个线程唤醒,其它的线程仍被阻塞在等待 mutex 打开的队列上。 另外,为了减少线程被阻塞的机会,在有的系统中还提供了一种用于 mutex 上的操作 命令 Trylock。当一个线程在利用 Trylock 命令去访问 mutex 时,若 mutex 处于开锁状态, Trylock 将返回一个指示成功的状态码;反之,若 mutex 处于关锁状态,则 Trylock 并不会 阻塞该线程,而只是返回一个指示操作失败的状态码。 2.条件变量 在许多情况下,只利用 mutex 来实现互斥访问可能会引起死锁,我们通过一个例子来 说明这一点。有一个线程在对 mutex 1 执行关锁操作成功后,便进入一临界区 C,若在临界 区内该线程又须访问某个临界资源 R,同样也为 R 设置另一互斥锁 mutex 2。假如资源 R 此 时正处于忙碌状态,线程在对 mutex 2 执行关锁操作后必将被阻塞,这样将使 mutex 1 一直 ·76· 计算机操作系统 保持关锁状态;如果保持了资源 R 的线程也要求进入临界区 C,但由于 mutex 1 一直保持关 锁状态而无法进入临界区,这样便形成了死锁。为了解决这个问题便引入了条件变量。 每一个条件变量通常都与一个互斥锁一起使用,亦即,在创建一个互斥锁时便联系着 一个条件变量。单纯的互斥锁用于短期锁定,主要是用来保证对临界区的互斥进入。而条 件变量则用于线程的长期等待,直至所等待的资源成为可用的资源。 现在,我们看看如何利用互斥锁和条件变量来实现对资源 R 的访问。线程首先对 mutex 执行关锁操作,若成功便进入临界区,然后查找用于描述该资源状态的数据结构,以了解 资源的情况。只要发现所需资源 R 正处于忙碌状态,线程便转为等待状态,并对 mutex 执 行开锁操作后,等待该资源被释放;若资源处于空闲状态,表明线程可以使用该资源,于 是将该资源设置为忙碌状态,再对 mutex 执行开锁操作。下面给出了对上述资源的申请(左 半部分)和释放(右半部分)操作的描述。 Lock mutex Lock mutex check data structures; mark resource as free; while(resource busy); unlock mutex; wait(condition variable); wakeup(condition variable); mark resource as busy; unlock mutex; 原来占有资源 R 的线程在使用完该资源后,便按照右半部分的描述释放该资源,其中 的 wakeup(condition variable)表示去唤醒在指定条件变量上等待的一个或多个线程。在大多 数情况下,由于所释放的是临界资源,此时所唤醒的只能是在条件变量上等待的某一个线 程,其它线程仍继续在该队列上等待。但如果线程所释放的是一个数据文件,该文件允许 多个线程同时对它执行读操作。在这种情况下,当一个写线程完成写操作并释放该文件后, 如果此时在该条件变量上还有多个读线程在等待,则该线程可以唤醒所有的等待线程。 3.信号量机制 前面所介绍的用于实现进程同步的最常用工具——信号量机制,也可用于多线程 OS 中,实现诸线程或进程之间的同步。为了提高效率,可为线程和进程分别设置相应的信 号量。 1) 私用信号量(private samephore) 当某线程需利用信号量来实现同一进程中各线程之间的同步时,可调用创建信号量的 命令来创建一私用信号量,其数据结构存放在应用程序的地址空间中。私用信号量属于特 定的进程所有,OS 并不知道私用信号量的存在,因此,一旦发生私用信号量的占用者异常 结束或正常结束,但并未释放该信号量所占有空间的情况时,系统将无法使它恢复为 0(空), 也不能将它传送给下一个请求它的线程。 2) 公用信号量(public semaphort) 公用信号量是为实现不同进程间或不同进程中各线程之间的同步而设置的。由于它有 着一个公开的名字供所有的进程使用,故而把它称为公用信号量。其数据结构是存放在受 保护的系统存储区中,由 OS 为它分配空间并进行管理,故也称为系统信号量。如果信号量 的占有者在结束时未释放该公用信号量,则 OS 会自动将该信号量空间回收,并通知下一进 第二章 进 程 管 理 ·77· 程。可见,公用信号量是一种比较安全的同步机制。 2.6.3 线程的实现方式 线程已在许多系统中实现,但各系统的实现方式并不完全相同。在有的系统中,特别 是一些数据库管理系统如 Infomix,所实现的是用户级线程(UserLevel Threads);而另一些系 统(如 Macintosh 和 OS/2 操作系统)所实现的是内核支持线程(KernelSupported Threads); 还 有一些系统如 Solaris 操作系统,则同时实现了这两种类型的线程。 1.内核支持线程 对于通常的进程,无论是系统进程还是用户进程,进程的创建、 撤消,以及要求由系 统设备完成的 I/O 操作,都是利用系统调用而进入内核,再由内核中的相应处理程序予以完 成的。进程的切换同样是在内核的支持下实现的。因此我们说,不论什么进程,它们都是 在操作系统内核的支持下运行的,是与内核紧密相关的。 这里所谓的内核支持线程 KST(Kernel Supported Threads),也都同样是在内核的支持下 运行的,即无论是用户进程中的线程,还是系统进程中的线程,他们的创建、撤消和切换 等也是依靠内核,在内核空间实现的。此外,在内核空间还为每一个内核支持线程设置了 一个线程控制块,内核是根据该控制块而感知某线程的存在,并对其加以控制。 这种线程实现方式主要有如下四个优点: (1) 在多处理器系统中,内核能够同时调度同一进程中多个线程并行执行; (2) 如果进程中的一个线程被阻塞了,内核可以调度该进程中的其它线程占有处理器运 行,也可以运行其它进程中的线程; (3) 内核支持线程具有很小的数据结构和堆栈,线程的切换比较快,切换开销小; (4) 内核本身也可以采用多线程技术,可以提高系统的执行速度和效率。 内核支持线程的主要缺点是:对于用户的线程切换而言,其模式切换的开销较大,在 同一个进程中,从一个线程切换到另一个线程时,需要从用户态转到内核态进行,这是因 为用户进程的线程在用户态运行,而线程调度和管理是在内核实现的,系统开销较大。 2.用户级线程 用户级线程 ULT(User Level Threads)仅存在于用户空间中。对于这种线程的创建、撤 消、线程之间的同步与通信等功能,都无须利用系统调用来实现。对于用户级线程的切换, 通常发生在一个应用进程的诸多线程之间,这时,也同样无须内核的支持。由于切换的规 则远比进程调度和切换的规则简单,因而使线程的切换速度特别快。可见,这种线程是与 内核无关的。我们可以为一个应用程序建立多个用户级线程。在一个系统中的用户级线程 的数目可以达到数百个至数千个。由于这些线程的任务控制块都是设置在用户空间,而线 程所执行的操作也无须内核的帮助,因而内核完全不知道用户级线程的存在。 值得说明的是,对于设置了用户级线程的系统,其调度仍是以进程为单位进行的。在 采用轮转调度算法时,各个进程轮流执行一个时间片,这对诸进程而言似乎是公平的。但 假如在进程 A 中包含了一个用户级线程,而在另一个进程 B 中含有 100 个用户级线程,这 样,进程 A 中线程的运行时间将是进程 B 中各线程运行时间的 100 倍;相应地,其速度要 快上 100 倍。 ·78· 计算机操作系统 假如系统中设置的是内核支持线程,则调度便是以线程为单位进行的。在采用轮转法 调度时,是各个线程轮流执行一个时间片。同样假定进程 A 中只有一个内核支持线程,而 在进程 B 中有 100 个内核支持线程。此时进程 B 可以获得的 CPU 时间是进程 A 的 100 倍, 且进程 B 可使 100 个系统调用并发工作。 使用用户级线程方式有许多优点,主要表现在如下三个方面: (1) 线程切换不需要转换到内核空间,对一个进程而言,其所有线程的管理数据结构均 在该进程的用户空间中,管理线程切换的线程库也在用户地址空间运行。因此,进程不必 切换到内核方式来做线程管理,从而节省了模式切换的开销,也节省了内核的宝贵资源。 (2) 调度算法可以是进程专用的。在不干扰操作系统调度的情况下,不同的进程可以根 据自身需要,选择不同的调度算法对自己的线程进行管理和调度,而与操作系统的低级调 度算法是无关的。 (3) 用户级线程的实现与操作系统平台无关,因为对于线程管理的代码是在用户程序内 的,属于用户程序的一部分,所有的应用程序都可以对之进行共享。因此,用户级线程甚 至可以在不支持线程机制的操作系统平台上实现。 用户级线程实现方式的主要缺点在于如下两个方面: (1) 系统调用的阻塞问题。在基于进程机制的操作系统中,大多数系统调用将阻塞进程, 因此,当线程执行一个系统调用时,不仅该线程被阻塞,而且进程内的所有线程都会被阻 塞。而在内核支持线程方式中,则进程中的其它线程仍然可以运行。 (2) 在单纯的用户级线程实现方式中,多线程应用不能利用多处理机进行多重处理的优 点。内核每次分配给一个进程的仅有一个 CPU,因此进程中仅有一个线程能执行,在该线 程放弃 CPU 之前,其它线程只能等待。 3.组合方式 有些操作系统把用户级线程和内核支持线程两种方式进行组合,提供了组合方式 ULT/KST 线程。在组合方式线程系统中,内核支持多 KST 线程的建立、调度和管理,同时, 也允许用户应用程序建立、调度和管理用户级线程。一些内核支持线程对应多个用户级线 程,程序员可按应用需要和机器配置对内核支持线程数目进行调整,以达到较好的效果。 组合方式线程中,同一个进程内的多个线程可以同时在多处理器上并行执行,而且在阻塞 一个线程时,并不需要将整个进程阻塞。所以,组合方式多线程机制能够结合 KST 和 ULT 两者的优点,并克服了其各自的不足。 2.6.4 线程的实现 不论是进程还是线程,都必须直接或间接地取得内核的支持。由于内核支持线程可以 直接利用系统调用为它服务,故线程的控制相当简单;而用户级线程必须借助于某种形式 的中间系统的帮助方能取得内核的服务,故在对线程的控制上要稍复杂些。 1.内核支持线程的实现 在仅设置了内核支持线程的 OS 中,一种可能的线程控制方法是,系统在创建一个新进 程时,便为它分配一个任务数据区 PTDA(Per Task Data Area),其中包括若干个线程控制块 TCB 空间,如图 2-15 所示。在每一个 TCB 中可保存线程标识符、优先级、线程运行的 CPU 第二章 进 程 管 理 ·79· 状态等信息。虽然这些信息与用户级线程 TCB 中的信息 相同,但现在却是被保存在内核空间中。 每当进程要创建一个线程时,便为新线程分配一个 TCB,将有关信息填入该 TCB 中,并为之分配必要的资 PTDA 进程资源 TCB #1 TCB #2 源,如为线程分配数百至数千个字节的栈空间和局部存 TCB #3 储区,于是新创建的线程便有条件立即执行。当 PTDA 中的所有 TCB 空间已用完,而进程又要创建新的线程 图 2-15 任务数据区空间 时,只要其所创建的线程数目未超过系统的允许值(通常为数十至数百个),系统可再为之分 配新的 TCB 空间;在撤消一个线程时,也应回收该线程的所有资源和 TCB。可见,内核支 持线程的创建、 撤消均与进程的相类似。在有的系统中为了减少创建和撤消一个线程时的 开销,在撤消一个线程时,并不立即回收该线程的资源和 TCB,当以后再要创建一个新线 程时,便可直接利用已被撤消但仍保持有资源和 TCB 的线程作为新线程。 内核支持线程的调度和切换与进程的调度和切换十分相似,也分抢占式方式和非抢占 方式两种。在线程的调度算法上,同样可采用时间片轮转法、优先权算法等。当线程调度 选中一个线程后,便将处理机分配给它。当然,线程在调度和切换上所花费的开销,要比 进程的小得多。 2.用户级线程的实现 用户级线程是在用户空间实现的。所有的用户级线程都具有相同的结构,它们都运行 在一个中间系统的上面。当前有两种方式实现的中间系统,即运行时系统和内核控制线程。 1) 运行时系统(Runtime System) 所谓“运行时系统”,实质上是用于管理和控制线程的函数(过程)的集合,其中包括用 于创建和撤消线程的函数、 线程同步和通信的函数以及实现线程调度的函数等。正因为有 这些函数,才能使用户级线程与内核无关。运行时系统中的所有函数都驻留在用户空间, 并作为用户级线程与内核之间的接口。 在传统的 OS 中,进程在切换时必须先由用户态转为核心态,再由核心来执行切换任务; 而用户级线程在切换时则不需转入核心态,而是由运行时系统中的线程切换过程来执行切 换任务。该过程将线程的 CPU 状态保存在该线程的堆栈中,然后按照一定的算法选择一个 处于就绪状态的新线程运行,将新线程堆栈中的 CPU 状态装入到 CPU 相应的寄存器中,一 旦将栈指针和程序计数器切换后,便开始了新线程的运行。由于用户级线程的切换无需进 入内核,且切换操作简单,因而使用户级线程的切换速度非常快。 不论在传统的 OS 中,还是在多线程 OS 中,系统资源都是由内核管理的。在传统的 OS 中,进程是利用 OS 提供的系统调用来请求系统资源的,系统调用通过软中断(如 trap) 机制进入 OS 内核,由内核来完成相应资源的分配。用户级线程是不能利用系统调用的。当 线程需要系统资源时,是将该要求传送给运行时系统,由后者通过相应的系统调用来获得 系统资源的。 2) 内核控制线程 这种线程又称为轻型进程 LWP(Light Weight Process)。每一个进程都可拥有多个 LWP, 同用户级线程一样,每个 LWP 都有自己的数据结构(如 TCB),其中包括线程标识符、优先 ·80· 计算机操作系统 级、状态,另外还有栈和局部存储区等。它们也可以共享进程所拥有的资源。LWP 可通过 系统调用来获得内核提供的服务,这样,当一个用户级线程运行时,只要将它连接到一个 LWP 上,此时它便具有了内核支持线程的所有属性。这种线程实现方式就是组合方式。 在一个系统中的用户级线程数量可能很大,为了节省系统开销,不可能设置太多的 LWP,而把这些 LWP 做成一个缓冲池,称为“线程池”。用户进程中的任一用户线程都可 以连接到 LWP 池中的任何一个 LWP 上。为使每一用户级线程都能利用 LWP 与内核通信, 可以使多个用户级线程多路复用一个 LWP,但只有当前连接到 LWP 上的线程才能与内核通 信,其余进程或者阻塞,或者等待 LWP。而每一个 LWP 都要连接到一个内核级线程上,这 样,通过 LWP 可把用户级线程与内核线程连接起来,用户级线程可通过 LWP 来访问内核, 但内核所看到的总是多个 LWP 而看不到用户级线程。亦即,由 LWP 实现了在内核与用户 级线程之间的隔离,从而使用户级线程与内核无关。图 2-16 示出了利用轻型进程作为中间 系统时用户级线程的实现方法。 任务1 任务2 任务3 用户级线程 轻型进程 内核线程 内核 CPU 图 2-16 利用轻型进程作为中间系统 当用户级线程不需要与内核通信时,并不需要 LWP;而当要通信时,便需借助于 LWP, 而且每个要通信的用户级线程都需要一个 LWP。例如,在一个任务中,如果同时有 5 个用 户级线程发出了对文件的读、写请求,这就需要有 5 个 LWP 来予以帮助,即由 LWP 将对 文件的读、写请求发送给相应的内核级线程,再由后者执行具体的读、写操作。如果一个 任务中只有 4 个 LWP,则只能有 4 个用户级线程的读、写请求被传送给内核线程,余下的 一个用户级线程必须等待。 在内核级线程执行操作时,如果发生阻塞,则与之相连接的多个 LWP 也将随之阻塞, 进而使连接到 LWP 上的用户级线程也被阻塞。如果进程中只包含了一个 LWP,此时进程也 应阻塞。这种情况与前述的传统 OS 一样,在进程执行系统调用时,该进程实际上是阻塞的。 但如果在一个进程中含有多个 LWP,则当一个 LWP 阻塞时,进程中的另一个 LWP 可继续 执行;即使进程中的所有 LWP 全部阻塞,进程中的线程也仍然能继续执行,只是不能再去 访问内核。 3.用户级线程与内核控制线程的连接 实际上,在不同的操作系统中,实现用户级线程与内核控制线程的连接有三种不同的 第二章 进 程 管 理 ·81· 模型:一对一模型、多对一模型和多对多模型。 1) 一对一模型 该模型是为每一个用户线程都设置一个内核控制线程与之连接,当一个线程阻塞时, 允许调度另一个线程运行。在多处理机系统中,则有多个线程并行执行。 该模型并行能力较强,但每创建一个用户线程相应地就需要创建一个内核线程,开销 较大,因此需要限制整个系统的线程数。Windows 2000、Windows NT、OS/2 等系统上都实 现了该模型。 2) 多对一模型 该模型是将多个用户线程映射到一个内核控制线程,为了管理方便,这些用户线程一 般属于一个进程,运行在该进程的用户空间,对这些线程的调度和管理也是在该进程的用 户空间中完成。当用户线程需要访问内核时,才将其映射到一个内核控制线程上,但每次 只允许一个线程进行映射。 该模型的主要优点是线程管理的开销小,效率高,但当一个线程在访问内核时发生阻 塞,则整个进程都会被阻塞,而且在多处理机系统中,一个进程的多个线程无法实现并行。 3) 多对多模型 该模型结合上述两种模型的优点,将多个用户线程映射到多个内核控制线程,内核控 制线程的数目可以根据应用进程和系统的不同而变化,可以比用户线程少,也可以与之 相同。 习题 1. 什么是前趋图?为什么要引入前趋图? 2. 试画出下面四条语句的前趋图: S1: a:=x+y; S2: b:=z+1; S3: c:=a-b; S4: w:=c+1; 3. 为什么程序并发执行会产生间断性特征? 4. 程序并发执行时为什么会失去封闭性和可再现性? 5. 在操作系统中为什么要引入进程的概念? 它会产生什么样的影响? 6. 试从动态性、并发性和独立性上比较进程和程序。 7. 试说明 PCB 的作用,为什么说 PCB 是进程存在的惟一标志? 8. 试说明进程在三个基本状态之间转换的典型原因。 9. 为什么要引入挂起状态?该状态有哪些性质? 10. 在进行进程切换时,所要保存的处理机状态信息有哪些? 11. 试说明引起进程创建的主要事件。 12. 试说明引起进程被撤消的主要事件。 13. 在创建一个进程时所要完成的主要工作是什么? ·82· 计算机操作系统 14. 在撤消一个进程时所要完成的主要工作是什么? 15. 试说明引起进程阻塞或被唤醒的主要事件是什么。 16. 进程在运行时存在哪两种形式的制约?并举例说明之。 17. 为什么进程在进入临界区之前应先执行“进入区”代码?而在退出前又要执行“退 出区”代码? 18. 同步机构应遵循哪些基本准则?为什么? 19. 试从物理概念上说明记录型信号量 wait 和 signal。 20. 你认为整型信号量机制是否完全遵循了同步机构的四条准则? 21. 如何利用信号量机制来实现多个进程对临界资源的互斥访问?并举例说明之。 22. 试写出相应的程序来描述图 2-17 所示的前趋图。 S1 S1 S2 S3 S2 S3 S4 S5 S6 S4 S5 S6 S7 S7 S8 (a) (b) 图 2-17 前趋图 23. 在生产者—消费者问题中,如果缺少了 signal(full)或 signal(empty),对执行结果将 会有何影响? 24. 在生产者—消费者问题中,如果将两个 wait 操作即 wait(full)和 wait(mutex)互换位 置,或者将 signal(mutex)与 signal(full)互换位置,结果会如何? 25. 我们为某临界资源设置一把锁 W,当 W=1 时表示关锁;当 W=0 时表示锁已打开。 试写出开锁和关锁原语,并利用它们去实现互斥。 26. 试修改下面生产者—消费者问题解法中的错误: producer: consumer: begin begin repeat repeat M wait(mutex); produce an item in nextp; wait(empty); wait(mutex); nextc:=buffer(out); wait(full); out:=out+1; buffer(in):=nextp; signal(mutex); signal(mutex); consume item in nextc; until false; until false; end end 第二章 进 程 管 理 ·83· 27. 试利用记录型信号量写出一个不会出现死锁的哲学家进餐问题的算法。 28. 在测量控制系统中的数据采集任务时,把所采集的数据送往一单缓冲区;计算任务 从该单缓冲区中取出数据进行计算。试写出利用信号量机制实现两任务共享单缓冲区的同 步算法。 29. 画图说明管程由哪几部分组成,为什么要引入条件变量? 30. 如何利用管程来解决生产者—消费者问题? 31. 什么是 AND 信号量?试利用 AND 信号量写出生产者—消费者问题的解法。 32. 什么是信号量集?试利用信号量集写出读者—写者问题的解法。 33. 试比较进程间的低级与高级通信工具。 34. 当前有哪几种高级通信机制? 35. 消息队列通信机制有哪几方面的功能? 36. 为什么要在 OS 中引入线程? 37. 试说明线程具有哪些属性? 38. 试从调度性、并发性、拥有资源及系统开销方面对进程和线程进行比较。 39. 为了在多线程 OS 中实现进程之间的同步与通信,通常提供了哪几种同步机制? 40. 用于实现线程同步的私用信号量和公用信号量之间有何差异? 41. 何谓用户级线程和内核支持线程? 42. 试说明用户级线程的实现方法。 43. 试说明内核支持线程的实现方法。 ·84· 计算机操作系统 第三章 处理机调度与死锁 在多道程序环境下,主存中有着多个进程,其数目往往多于处理机数目。这就要求系 统能按某种算法,动态地把处理机分配给就绪队列中的一个进程,使之执行。分配处理机 的任务是由处理机调度程序完成的。由于处理机是最重要的计算机资源,提高处理机的利 用率及改善系统性能(吞吐量、响应时间),在很大程度上取决于处理机调度性能的好坏,因 而,处理机调度便成为操作系统设计的中心问题之一。为此,本章将对处理机调度作较详 细的阐述。 3.1 处理机调度的层次 在多道程序系统中,一个作业被提交后必须经过处理机调度后,方能获得处理机执行。 对于批量型作业而言,通常需要经历作业调度(又称高级调度或长程调度)和进程调度(又称 低级调度或短程调度)两个过程后方能获得处理机;对于终端型作业,则通常只需经过进程 调度即可获得处理机。在较完善的操作系统中,为提高内存的利用率,往往还设置了中级 调度(又称中程调度)。对于上述的每一级调度,又都可采用不同的调度方式和调度算法。对 于一个批处理型作业,从进入系统并驻留在外存的后备队列开始,直至作业运行完毕,可 能要经历上述的三级调度。本节主要是对处理机调度层次做较详细的介绍。 3.1.1 高级调度 高级调度(High Level Scheduling)又称为作业调度或长程调度(LongTerm Scheduling),其 主要功能是根据某种算法,把外存上处于后备队列中的那些作业调入内存,也就是说,它 的调度对象是作业。为此,我们先对作业的基本概念作简单介绍。 1.作业和作业步 (1) 作业(Job)。作业是一个比程序更为广泛的概念,它不仅包含了通常的程序和数据, 而且还应配有一份作业说明书,系统根据该说明书来对程序的运行进行控制。在批处理系 统中,是以作业为基本单位从外存调入内存的。 (2) 作业步(Job Step)。通常,在作业运行期间,每个作业都必须经过若干个相对独立, 又相互关联的顺序加工步骤才能得到结果,我们把其中的每一个加工步骤称为一个作业步, 各作业步之间存在着相互联系,往往是把上一个作业步的输出作为下一个作业步的输入。 例如,一个典型的作业可分成三个作业步:① “编译”作业步,通过执行编译程序对源程 序进行编译,产生若干个目标程序段;② “连结装配”作业步,将“编译”作业步所产生 的若干个目标程序段装配成可执行的目标程序;③ “运行”作业步,将可执行的目标程序 读入内存并控制其运行。 第三章 处理机调度与死锁 ·85· (3) 作业流。若干个作业进入系统后,被依次存放在外存上,这便形成了输入的作业流; 在操作系统的控制下,逐个作业进行处理,于是便形成了处理作业流。 2.作业控制块 JCB(Job Control Block) 为了管理和调度作业,在多道批处理系统中为每个作业设置了一个作业控制块,如同 进程控制块是进程在系统中存在的标志一样,它是作业在系统中存在的标志,其中保存了 系统对作业进行管理和调度所需的全部信息。在 JCB 中所包含的内容因系统而异,通常应 包含的内容有:作业标识、用户名称、用户帐户、作业类型(CPU 繁忙型、I/O 繁忙型、批 量型、终端型)、作业状态、调度信息(优先级、作业已运行时间)、资源需求(预计运行时间、 要求内存大小、要求 I/O 设备的类型和数量等)、进入系统时间、开始处理时间、作业完成 时间、作业退出时间、资源使用情况等。 每当作业进入系统时,系统便为每个作业建立一个 JCB,根据作业类型将它插入相应 的后备队列中。作业调度程序依据一定的调度算法来调度它们,被调度到的作业将会装入 内存。在作业运行期间,系统就按照 JCB 中的信息对作业进行控制。当一个作业执行结束 进入完成状态时,系统负责回收分配给它的资源,撤消它的作业控制块。 3.作业调度 作业调度的主要功能是根据作业控制块中的信息,审查系统能否满足用户作业的资源 需求,以及按照一定的算法,从外存的后备队列中选取某些作业调入内存,并为它们创建 进程、分配必要的资源。然后再将新创建的进程插入就绪队列,准备执行。因此,有时也 把作业调度称为接纳调度(Admission Scheduling)。 对用户而言,总希望自己作业的周转时间尽可能的少,最好周转时间就等于作业的执 行时间。然而对系统来说,则希望作业的平均周转时间尽可能少,有利于提高 CPU 的利用 率和系统的吞吐量。为此,每个系统在选择作业调度算法时,既应考虑用户的要求,又能 确保系统具有较高的效率。在每次执行作业调度时,都须做出以下两个决定。 1) 决定接纳多少个作业 作业调度每次要接纳多少个作业进入内存,取决于多道程序度(Degree of Multiprogramming), 即允许多少个作业同时在内存中运行。当内存中同时运行的作业数目太多时,可能会影响 到系统的服务质量,比如,使周转时间太长。但如果在内存中同时运行作业的数量太少时, 又会导致系统的资源利用率和系统吞吐量太低,因此,多道程序度的确定应根据系统的规 模和运行速度等情况做适当的折衷。 2) 决定接纳哪些作业 应将哪些作业从外存调入内存,这将取决于所采用的调度算法。最简单的是先来先服 务调度算法,这是指将最早进入外存的作业最先调入内存;较常用的一种算法是短作业优 先调度算法,是将外存上最短的作业最先调入内存;另一种较常用的是基于作业优先级的 调度算法,该算法是将外存上优先级最高的作业优先调入内存;比较好的一种算法是“响 应比高者优先”的调度算法。我们将在后面对上述几种算法作较为详细的介绍。 在批处理系统中,作业进入系统后,总是先驻留在外存的后备队列上,因此需要有作 业调度的过程,以便将它们分批地装入内存。然而在分时系统中,为了做到及时响应,用 户通过键盘输入的命令或数据等都是被直接送入内存的,因而无需再配置上述的作业调度 ·86· 计算机操作系统 机制,但也需要有某些限制性措施来限制进入系统的用户数。即,如果系统尚未饱和,将 接纳所有授权用户,否则,将拒绝接纳。类似地,在实时系统中通常也不需要作业调度。 3.1.2 低级调度 通 常 也 把 低 级 调 度 (Low Level Scheduling) 称 为 进 程 调 度 或 短 程 调 度 (ShortTerm Scheduling),它所调度的对象是进程(或内核级线程)。进程调度是最基本的一种调度,在多 道批处理、分时和实时三种类型的 OS 中,都必须配置这级调度。 1.低级调度的功能 低级调度用于决定就绪队列中的哪个进程(或内核级线程,为叙述方便,以后只写进程) 应获得处理机,然后再由分派程序执行把处理机分配给该进程的具体操作。 低级调度的主要功能如下: (1) 保存处理机的现场信息。在进程调度进行调度时,首先需要保存当前进程的处理机 的现场信息,如程序计数器、多个通用寄存器中的内容等,将它们送入该进程的进程控制 块(PCB)中的相应单元。 (2) 按某种算法选取进程。低级调度程序按某种算法如优先数算法、轮转法等,从就绪 队列中选取一个进程,把它的状态改为运行状态,并准备把处理机分配给它。 (3) 把处理器分配给进程。由分派程序(Dispatcher)把处理器分配给进程。此时需为选中 的进程恢复处理机现场,即把选中进程的进程控制块内有关处理机现场的信息装入处理器 相应的各个寄存器中,把处理器的控制权交给该进程,让它从取出的断点处开始继续运行。 2.进程调度中的三个基本机制 为了实现进程调度,应具有如下三个基本机制: (1) 排队器。为了提高进程调度的效率,应事先将系统中所有的就绪进程按照一定的方 式排成一个或多个队列,以便调度程序能最快地找到它。 (2) 分派器(分派程序)。分派器把由进程调度程序所选定的进程,从就绪队列中取出该 进程,然后进行上下文切换,将处理机分配给它。 (3) 上下文切换机制。当对处理机进行切换时,会发生两对上下文切换操作。在第一对 上下文切换时,操作系统将保存当前进程的上下文,而装入分派程序的上下文,以便分派 程序运行;在第二对上下文切换时,将移出分派程序,而把新选进程的 CPU 现场信息装入 到处理机的各个相应寄存器中。 应当指出,上下文切换将花去不少的处理机时间,即使是现代计算机,每一次上下文 切换大约需要花费几毫秒的时间,该时间大约可执行上千条指令。为此,现在已有通过硬 件(采用两组或多组寄存器)的方法来减少上下文切换的时间。一组寄存器供处理机在系统态 时使用,另一组寄存器供应用程序使用。在这种条件下的上下文切换只需改变指针,使其 指向当前寄存器组即可。 3.进程调度方式 进程调度可采用下述两种调度方式。 1) 非抢占方式(Nonpreemptive Mode) 在采用这种调度方式时,一旦把处理机分配给某进程后,不管它要运行多长时间,都 第三章 处理机调度与死锁 ·87· 一直让它运行下去,决不会因为时钟中断等原因而抢占正在运行进程的处理机,也不允许 其它进程抢占已经分配给它的处理机。直至该进程完成,自愿释放处理机,或发生某事件 而被阻塞时,才再把处理机分配给其他进程。 在采用非抢占调度方式时,可能引起进程调度的因素可归结为如下几个: (1) 正在执行的进程执行完毕,或因发生某事件而不能再继续执行; (2) 执行中的进程因提出 I/O 请求而暂停执行; (3) 在进程通信或同步过程中执行了某种原语操作,如 P 操作(wait 操作)、Block 原语、 Wakeup 原语等。 这种调度方式的优点是实现简单,系统开销小,适用于大多数的批处理系统环境。但 它难以满足紧急任务的要求——立即执行,因而可能造成难以预料的后果。显然,在要求 比较严格的实时系统中,不宜采用这种调度方式。 2) 抢占方式(Preemptive Mode) 这种调度方式允许调度程序根据某种原则去暂停某个正在执行的进程,将已分配给该 进程的处理机重新分配给另一进程。抢占方式的优点是,可以防止一个长进程长时间占用 处理机,能为大多数进程提供更公平的服务,特别是能满足对响应时间有着较严格要求的 实时任务的需求。但抢占方式比非抢占方式调度所需付出的开销较大。抢占调度方式是基 于一定原则的,主要有如下几条: (1) 优先权原则。通常是对一些重要的和紧急的作业赋予较高的优先权。当这种作业到 达时,如果其优先权比正在执行进程的优先权高,便停止正在执行(当前)的进程,将处理机 分配给优先权高的新到的进程,使之执行;或者说,允许优先权高的新到进程抢占当前进 程的处理机。 (2) 短作业(进程)优先原则。当新到达的作业(进程)比正在执行的作业(进程)明显的短 时,将暂停当前长作业(进程)的执行,将处理机分配给新到的短作业(进程),使之优先执行; 或者说,短作业(进程)可以抢占当前较长作业(进程)的处理机。 (3) 时间片原则。各进程按时间片轮流运行,当一个时间片用完后,便停止该进程的执 行而重新进行调度。这种原则适用于分时系统、大多数的实时系统,以及要求较高的批处 理系统。 3.1.3 中级调度 中级调度(Intermediate Level Scheduling)又称中程调度(Medium-Term Scheduling)。引入 中级调度的主要目的是为了提高内存利用率和系统吞吐量。为此,应使那些暂时不能运行 的进程不再占用宝贵的内存资源,而将它们调至外存上去等待,把此时的进程状态称为就 绪驻外存状态或挂起状态。当这些进程重又具备运行条件且内存又稍有空闲时,由中级调 度来决定把外存上的那些又具备运行条件的就绪进程重新调入内存,并修改其状态为就绪 状态,挂在就绪队列上等待进程调度。中级调度实际上就是存储器管理中的对换功能,我 们将在第四章中做详细阐述。 在上述三种调度中,进程调度的运行频率最高,在分时系统中通常是 10~100 ms 便进 行一次进程调度,因此把它称为短程调度。为避免进程调度占用太多的 CPU 时间,进程调 度算法不宜太复杂。作业调度往往是发生在一个(批)作业运行完毕,退出系统,而需要重新 ·88· 计算机操作系统 调入一个(批)作业进入内存时,故作业调度的周期较长,大约几分钟一次,因此把它称为长 程调度。由于其运行频率较低,故允许作业调度算法花费较多的时间。中级调度的运行频 率基本上介于上述两种调度之间,因此把它称为中程调度。 3.2 调度队列模型和调度准则 3.2.1 调度队列模型 前面所介绍的高级调度、低级调度以及中级调度,都将涉及到作业或进程的队列,由 此可以形成如下三种类型的调度队列模型。 1.仅有进程调度的调度队列模型 在分时系统中,通常仅设置了进程调度,用户键入的命令和数据都直接送入内存。对 于命令,是由 OS 为之建立一个进程。系统可以把处于就绪状态的进程组织成栈、树或一个 无序链表,至于到底采用其中哪种形式,则与 OS 类型和所采用的调度算法有关。例如,在 分时系统中,常把就绪进程组织成 FIFO 队列形式。每当 OS 创建一个新进程时,便将它挂 在就绪队列的末尾,然后按时间片轮转方式运行。 每个进程在执行时都可能出现以下三种情况: (1) 任务在给定的时间片内已经完成,该进程便在释放处理机后进入完成状态; (2) 任务在本次分得的时间片内尚未完成,OS 便将该任务再放入就绪队列的末尾; (3) 在执行期间,进程因为某事件而被阻塞后,被 OS 放入阻塞队列。 图 3-1 示出了仅具有进程调度的调度队列模型。 时间片完 交互用户 事 件 出 现 就绪队列 阻塞队列 进程调度 进程完成 CPU 等待事件 图 3-1 仅具有进程调度的调度队列模型 2.具有高级和低级调度的调度队列模型 在批处理系统中,不仅需要进程调度,而且还需有作业调度,由后者按一定的作业调度 算法,从外存的后备队列中选择一批作业调入内存,并为它们建立进程,送入就绪队列,然 后才由进程调度按照一定的进程调度算法选择一个进程,把处理机分配给该进程。图 3-2 示 出了具有高、低两级调度的调度队列模型。该模型与上一模型的主要区别在于如下两个方面。 (1) 就绪队列的形式。在批处理系统中,最常用的是最高优先权优先调度算法,相应地, 最常用的就绪队列形式是优先权队列。进程在进入优先级队列时,根据其优先权的高低, 被插入具有相应优先权的位置上,这样,调度程序总是把处理机分配给就绪队列中的队首 进程。在最高优先权优先的调度算法中,也可采用无序链表方式,即每次把新到的进程挂 第三章 处理机调度与死锁 ·89· 在链尾,而调度程序每次调度时,是依次比较该链中各进程的优先权,从中找出优先权最 高的进程,将之从链中摘下,并把处理机分配给它。显然,无序链表方式与优先权队列相 比,这种方式的调度效率较低。 作业 调度 后备队列 时间片完 就绪队列 进程调度 进程完成 CPU 事件1出现 等待事件1 事件2出现 等待事件2 … … … … 事件n出现 等待事件n 图 3-2 具有高、低两级调度的调度队列模型 (2) 设置多个阻塞队列。对于小型系统,可以只设置一个阻塞队列;但当系统较大时, 若仍只有一个阻塞队列,其长度必然会很长,队列中的进程数可以达到数百个,这将严重 影响对阻塞队列操作的效率。故在大、中型系统中通常都设置了若干个阻塞队列,每个队 列对应于某一种进程阻塞事件。 3.同时具有三级调度的调度队列模型 当在 OS 中引入中级调度后,人们可把进程的就绪状态分为内存就绪(表示进程在内存 中就绪)和外存就绪(进程在外存中就绪)。类似地,也可把阻塞状态进一步分成内存阻塞和 外存阻塞两种状态。在调出操作的作用下,可使进程状态由内存就绪转为外存就绪,由内 存阻塞转为外存阻塞;在中级调度的作用下,又可使外存就绪转为内存就绪。图 3-3 示出了 具有三级调度的调度队列模型。 作业调度 时间片完 后备队列 批量作业 交互型作业 就绪队列 进程调度 进程完成 CPU 中级调度 就绪,挂起队列 事件出现 阻塞,挂起队列 事 件 出 挂起 现 阻塞队列 等待事件 图 3-3 具有三级调度时的调度队列模型 ·90· 计算机操作系统 3.2.2 选择调度方式和调度算法的若干准则 在一个操作系统的设计中,应如何选择调度方式和算法,在很大程度上取决于操作 系统的类型及其目标。例如,在批处理系统、分时系统和实时系统中,通常都采用不同 的调度方式和算法。选择调度方式和算法的准则,有的是面向用户的,有的是面向系 统的。 1.面向用户的准则 这是为了满足用户的需求所应遵循的一些准则。其中,比较重要的有以下几点。 (1) 周转时间短。通常把周转时间的长短作为评价批处理系统的性能、选择作业调度方 式与算法的重要准则之一。所谓周转时间,是指从作业被提交给系统开始,到作业完成为 止的这段时间间隔(称为作业周转时间)。它包括四部分时间:作业在外存后备队列上等待 (作业)调度的时间,进程在就绪队列上等待进程调度的时间,进程在 CPU 上执行的时间, 以及进程等待 I/O 操作完成的时间。其中的后三项在一个作业的整个处理过程中可能会发生 多次。 对每个用户而言,都希望自己作业的周转时间最短。但作为计算机系统的管理者,则 总是希望能使平均周转时间最短,这不仅会有效地提高系统资源的利用率,而且还可使大 多数用户都感到满意。可把平均周转时间描述为: T = 1 n ∑i =n1 Ti   作业的周转时间 T 与系统为它提供服务的时间 Ts 之比,即 W = T/Ts,称为带权周转时 间,而平均带权周转时间则可表示为: W = 1 n n ∑  i=1 Ti Ts    (2) 响应时间快。常把响应时间的长短用来评价分时系统的性能,这是选择分时系统中 进程调度算法的重要准则之一。所谓响应时间,是从用户通过键盘提交一个请求开始,直 至系统首次产生响应为止的时间,或者说,直到屏幕上显示出结果为止的一段时间间隔。 它包括三部分时间:从键盘输入的请求信息传送到处理机的时间,处理机对请求信息进行 处理的时间,以及将所形成的响应信息回送到终端显示器的时间。 (3) 截止时间的保证。这是评价实时系统性能的重要指标,因而是选择实时调度算法的 重要准则。所谓截止时间,是指某任务必须开始执行的最迟时间,或必须完成的最迟时间。 对于严格的实时系统,其调度方式和调度算法必须能保证这一点,否则将可能造成难以预 料的后果。 (4) 优先权准则。在批处理、分时和实时系统中选择调度算法时,都可遵循优先权准则, 以便让某些紧急的作业能得到及时处理。在要求较严格的场合,往往还须选择抢占式调度 方式,才能保证紧急作业得到及时处理。 第三章 处理机调度与死锁 ·91· 2.面向系统的准则 这是为了满足系统要求而应遵循的一些准则。其中,较重要的有以下几点: (1) 系统吞吐量高。这是用于评价批处理系统性能的另一个重要指标,因而是选择批处 理作业调度的重要准则。由于吞吐量是指在单位时间内系统所完成的作业数,因而它与批 处理作业的平均长度具有密切关系。对于大型作业,一般吞吐量约为每小时一道作业;对 于中、小型作业,其吞吐量则可能达到数十道作业之多。作业调度的方式和算法对吞吐量 的大小也将产生较大影响。事实上,对于同一批作业,若采用了较好的调度方式和算法, 则可显著地提高系统的吞吐量。 (2) 处理机利用率好。对于大、中型多用户系统,由于 CPU 价格十分昂贵,致使处理 机的利用率成为衡量系统性能的十分重要的指标;而调度方式和算法对处理机的利用率起 着十分重要的作用。在实际系统中,CPU 的利用率一般在 40%(系统负荷较轻)到 90%之间。 在大、中型系统中,在选择调度方式和算法时,应考虑到这一准则。但对于单用户微机或 某些实时系统,则此准则就不那么重要了。 (3) 各类资源的平衡利用。在大、中型系统中,不仅要使处理机的利用率高,而且还应 能有效地利用其它各类资源,如内存、外存和 I/O 设备等。选择适当的调度方式和算法可以 保持系统中各类资源都处于忙碌状态。但对于微型机和某些实时系统而言,该准则并不 重要。 3.3 调 度 算 法 在 OS 中调度的实质是一种资源分配,因而调度算法是指:根据系统的资源分配策略所 规定的资源分配算法。对于不同的系统和系统目标,通常采用不同的调度算法,例如,在 批处理系统中,为了照顾为数众多的短作业,应采用短作业优先的调度算法;又如在分时 系统中,为了保证系统具有合理的响应时间,应采用轮转法进行调度。目前存在的多种调 度算法中,有的算法适用于作业调度,有的算法适用于进程调度;但也有些调度算法既可 用于作业调度,也可用于进程调度。 3.2.1 先来先服务和短作业(进程)优先调度算法 1.先来先服务调度算法 先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也 可用于进程调度。当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一 个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放 入就绪队列。在进程调度中采用 FCFS 算法时,则每次调度是从就绪队列中选择一个最先进 入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件 而阻塞后才放弃处理机。 FCFS 算法比较有利于长作业(进程),而不利于短作业(进程)。下表列出了 A、B、C、 D 四个作业分别到达系统的时间、要求服务的时间、开始执行的时间及各自的完成时间, 并计算出各自的周转时间和带权周转时间。 ·92· 计算机操作系统 进程名 到达时间 A 0 B 1 C 2 D 3 服务时间 1 100 1 100 开始执行时间 0 1 101 102 完成时间 1 101 102 202 周转时间 1 100 100 199 带权周 转时间 1 1 100 1.99 从表上可以看出,其中短作业 C 的带权周转时间竞高达 100,这是不能容忍的;而长作 业 D 的带权周转时间仅为 1.99。据此可知,FCFS 调度算法有利于 CPU 繁忙型的作业,而 不利于 I/O 繁忙型的作业(进程)。CPU 繁忙型作业是指该类作业需要大量的 CPU 时间进行 计算,而很少请求 I/O。通常的科学计算便属于 CPU 繁忙型作业。I/O 繁忙型作业是指 CPU 进行处理时需频繁地请求 I/O。目前的大多数事务处理都属于 I/O 繁忙型作业。 在此,我们通过一个例子来说明采用 FCFS 调度算法时的调度性能。图 3-4(a)示出有五 个进程 A、B、C、D、E,它们到达的时间分别是 0、1、2、3 和 4,所要求的服务时间分别 是 4、3、5、2 和 4,其完成时间分别是 4、7、12、14 和 18。从每个进程的完成时间中减 去其到达时间,即得到其周转时间,进而可以算出每个进程的带权周转时间。 作业 情况 调度 算法 进程名 到达时间 服务时间 A B C D E 平均 0 1 23 4 4 3 52 4 FCFS (a) SJF (b) 完成时间 4 7 12 14 18 周转时间 4 6 10 11 14 9 带权周转时间 1 2 2 5.5 3.5 2.8 完成时间 4 9 18 6 13 周转时间 4 8 16 3 9 8 带权周转时间 1 2.67 3.1 1.5 2.25 2.1 图 3-4 FCFS 和 SJF 调度算法的性能 2.短作业(进程)优先调度算法 短作业(进程)优先调度算法 SJ(P)F,是指对短作业或短进程优先调度的算法。它们可以 分别用于作业调度和进程调度。短作业优先(SJF)的调度算法是从后备队列中选择一个或若 干个估计运行时间最短的作业,将它们调入内存运行。而短进程优先(SPF)调度算法则是从 就绪队列中选出一个估计运行时间最短的进程,将处理机分配给它,使它立即执行并一直 执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。 为了和 FCFS 调度算法进行比较,我们仍利用 FCFS 算法中所使用的实例,并改用 SJ(P)F 算法重新调度,再进行性能分析。由图 3-4 中的(a)和(b)可以看出,采用 SJ(P)F 算法后,不 论是平均周转时间还是平均带权周转时间,都有较明显的改善,尤其是对短作业 D,其周 转时间由原来的(用 FCFS 算法时)11 降为 3;而平均带权周转时间是从 5.5 降到 1.5。这说明 SJF 调度算法能有效地降低作业的平均等待时间,提高系统吞吐量。 第三章 处理机调度与死锁 ·93· SJ(P)F 调度算法也存在不容忽视的缺点: (1) 该算法对长作业不利,如作业 C 的周转时间由 10 增至 16,其带权周转时间由 2 增 至 3.1。更严重的是,如果有一长作业(进程)进入系统的后备队列(就绪队列),由于调度程序 总是优先调度那些(即使是后进来的)短作业(进程),将导致长作业(进程)长期不被调度。 (2) 该算法完全未考虑作业的紧迫程度,因而不能保证紧迫性作业(进程)会被及时处理。 (3) 由于作业(进程)的长短只是根据用户所提供的估计执行时间而定的,而用户又可能 会有意或无意地缩短其作业的估计运行时间,致使该算法不一定能真正做到短作业优先 调度。 3.3.2 高优先权优先调度算法 1.优先权调度算法的类型 为了照顾紧迫型作业,使之在进入系统后便获得优先处理,引入了最高优先权优先(FPF) 调度算法。此算法常被用于批处理系统中,作为作业调度算法,也作为多种操作系统中的 进程调度算法,还可用于实时系统中。当把该算法用于作业调度时,系统将从后备队列中 选择若干个优先权最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就 绪队列中优先权最高的进程,这时,又可进一步把该算法分成如下两种。 1) 非抢占式优先权算法 在这种方式下,系统一旦把处理机分配给就绪队列中优先权最高的进程后,该进程便 一直执行下去,直至完成;或因发生某事件使该进程放弃处理机时,系统方可再将处理机 重新分配给另一优先权最高的进程。这种调度算法主要用于批处理系统中;也可用于某些 对实时性要求不严的实时系统中。 2) 抢占式优先权调度算法 在这种方式下,系统同样是把处理机分配给优先权最高的进程,使之执行。但在其执 行期间,只要又出现了另一个其优先权更高的进程,进程调度程序就立即停止当前进程(原 优先权最高的进程)的执行,重新将处理机分配给新到的优先权最高的进程。因此,在采用 这种调度算法时,是每当系统中出现一个新的就绪进程 i 时,就将其优先权 Pi 与正在执行 的进程 j 的优先权 Pj 进行比较。如果 Pi≤Pj,原进程 Pj 便继续执行;但如果是 Pi>Pj,则立 即停止 Pj 的执行,做进程切换,使 i 进程投入执行。显然,这种抢占式的优先权调度算法 能更好地满足紧迫作业的要求,故而常用于要求比较严格的实时系统中,以及对性能要求 较高的批处理和分时系统中。 2.优先权的类型 对于最高优先权优先调度算法,其关键在于:它是使用静态优先权,还是用动态优先 权,以及如何确定进程的优先权。 1) 静态优先权 静态优先权是在创建进程时确定的,且在进程的整个运行期间保持不变。一般地,优 先权是利用某一范围内的一个整数来表示的,例如,0~7 或 0~255 中的某一整数,又把该 整数称为优先数,只是具体用法各异:有的系统用“0”表示最高优先权,当数值愈大时, 其优先权愈低;而有的系统恰恰相反。 ·94· 计算机操作系统 确定进程优先权的依据有如下三个方面: (1) 进程类型。通常,系统进程(如接收进程、对换进程、磁盘 I/O 进程)的优先权高于 一般用户进程的优先权。 (2) 进程对资源的需求。如进程的估计执行时间及内存需要量的多少,对这些要求少的 进程应赋予较高的优先权。 (3) 用户要求。这是由用户进程的紧迫程度及用户所付费用的多少来确定优先权的。 静态优先权法简单易行,系统开销小,但不够精确,很可能出现优先权低的作业(进程) 长期没有被调度的情况。因此,仅在要求不高的系统中才使用静态优先权。 2) 动态优先权 动态优先权是指在创建进程时所赋予的优先权,是可以随进程的推进或随其等待时间 的增加而改变的,以便获得更好的调度性能。例如,我们可以规定,在就绪队列中的进程, 随其等待时间的增长,其优先权以速率 a 提高。若所有的进程都具有相同的优先权初值, 则显然是最先进入就绪队列的进程将因其动态优先权变得最高而优先获得处理机,此即 FCFS 算法。若所有的就绪进程具有各不相同的优先权初值,那么,对于优先权初值低的进 程,在等待了足够的时间后,其优先权便可能升为最高,从而可以获得处理机。当采用抢 占式优先权调度算法时,如果再规定当前进程的优先权以速率 b 下降,则可防止一个长作 业长期地垄断处理机。 3.高响应比优先调度算法 在批处理系统中,短作业优先算法是一种比较好的算法,其主要的不足之处是长作业 的运行得不到保证。如果我们能为每个作业引入前面所述的动态优先权,并使作业的优先 级随着等待时间的增加而以速率 a 提高,则长作业在等待一定的时间后,必然有机会分配 到处理机。该优先权的变化规律可描述为: 优先权 = 等待时间 + 要求服务时间 要求服务时间 由于等待时间与服务时间之和就是系统对该作业的响应时间,故该优先权又相当于响 应比 RP。据此,又可表示为: RP = 等待时间 + 要求服务时间 要求服务时间 响应时间 = 要求服务时间 由上式可以看出: (1) 如果作业的等待时间相同,则要求服务的时间愈短,其优先权愈高,因而该算法有 利于短作业。 (2) 当要求服务的时间相同时,作业的优先权决定于其等待时间,等待时间愈长,其优 先权愈高,因而它实现的是先来先服务。 (3) 对于长作业,作业的优先级可以随等待时间的增加而提高,当其等待时间足够长时, 其优先级便可升到很高,从而也可获得处理机。 简言之,该算法既照顾了短作业,又考虑了作业到达的先后次序,不会使长作业长期 得不到服务。因此,该算法实现了一种较好的折衷。当然,在利用该算法时,每要进行调 第三章 处理机调度与死锁 ·95· 度之前,都须先做响应比的计算,这会增加系统开销。 3.3.3 基于时间片的轮转调度算法 如前所述,在分时系统中,为保证能及时响应用户的请求,必须采用基于时间片的轮 转式进程调度算法。在早期,分时系统中采用的是简单的时间片轮转法;进入 20 世纪 90 年代后,广泛采用多级反馈队列调度算法。 1.时间片轮转法 1) 基本原理 在早期的时间片轮转法中,系统将所有的就绪进程按先来先服务的原则排成一个队列, 每次调度时,把 CPU 分配给队首进程,并令其执行一个时间片。时间片的大小从几 ms 到 几百 ms。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号 来停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中 新的队首进程,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程在一 给定的时间内均能获得一时间片的处理机执行时间。换言之,系统能在给定的时间内响应 所有用户的请求。 2) 时间片大小的确定 在时间片轮转算法中,时间片的大小对系统性能有很大的影响,如选择很小的时间片 将有利于短作业,因为它能较快地完成,但会频繁地发生中断、进程上下文的切换,从而 增加系统的开销;反之,如选择太长的时间片,使得每个进程都能在一个时间片内完成, 时间片轮转算法便退化为 FCFS 算法,无法满足交互式用户的需求。一个较为可取的大小是, 时间片略大于一次典型的交互所需要的时间。这样可使大多数进程在一个时间片内完成。 图 3-5 示出了时间片分别为 q=1 和 q=4 时,A、B、C、D、E 五个进程的运行情况,而 图 3-6 为 q=1 和 q=4 时各进程的平均周转时间和带权平均周转时间。图中的 RR(Round Robin) 表示轮转调度算法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 t A (a) q=1 B A B A A B C C C C D D E E E E (b) q=4 图 3-5 q=1 和 q=4 时的进程运行情况 ·96· 计算机操作系统 作业 情况 时间片 RR q=1 RR q=4 进程名 A B C D E 平均 到达时间 0 1 2 3 4 服务时间 4 3 4 2 4 完成时间 15 12 16 9 17 周转时间 15 11 14 6 13 11.8 带权周转时间 3.75 3.67 3.5 3 3.33 3.46 完成时间 4 7 11 13 17 周转时间 4 6 9 10 13 8.4 带权周转时间 1 2 2.25 5 3.33 2.5 图 3-6 q=1 和 q=4 时进程的周转时间 2.多级反馈队列调度算法 前面介绍的各种用作进程调度的算法都有一定的局限性。如短进程优先的调度算法, 仅照顾了短进程而忽略了长进程,而且如果并未指明进程的长度,则短进程优先和基于进程 长度的抢占式调度算法都将无法使用。而多级反馈队列调度算法则不必事先知道各种进程所 需的执行时间,而且还可以满足各种类型进程的需要,因而它是目前被公认的一种较好的进 程调度算法。在采用多级反馈队列调度算法的系统中,调度算法的实施过程如下所述。 (1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高, 第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片 的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例 如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第 i+1 个队列的时间片要 比第 i 个队列的时间片长一倍。图 3-7 是多级反馈队列算法的示意。 就绪队列1 就绪队列2 就绪队列3 就绪队列n S1 至CPU S2 至CPU S3 至CPU 至CPU (时间片:S1<S2<S3) 图 3-7 多级反馈队列调度算法 (2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按 FCFS 原则排队等待 调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一 个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按 FCFS 原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第 三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第 n 队列后,在第 n 队 第三章 处理机调度与死锁 ·97· 列中便采取按时间片轮转的方式运行。 (3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第 1~(i-1)队 列均空时,才会调度第 i 队列中的进程运行。如果处理机正在第 i 队列中为某进程服务时, 又有新进程进入优先权较高的队列(第 1~(i-1)中的任何一个队列),则此时新进程将抢占正 在运行进程的处理机,即由调度程序把正在运行的进程放回到第 i 队列的末尾,把处理机分 配给新到的高优先权进程。 3.多级反馈队列调度算法的性能 多级反馈队列调度算法具有较好的性能,能很好地满足各种类型用户的需要。 (1) 终端型作业用户。由于终端型作业用户所提交的作业大多属于交互型作业,作业通 常较小,系统只要能使这些作业(进程)在第一队列所规定的时间片内完成,便可使终端型作 业用户都感到满意。 (2) 短批处理作业用户。对于很短的批处理型作业,开始时像终端型作业一样,如果仅 在第一队列中执行一个时间片即可完成,便可获得与终端型作业一样的响应时间。对于稍 长的作业,通常也只需在第二队列和第三队列各执行一个时间片即可完成,其周转时间仍 然较短。 (3) 长批处理作业用户。对于长作业,它将依次在第 1,2,…,n 个队列中运行,然后 再按轮转方式运行,用户不必担心其作业长期得不到处理。 3.4 实 时 调 度 由于在实时系统中都存在着若干个实时进程或任务,它们用来反应或控制某个(些)外部 事件,往往带有某种程度的紧迫性,因而对实时系统中的调度提出了某些特殊要求。前面 所介绍的多种调度算法并不能很好地满足实时系统对调度的要求,为此,需要引入一种新 的调度,即实时调度。 3.4.1 实现实时调度的基本条件 在实时系统中,硬实时任务和软实时任务都联系着一个截止时间。为保证系统能正常 工作,实时调度必须能满足实时任务对截止时间的要求,为此,实现实时调度应具备下述 几个条件。 1.提供必要的信息 为了实现实时调度,系统应向调度程序提供有关任务的下述一些信息: (1) 就绪时间。这是该任务成为就绪状态的起始时间,在周期任务的情况下,它就是事 先预知的一串时间序列;而在非周期任务的情况下,它也可能是预知的。 (2) 开始截止时间和完成截止时间。对于典型的实时应用,只须知道开始截止时间,或 者知道完成截止时间。 (3) 处理时间。这是指一个任务从开始执行直至完成所需的时间。在某些情况下,该时 ·98· 计算机操作系统 间也是系统提供的。 (4) 资源要求。这是指任务执行时所需的一组资源。 (5) 优先级。如果某任务的开始截止时间已经错过,就会引起故障,则应为该任务赋予 “绝对”优先级;如果开始截止时间的推迟对任务的继续运行无重大影响,则可为该任务 赋予“相对”优先级,供调度程序参考。 2.系统处理能力强 在实时系统中,通常都有着多个实时任务。若处理机的处理能力不够强,则有可能因 处理机忙不过来而使某些实时任务不能得到及时处理,从而导致发生难以预料的后果。假 定系统中有 m 个周期性的硬实时任务,它们的处理时间可表示为 Ci,周期时间表示为 Pi, 则在单处理机情况下,必须满足下面的限制条件: m ∑ Ci ≤1 i =1 Pi 系统才是可调度的。假如系统中有 6 个硬实时任务,它们的周期时间都是 50 ms,而每次的 处理时间为 10 ms,则不难算出,此时是不能满足上式的,因而系统是不可调度的。 解决的方法是提高系统的处理能力,其途径有二:其一仍是采用单处理机系统,但须 增强其处理能力,以显著地减少对每一个任务的处理时间;其二是采用多处理机系统。假 定系统中的处理机数为 N,则应将上述的限制条件改为: ∑m Ci i =1 Pi ≤N 顺便说明一下,上述的限制条件并未考虑到任务的切换时间,包括执行调度算法和进行任 务切换,以及消息的传递时间等开销,因此,当利用上述限制条件来确定系统是否可调度 时,还应适当地留有余地。 3.采用抢占式调度机制 在含有硬实时任务的实时系统中,广泛采用抢占机制。当一个优先权更高的任务到达 时,允许将当前任务暂时挂起,而令高优先权任务立即投入运行,这样便可满足该硬实时 任务对截止时间的要求。但这种调度机制比较复杂。 对于一些小型实时系统,如果能预知任务的开始截止时间,则对实时任务的调度可采 用非抢占调度机制,以简化调度程序和对任务调度时所花费的系统开销。但在设计这种调 度机制时,应使所有的实时任务都比较小,并在执行完关键性程序和临界区后,能及时地 将自己阻塞起来,以便释放出处理机,供调度程序去调度那种开始截止时间即将到达的 任务。 4.具有快速切换机制 为保证要求较高的硬实时任务能及时运行,在实时系统中还应具有快速切换机制,以 保证能进行任务的快速切换。该机制应具有如下两方面的能力: (1) 对外部中断的快速响应能力。为使在紧迫的外部事件请求中断时系统能及时响应, 第三章 处理机调度与死锁 ·99· 要求系统具有快速硬件中断机构,还应使禁止中断的时间间隔尽量短,以免耽误时机(其它 紧迫任务)。 (2) 快速的任务分派能力。在完成任务调度后,便应进行任务切换。为了提高分派程序 进行任务切换时的速度,应使系统中的每个运行功能单位适当地小,以减少任务切换的时 间开销。 3.4.2 实时调度算法的分类 可以按不同方式对实时调度算法加以分类,如根据实时任务性质的不同,可将实时调 度的算法分为硬实时调度算法和软实时调度算法;而按调度方式的不同,又可分为非抢占 调度算法和抢占调度算法; 还可因调度程序调度时间的不同而分成静态调度算法和动态调 度算法,前者是指在进程执行前,调度程序便已经决定了各进程间的执行顺序,而后者则 是在进程的执行过程中,由调度程序届时根据情况临时决定将哪一进程投入运行。在多处 理机环境下,还可将调度算法分为集中式调度和分布式调度两种算法。这里,我们仅按调 度方式的不同对调度算法进行分类。 1.非抢占式调度算法 由于非抢占式调度算法比较简单,易于实现,故在一些小型实时系统或要求不太严格 的实时控制系统中经常采用之。我们又可把它分成以下两种。 1) 非抢占式轮转调度算法 该算法常用于工业生产的群控系统中,由一台计算机控制若干个相同的(或类似的)对 象,为每一个被控对象建立一个实时任务,并将它们排成一个轮转队列。调度程序每次选 择队列中的第一个任务投入运行。当该任务完成后,便把它挂在轮转队列的末尾,等待下 次调度运行,而调度程序再选择下一个(队首)任务运行。这种调度算法可获得数秒至数十秒 的响应时间,可用于要求不太严格的实时控制系统中。 2) 非抢占式优先调度算法 如果在实时系统中存在着要求较为严格(响应时间为数百毫秒)的任务,则可采用非抢占 式优先调度算法为这些任务赋予较高的优先级。当这些实时任务到达时,把它们安排在就 绪队列的队首,等待当前任务自我终止或运行完成后才能被调度执行。这种调度算法在做 了精心的处理后,有可能获得仅为数秒至数百毫秒级的响应时间,因而可用于有一定要求 的实时控制系统中。 2.抢占式调度算法 在要求较严格的(响应时间为数十毫秒以下)的实时系统中,应采用抢占式优先权调度算 法。可根据抢占发生时间的不同而进一步分成以下两种调度算法。 1) 基于时钟中断的抢占式优先权调度算法 在某实时任务到达后,如果该任务的优先级高于当前任务的优先级,这时并不立即抢 占当前任务的处理机,而是等到时钟中断到来时,调度程序才剥夺当前任务的执行,将处 理机分配给新到的高优先权任务。这种调度算法能获得较好的响应效果,其调度延迟可降 为几十毫秒至几毫秒。因此,此算法可用于大多数的实时系统中。 ·100· 计算机操作系统 2) 立即抢占(Immediate Preemption)的优先权调度算法 在这种调度策略中,要求操作系统具有快速响应外部事件中断的能力。一旦出现外部 中断,只要当前任务未处于临界区,便立即剥夺当前任务的执行,把处理机分配给请求中 断的紧迫任务。这种算法能获得非常快的响应,可把调度延迟降低到几毫秒至 100 微秒, 甚至更低。 图 3-8 中的(a)、(b)、(c)、(d)分别示出了采用非抢占式轮转调度算法、非抢占式优先权 调度算法、基于时钟中断抢占的优先权调度算法和立即抢占的优先权调度算法四种情况的 调度时间。 实时进程要求调度 调度实时进程运行 实时进程请求调度 时钟中断到来时 进程 1 进程 2 … 进程 n 调度时间 (a) 非抢占式轮转调度 实时进程 当前进程 调度时间 实时进程 (c) 基于时钟中断抢占的优先权抢占调度 实时进程请求调度 当前进程运行完成 当前进程 调度时间 实时进程 实时进程请求调度 实时进程抢占当前 进程并立即执行 当前进程 实时进程 调度时间 (b) 非抢占式优先权调度 (d) 立即抢占的优先权调度 图 3-8 实时进程调度 3.4.3 常用的几种实时调度算法 目前已有许多用于实时系统的调度算法,其中有的算法仅适用于抢占式或非抢占式调 度,而有的算法则既适用于非抢占式,也适用于抢占式调度方式。在常用的几种算法中, 它们都是基于任务的优先权,并根据确定优先级方法的不同而又形成了以下几种实时调度 算法。 1.最早截止时间优先即 EDF(Earliest Deadline First)算法 该算法是根据任务的开始截止时间来确定任务的优先级。截止时间愈早,其优先级愈 高。该算法要求在系统中保持一个实时任务就绪队列,该队列按各任务截止时间的早晚排 序;当然,具有最早截止时间的任务排在队列的最前面。调度程序在选择任务时,总是选 择就绪队列中的第一个任务,为之分配处理机,使之投入运行。最早截止时间优先算法既 可用于抢占式调度,也可用于非抢占式调度方式中。 1) 非抢占式调度方式用于非周期实时任务 图 3-9 示出了将该算法用于非抢占调度方式之例。该例中具有四个非周期任务,它们先 后到达。系统首先调度任务 1 执行,在任务 1 执行期间,任务 2、3 又先后到达。由于任务 3 的开始截止时间早于任务 2,故系统在任务 1 后将调度任务 3 执行。在此期间又到达作业 4,其开始截止时间仍是早于任务 2 的,故在任务 3 执行完后,系统又调度任务 4 执行,最 第三章 处理机调度与死锁 后才调度任务 2 执行。 开始截止时间 1 3 4 2 ·101· 任务执行 1 3 4 2 t 任务到达 1 23 4 图 3-9 EDF 算法用于非抢占调度的调度方式 2) 抢占式调度方式用于周期实时任务 图 3-10 示出了将最早截止时间优先算法用于抢占调度方式之例。在该例中有两个周期 性任务,任务 A 的周期时间为 20 ms,每个周期的处理时间为 10 ms;任务 B 的周期时间为 50 ms,每个周期的处理时间为 25 ms。图中的第一行示出了两个任务的到达时间、最后期 限和执行时间图。其中任务 A 的到达时间为 0、20、40、…;任务 A 的最后期限为 20、40、 60、…;任务 B 的到达时间为 0、50、100、…;任务 B 的最后期限为 50、100、150、…(注: 单位皆为 ms)。 B1 最后期限 B2 最后期限 A1 A2 A3 A4 A5 最后期限 最后期限 最后期限 最后期限 最后期限 到达时间、执行时间 和最后期限 A1 A2 B1 0 10 20 30 A3 A4 B2 40 50 60 70 A5 80 90 100 时间t/ms 固定优先级调度 (A有较高优先级) A1 B1 A2 B1 A3 B2 A4 B2 A5 B2 A1 A2 B1 A3 A4 A5,B2 (错过) 固定优先级调度 (B有较高优先级) B1 A2 A3 B2 A1 (错过) A2 B1 A3 A5 A4 A5,B2 (错过) 使用完成最后期限 最早和最后期限调度 A1 B1 A2 B1 A3 B2 A4 B2 A5 A1 A2 B1 A3 A4 A5,B2 图 3-10 最早截止时间优先算法用于抢占调度方式之例 为了说明通常的优先级调度不能适用于实时系统,该图特增加了第二和第三行。在第 二行中假定任务 A 具有较高的优先级,所以在 t = 0 ms 时,先调度 A1 执行,在 A1 完成后 (t = 10 ms)才调度 B1 执行;在 t = 20 ms 时,调度 A2 执行;在 t = 30 ms 时,A2 完成,又调 度 B1 执行;在 t = 40 ms 时,调度 A3 执行;在 t = 50 ms 时,虽然 A3 已完成,但 B1 已错 ·102· 计算机操作系统 过了它的最后期限,这说明了利用通常的优先级调度已经失败。第三行与第二行类似,只 是假定任务 B 具有较高的优先级。 第四行是采用最早截止时间优先算法的时间图。在 t = 0 时,A1 和 B1 同时到达,由于 A1 的截止时间比 B1 早,故调度 A1 执行;在 t = 10 时,A1 完成,又调度 B1 执行;在 t = 20 时,A2 到达,由于 A2 的截止时间比 B2 早,B1 被中断而调度 A2 执行;在 t = 30 时,A2 完成,又重新调度 B1 执行;在 t = 40 时,A3 又到达,但 B1 的截止时间要比 A3 早,仍应 让 B1 继续执行直到完成(t = 45),然后再调度 A3 执行;在 t = 55 时,A3 完成,又调度 B2 执行。在该例中利用最早截止时间优先算法可以满足系统的要求。 2.最低松弛度优先即 LLF(Least Laxity First)算法 该算法是根据任务紧急(或松弛)的程度,来确定任务的优先级。任务的紧急程度愈高, 为该任务所赋予的优先级就愈高,以使之优先执行。例如,一个任务在 200 ms 时必须完成, 而它本身所需的运行时间就有 100 ms,因此,调度程序必须在 100 ms 之前调度执行,该任 务的紧急程度(松弛程度)为 100 ms。又如,另一任务在 400 ms 时必须完成,它本身需要运 行 150 ms,则其松弛程度为 250 ms。在实现该算法时要求系统中有一个按松弛度排序的实 时任务就绪队列,松弛度最低的任务排在队列最前面,调度程序总是选择就绪队列中的队 首任务执行。 该算法主要用于可抢占调度方式中。假如在一个实时系统中,有两个周期性实时任务 A 和 B,任务 A 要求每 20 ms 执行一次,执行时间为 10 ms;任务 B 只要求每 50 ms 执行一 次,执行时间为 25 ms。由此可得知任务 A 和 B 每次必须完成的时间分别为:A1、A2、A3、… 和 B1、B2、B3、…,见图 3-11。为保证不遗漏任何一次截止时间,应采用最低松弛度优先 的抢占调度策略。 A1 A2 A3 A4 A5 A6 A7 A8 t 0 20 40 60 80 100 120 140 160 B1 B2 B3 图 3-11 A 和 B 任务每次必须完成的时间 在刚开始时(t1 = 0),A1 必须在 20 ms 时完成,而它本身运行又需 10 ms,可算出 A1 的 松弛度为 10 ms;B1 必须在 50 ms 时完成,而它本身运行就需 25 ms,可算出 B1 的松弛度 为 25 ms,故调度程序应先调度 A1 执行。在 t2 = 10 ms 时,A2 的松弛度可按下式算出: A2 的松弛度 = 必须完成时间 - 其本身的运行时间 - 当前时间 = 40 ms-10 ms-10 ms = 20 ms 类似地,可算出 B1 的松弛度为 15 ms,故调度程序应选择 B2 运行。在 t3 = 30 ms 时, A2 的松弛度已减为 0(即 40 - 10 - 30),而 B1 的松弛度为 15 ms(即 50 - 5 - 30),于是调度程 序应抢占 B1 的处理机而调度 A2 运行。在 t4 = 40 ms 时,A3 的松弛度为 10 ms(即 60 - 10 - 40), 而 B1 的松弛度仅为 5 ms(即 50 - 5 - 40),故又应重新调度 B1 执行。在 t5 = 45 ms 时,B1 执行完成,而此时 A3 的松弛度已减为 5 ms(即 60 - 10 - 45),而 B2 的松弛度为 30 ms (即 100 - 25 - 45),于是又应调度 A3 执行。在 t6 = 55 ms 时,任务 A 尚未进入第 4 周期, 第三章 处理机调度与死锁 ·103· 而任务 B 已进入第 2 周期,故再调度 B2 执行。在 t7 = 70 ms 时,A4 的松弛度已减至 0 ms (即 80 - 10 - 70),而 B2 的松弛度为 20 ms(即 100 - 10 - 70),故此时调度又应抢占 B2 的处 理机而调度 A4 执行。图 3-12 示出了具有两个周期性实时任务的调度情况。 A1(10) A2(10) A3(10) A4(10) t1 0 t1=0 t2 t3 t4 t5 t6 t7 10 20 30 40 50 60 70 B1(20) B1(5) B2(15) 图 3-12 利用 LLF 算法进行调度的情况 t8 t 80 B2(10) 3.5 产生死锁的原因和必要条件 在多道程序系统中,虽可借助于多个进程的并发执行来改善系统的资源利用率,提高 系统的吞吐量,但可能发生一种危险——死锁。所谓死锁(Deadlock),是指多个进程在运行 过程中因争夺资源而造成的一种僵局(DeadlyEmbrace),当进程处于这种僵持状态时,若无 外力作用,它们都将无法再向前推进。在前面介绍把信号量作为同步工具时已提及到,若 多个 wait 和 signal 操作顺序不当,会产生进程死锁。 3.5.1 产生死锁的原因 产生死锁的原因可归结为如下两点: (1) 竞争资源。当系统中供多个进程共享的资源如打印机、公用队列等,其数目不足以 满足诸进程的需要时,会引起诸进程对资源的竞争而产生死锁。 (2) 进程间推进顺序非法。进程在运行过程中,请求和释放资源的顺序不当,也同样会 导致产生进程死锁。 下面详细分析产生死锁的原因。 1.竞争资源引起进程死锁 1) 可剥夺和非剥夺性资源 可把系统中的资源分成两类,一类是可剥夺性资源,是指某进程在获得这类资源后, 该资源可以再被其他进程或系统剥夺。例如,优先权高的进程可以剥夺优先权低的进程的 处理机。又如,内存区可由存储器管理程序把一个进程从一个存储区移到另一个存储区, 此即剥夺了该进程原来占有的存储区。甚至可将一个进程从内存调出到外存上。可见,CPU 和主存均属于可剥夺性资源。另一类资源是不可剥夺性资源,当系统把这类资源分配给某 进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。 2) 竞争非剥夺性资源 在系统中所配置的非剥夺性资源,由于它们的数量不能满足诸进程运行的需要,会使 进程在运行过程中,因争夺这些资源而陷入僵局。例如,系统中只有一台打印机 R1 和一台 磁带机 R2,可供进程 P1 和 P2 共享。假定 P1 已占用了打印机 R1,P2 已占用了磁带机 R2。此 时,若 P2 继续要求打印机,P2 将阻塞;P1 若又要求磁带机,P1 也将阻塞。于是,在 P1 与 ·104· 计算机操作系统 P2 之间便形成了僵局,两个进程都在等待对方释 放出自己所需的资源。但它们又都因不能继续获 得自己所需的资源而不能继续推进,从而也不能 释放出自己已占有的资源,以致进入死锁状态。 为便于说明,我们用方块代表资源,用圆圈代表 进程,见图 3-13 所示。当箭头从进程指向资源 时,表示进程请求资源;当箭头从资源指向进程 时,表示该资源已被分配给该进程。从中可以看 出,这时在 P1、P2 及 R1 和 R2 之间已经形成了一 个环路,说明已进入死锁状态。 P1 R1 R2 P2 图 3-13 I/O 设备共享时的死锁情况 3) 竞争临时性资源 上述的打印机资源属于可顺序重复使用型资源,称为永久性资源。还有一种是所谓的 临时性资源,这是指由一个进程产生,被另一进程使用一短暂时间后便无用的资源,故也 称之为消耗性资源,它也可能引起死锁。图 3-14 示出了在进程之间通信时形成死锁的情况。 图中 S1、S2 和 S3 是临时性资源。进程 P1 产生消息 S1,又要求从 P3 接收消息 S3;进程 P3 产生消息 S3,又要求从进程 P2 接收其所产生的消 息 S2;进程 P2 产生消息 S2,又需要接收进程 P1 P1 所产生的消息 S1。如果消息通信按下述顺序进行: S3 S1 P1: …Release(S1); Reqaest(S3); … P2: …Release(S2); Request(S1); … P3: …Release(S3); Request(S2); … P3 P2 并不可能发生死锁,但若改成下述的运行顺序: S2 P1: …Request(S3); Release(S1); … P2: …Request(S1); Release(S2); … 图 3-14 进程之间通信时的死锁 P3: …Request(S2); Release(S3); … 则可能发生死锁。 2.进程推进顺序不当引起死锁 由于进程在运行中具有异步性特征,这就可能使上述 P1 和 P2 两个进程按下述两种顺序 向前推进。 1) 进程推进顺序合法 在进程 P1 和 P2 并发执行时,如果按下述顺序推进: P1:Request(R1);Request(R2); P1:Releast(R1);Release(R2); P2:Request(R2);Request(R1); P2:Release(R2);Release(R1); 两个进程便可顺利完成。图 3-15 中的曲线①示出了这一情况。类似地,若按曲线②和 ③所示的顺序推进,两进程也可顺利完成。我们称这种不会引起进程死锁的推进顺序是合 法的。 P2Rel(R1) P2Rel(R2) P2Req(R1) P2Req(R2) 第三章 处理机调度与死锁 ② D ④ ① ③ ·105· P1Req(R1) P1Req(R2) P1Rel(R1) P1Rel(R2) 图 3-15 进程推进顺序对死锁的影响 2) 进程推进顺序非法 若并发进程 P1 和 P2 按曲线④所示的顺序推进,它们将进入不安全区 D 内。此时 P1 保 持了资源 R1,P2 保持了资源 R2,系统处于不安全状态。因为这时两进程再向前推进,便可 能发生死锁。例如,当 P1 运行到 P1:Request(R2)时,将因 R2 已被 P2 占用而阻塞;当 P2 运 行到 P2:Request(R1)时,也将因 R1 已被 P1 占用而阻塞,于是发生了进程死锁。 3.5.2 产生死锁的必要条件 虽然进程在运行过程中可能发生死锁,但死锁的发生也必须具备一定的条件。综上所 述不难看出,死锁的发生必须具备下列四个必要条件。 (1) 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由 一个进程占用。如果此时还有其它进程请求该资源,则请求者只能等待,直至占有该资源 的进程用毕释放。 (2) 请求和保持条件:指进程已经保持了至少一个资源,但又提出了新的资源请求,而 该资源又已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。 (3) 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完 时由自己释放。 (4) 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集 合{P0,P1,P2,…,Pn}中的 P0 正在等待一个 P1 占用的资源;P1 正在等待 P2 占用的资源,……, Pn 正在等待已被 P0 占用的资源。 3.5.3 处理死锁的基本方法 为保证系统中诸进程的正常运行,应事先采取必要的措施,来预防发生死锁。在系统 中已经出现死锁后,则应及时检测到死锁的发生,并采取适当措施来解除死锁。目前,处 理死锁的方法可归结为以下四种: (1) 预防死锁。这是一种较简单和直观的事先预防的方法。该方法是通过设置某些限制 条件,去破坏产生死锁的四个必要条件中的一个或几个条件,来预防发生死锁。预防死锁 ·106· 计算机操作系统 是一种较易实现的方法,已被广泛使用。但由于所施加的限制条件往往太严格,因而可能 会导致系统资源利用率和系统吞吐量降低。 (2) 避免死锁。该方法同样是属于事先预防的策略,但它并不须事先采取各种限制措施 去破坏产生死锁的四个必要条件,而是在资源的动态分配过程中,用某种方法去防止系统 进入不安全状态,从而避免发生死锁。这种方法只需事先施加较弱的限制条件,便可获得 较高的资源利用率及系统吞吐量,但在实现上有一定的难度。目前在较完善的系统中常用 此方法来避免发生死锁。 (3) 检测死锁。这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进 入不安全区,而是允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及 时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源; 然后,采取适当措施, 从系统中将已发生的死锁清除掉。 (4) 解除死锁。这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须 将进程从死锁状态中解脱出来。常用的实施方法是撤消或挂起一些进程,以便回收一些资 源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。死锁 的检测和解除措施有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。 3.6 预防死锁的方法 如前所述,预防死锁和避免死锁这两种方法实质上都是通过施加某些限制条件,来预 防发生死锁。两者的主要差别在于:为预防死锁所施加的限制条件较严格,这往往会影响 进程的并发执行;而为避免死锁所施加的限制条件则较宽松,这给进程的运行提供了较宽 松的环境,有利于进程的并发执行。 3.6.1 预防死锁 预防死锁的方法是使四个必要条件中的第 2、3、4 个条件之一不能成立,来避免发生 死锁。至于必要条件 1,因为它是由设备的固有特性所决定的,不仅不能改变,还应加以 保证。 1.摒弃“请求和保持”条件 在采用这种方法时,系统规定所有进程在开始运行之前,都必须一次性地申请其在整 个运行过程所需的全部资源。此时,若系统有足够的资源分配给某进程,便可把其需要的 所有资源分配给该进程,这样,该进程在整个运行期间便不会再提出资源要求,从而摒弃 了请求条件。但在分配资源时,只要有一种资源不能满足某进程的要求,即使其它所需的 各资源都空闲,也不分配给该进程,而让该进程等待。由于在该进程的等待期间,它并未 占有任何资源,因而也摒弃了保持条件,从而可以避免发生死锁。 这种预防死锁的方法其优点是简单、易于实现且很安全。但其缺点却也极其明显:首 先表现为资源被严重浪费,因为一个进程是一次性地获得其整个运行过程所需的全部资源 的,且独占资源,其中可能有些资源很少使用,甚至在整个运行期间都未使用,这就严重 地恶化了系统资源的利用率;其次是使进程延迟运行,仅当进程在获得了其所需的全部资 第三章 处理机调度与死锁 ·107· 源后,才能开始运行,但可能因有些资源已长期被其它进程占用而致使等待该资源的进程 迟迟不能运行。 2.摒弃“不剥夺”条件 在采用这种方法时系统规定,进程是逐个地提出对资源的要求的。当一个已经保持了 某些资源的进程,再提出新的资源请求而不能立即得到满足时,必须释放它已经保持了的 所有资源,待以后需要时再重新申请。这意味着某一进程已经占有的资源,在运行过程中 会被暂时地释放掉,也可认为是被剥夺了,从而摒弃了“不剥夺”条件。 这种预防死锁的方法实现起来比较复杂且要付出很大的代价。因为一个资源在使用一 段时间后,它的被迫释放可能会造成前段工作的失效,即使是采取了某些防范措施,也还 会使进程前后两次运行的信息不连续,例如,进程在运行过程中已用打印机输出信息,但 中途又因申请另一资源未果而被迫暂停运行并释放打印机,后来系统又把打印机分配给其 它进程使用。当进程再次恢复运行并再次获得打印机继续打印时,这前后两次打印输出的 数据并不连续,即打印输出的信息其中间有一段是另一进程的。此外,这种策略还可能因 为反复地申请和释放资源,致使进程的执行被无限地推迟,这不仅延长了进程的周转时间, 而且也增加了系统开销,降低了系统吞吐量。 3.摒弃“环路等待”条件 这种方法中规定,系统将所有资源按类型进行线性排队,并赋予不同的序号。例如, 令输入机的序号为 1,打印机的序号为 2,磁带机为 3,磁盘为 4。所有进程对资源的请求 必须严格按照资源序号递增的次序提出,这样,在所形成的资源分配图中,不可能再出现 环路,因而摒弃了“环路等待”条件。事实上,在采用这种策略时,总有一个进程占据了 较高序号的资源,此后它继续申请的资源必然是空闲的,因而进程可以一直向前推进。 这种预防死锁的策略与前两种策略比较,其资源利用率和系统吞吐量都有较明显的改 善。但也存在下述严重问题: 首先是为系统中各类资源所分配(确定)的序号必须相对稳定,这就限制了新类型设备的 增加。 其次,尽管在为资源的类型分配序号时,已经考虑到大多数作业在实际使用这些资源 时的顺序,但也经常会发生这种情况:即作业(进程)使用各类资源的顺序与系统规定的顺序 不同,造成对资源的浪费。例如,某进程先用磁带机,后用打印机,但按系统规定,该进 程应先申请打印机而后申请磁带机,致使先获得的打印机被长时间闲置。 第三,为方便用户,系统对用户在编程时所施加的限制条件应尽量少。然而这种按规 定次序申请的方法,必然会限制用户简单、自主地编程。 3.6.2 系统安全状态 在预防死锁的几种方法中,都施加了较强的限制条件;在避免死锁的方法中,所施加 的限制条件较弱,有可能获得令人满意的系统性能。在该方法中把系统的状态分为安全状 态和不安全状态,只要能使系统始终都处于安全状态,便可避免发生死锁。 1.安全状态 在避免死锁的方法中,允许进程动态地申请资源,但系统在进行资源分配之前,应先 ·108· 计算机操作系统 计算此次资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给 进程;否则,令进程等待。 所谓安全状态,是指系统能按某种进程顺序(P1,P2,…,Pn)(称〈P1,P2,…,Pn〉序 列为安全序列),来为每个进程 Pi 分配其所需资源,直至满足每个进程对资源的最大需求, 使每个进程都可顺利地完成。如果系统无法找到这样一个安全序列,则称系统处于不安全 状态。 虽然并非所有的不安全状态都必然会转为死锁状态,但当系统进入不安全状态后,便 有可能进而进入死锁状态;反之,只要系统处于安全状态,系统便可避免进入死锁状态。 因此,避免死锁的实质在于:系统在进行资源分配时,如何使系统不进入不安全状态。 2.安全状态之例 我们通过一个例子来说明安全性。假定系统中有三个进程 P1、P2 和 P3,共有 12 台磁带 机。进程 P1 总共要求 10 台磁带机,P2 和 P3 分别要求 4 台和 9 台。假设在 T0 时刻,进程 P1、 P2 和 P3 已分别获得 5 台、2 台和 2 台磁带机,尚有 3 台空闲未分配,如下表所示: 进程 P1 P2 P3 最大需求 10 4 9 已分配 5 2 2 可用 3 经分析发现,在 T0 时刻系统是安全的,因为这时存在一个安全序列〈P2,P1,P3〉,即 只要系统按此进程序列分配资源,就能使每个进程都顺利完成。例如,将剩余的磁带机取 2 台分配给 P2,使之继续运行,待 P2 完成,便可释放出 4 台磁带机,于是可用资源增至 5 台; 以后再将这些全部分配给进程 P1,使之运行,待 P1 完成后,将释放出 10 台磁带机,P3 便 能获得足够的资源,从而使 P1、P2、P3 每个进程都能顺利完成。 3.由安全状态向不安全状态的转换 如果不按照安全序列分配资源,则系统可能会由安全状态进入不安全状态。例如,在 T0 时刻以后,P3 又请求 1 台磁带机,若此时系统把剩余 3 台中的 1 台分配给 P3,则系统便 进入不安全状态。因为此时也无法再找到一个安全序列,例如,把其余的 2 台分配给 P2, 这样,在 P2 完成后只能释放出 4 台,既不能满足 P1 尚需 5 台的要求,也不能满足 P3 尚需 6 台的要求,致使它们都无法推进到完成,彼此都在等待对方释放资源,即陷入僵局,结果 导致死锁。类似地,如果我们将剩余的 2 台磁带机先分配给 P1 或 P3,也同样都无法使它们 推进到完成,因此,从给 P3 分配了第 3 台磁带机开始,系统便又进入了不安全状态。由此 可见,在 P3 请求资源时,尽管系统中尚有可用的磁带机,但却不能分配给它,必须让 P3 一 直等待到 P1 和 P2 完成,释放出资源后再将足够的资源分配给 P3,它才能顺利完成。 3.6.3 利用银行家算法避免死锁 最有代表性的避免死锁的算法,是 Dijkstra 的银行家算法。这是由于该算法能用于银行 系统现金贷款的发放而得名的。为实现银行家算法,系统中必须设置若干数据结构。 第三章 处理机调度与死锁 ·109· 1.银行家算法中的数据结构 (1) 可利用资源向量 Available。这是一个含有 m 个元素的数组,其中的每一个元素代 表一类可利用的资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值 随该类资源的分配和回收而动态地改变。如果 Available[j]=K,则表示系统中现有 R j 类资源 K 个。 (2) 最大需求矩阵 Max。这是一个 n×m 的矩阵,它定义了系统中 n 个进程中的每一个 进程对 m 类资源的最大需求。如果 Max[i,j]=K,则表示进程 i 需要 Rj 类资源的最大数目 为 K。 (3) 分配矩阵 Allocation。这也是一个 n×m 的矩阵,它定义了系统中每一类资源当前已 分配给每一进程的资源数。如果 Allocation[i,j]=K,则表示进程 i 当前已分得 R j 类资源的数 目为 K。 (4) 需求矩阵 Need。这也是一个 n×m 的矩阵,用以表示每一个进程尚需的各类资源数。 如果 Need[i,j]=K,则表示进程 i 还需要 R j 类资源 K 个,方能完成其任务。 上述三个矩阵间存在下述关系: Need[i, j]=Max[i, j]-Allocation[i, j] 2.银行家算法 设 Request i 是进程 Pi 的请求向量,如果 Request i[j]=K,表示进程 P i 需要 K 个 R j 类型 的资源。当 P i 发出资源请求后,系统按下述步骤进行检查: (1) 如果 Request i[j]≤Need[i,j],便转向步骤(2);否则认为出错,因为它所需要的资源 数已超过它所宣布的最大值。 (2) 如果 Requesti[j]≤Available[j],便转向步骤(3);否则,表示尚无足够资源,Pi 须 等待。 (3) 系统试探着把资源分配给进程 P i,并修改下面数据结构中的数值: Available[j]:= Available[j]-Request i[j]; Allocation[i,j]:= Allocation[i,j]+Request i[j]; Need[i,j]:= Need[i,j]-Request i[j]; (4) 系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。若安全,才正 式将资源分配给进程 Pi,以完成本次分配;否则,将本次的试探分配作废,恢复原来的资 源分配状态,让进程 Pi 等待。 3.安全性算法 系统所执行的安全性算法可描述如下: (1) 设置两个向量: ① 工作向量 Work,它表示系统可提供给进程继续运行所需的各类资源数目,它含有 m 个元素,在执行安全算法开始时,Work:=Available。 ② Finish,它表示系统是否有足够的资源分配给进程,使之运行完成。开始时先做 Finish[i]:=false;当有足够资源分配给进程时,再令 Finish[i]:=true。 (2) 从进程集合中找到一个能满足下述条件的进程: ① Finish[i]=false; ·110· 计算机操作系统 ② Need[i,j]≤Work[j];若找到,执行步骤(3),否则,执行步骤(4)。 (3) 当进程 Pi 获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应 执行: Work[j]:= Work[j]+Allocation[i,j]; Finish[i]:=true; go to step 2; (4) 如果所有进程的 Finish[i]=true 都满足,则表示系统处于安全状态;否则,系统处于 不安全状态。 4.银行家算法之例 假定系统中有五个进程{P0,P1,P2,P3,P4}和三类资源{A,B,C},各种资源的数量 分别为 10、5、7,在 T0 时刻的资源分配情况如图 3-16 所示。 资源 Max 情况 进程 A B C Allocation ABC Need ABC Available A BC P0 7 5 3 0 1 0 7 4 3 3 32 (2 3 0) P1 3 2 2200122 (3 0 2) (0 2 0) P2 9 0 2302600 P3 2 2 2211011 P4 4 3 3002431 图 3-16 T0 时刻的资源分配表 (1) T0 时刻的安全性:利用安全性算法对 T0 时刻的资源分配情况进行分析(见图 3-17 所 示)可知,在 T0 时刻存在着一个安全序列{P1,P3,P4,P2,P0},故系统是安全的。 资源 情况 进程 Work A BC Need AB C Allocation ABC Work+Allocation ABC Finish P1 3 3 2122200 5 3 2 true P3 5 3 2011211 7 4 3 true P4 7 4 3431002 7 4 5 true P2 7 4 5 6 0 0 3 0 2 10 4 7 true P0 10 4 7 7 4 3 0 1 0 10 5 7 true 图 3-17 T0 时刻的安全序列 (2) P1 请求资源:P1 发出请求向量 Request1(1,0,2),系统按银行家算法进行检查: ① Request1(1,0,2)≤Need1(1,2,2) ② Request1(1,0,2)≤Available1(3,3,2) ③ 系统先假定可为 P1 分配资源,并修改 Available,Allocation1 和 Need1 向量,由此形 成的资源变化情况如图 3-16 中的圆括号所示。 第三章 处理机调度与死锁 ·111· ④ 再利用安全性算法检查此时系统是否安全。如图 3-18 所示。 资源 情况 进程 Work A BC Need AB C Allocation Work+Allocation ABC A B C Finish P1 2 3 0020302 5 3 2 true P3 5 3 2011211 7 4 3 true P4 7 4 3431002 7 4 5 true P0 7 4 5743010 7 5 5 true P2 7 5 5 6 0 0 3 0 2 10 5 7 true 图 3-18 P1 申请资源时的安全性检查 由所进行的安全性检查得知,可以找到一个安全序列{P1,P3,P4,P2,P0}。因此,系 统是安全的,可以立即将 P1 所申请的资源分配给它。 (3) P4 请求资源:P4 发出请求向量 Request4(3,3,0),系统按银行家算法进行检查: ① Request4(3,3,0)≤Need4(4,3,1); ② Request4(3,3,0)≤Available(2,3,0),让 P4 等待。 (4) P0 请求资源:P0 发出请求向量 Requst0(0,2,0),系统按银行家算法进行检查: ① Request0(0,2,0)≤Need0(7,4,3); ② Request0(0,2,0)≤Available(2,3,0); ③ 系统暂时先假定可为 P0 分配资源,并修改有关数据,如图 3-19 所示。 资源 情况 进程 Allocation A B C Need A B C Available A B C P0 0 3 0 7 3 2 2 1 0 P1 3 0 2 0 2 0 P2 3 0 2 6 0 0 P3 2 1 1 0 1 1 P4 0 0 2 4 3 1 图 3-19 为 P0 分配资源后的有关资源数据 (5) 进行安全性检查:可用资源 Available(2,1,0)已不能满足任何进程的需要,故系统 进入不安全状态,此时系统不分配资源。 如果在银行家算法中,把 P0 发出的请求向量改为 Request0(0,1,0),系统是否能将资 源分配给它,请读者考虑。 3.7 死锁的检测与解除 3.7.1 死锁的检测 当系统为进程分配资源时,若未采取任何限制性措施,则系统必须提供检测和解除死 锁的手段,为此,系统必须做到: ·112· 计算机操作系统 (1) 保存有关资源的请求和分配信息; (2) 提供一种算法,以利用这些信息来检测系统是否已进入死锁状态。 1.资源分配图(Resource Allocation Graph) 系统死锁可利用资源分配图来描述。该图是由一组结点 N 和一组边 E 所组成的一个对 偶 G=(N1E),它具有下述形式的定义和限制: (1) 把 N 分为两个互斥的子集,即一组进程结点 P={p1,p2,…,pn}和一组资源结 点 R={r1,r2,…,rn},N=P∪R。在图 3-20 所示的例子中,P={p1,p2},R={r1,r2}, N={r1,r2}∪{p1,p2}。 (2) 凡属于 E 中的一个边 e∈E,都连接着 P 中的一个结点和 R 中的一个结点,e={pi,rj} 是资源请求边,由进程 pi 指向资源 rj,它表示进程 pi 请求一个单位的 rj 资源。e={rj,pi}是 资源分配边,由资源 rj 指向进程 pi,它表示把一个单位的资源 rj 分配给进程 pi。图 3-13 中 示出了两个请求边和两个分配边,即 E={(p1,r2),(r2,p2),(p2,r1),(r1,p1)}。 我们用圆圈代表一个进程,用方框代表一类 资源。由于一种类型的资源可能有多个,我们用 P1 方框中的一个点代表一类资源中的一个资源。此 时,请求边是由进程指向方框中的 rj,而分配边则 应始于方框中的一个点。图 3-20 示出了一个资源 分配图。图中,p1 进程已经分得了两个 r1 资源, 并又请求一个 r2 资源;p2 进程分得了一个 r1 和一 个 r2 资源,并又请求 r1 资源。 2.死锁定理 r1 r2 P2 图 3-20 每类资源有多个时的情况 我们可以利用把资源分配图加以简化的方法(图 3-21),来检测当系统处于 S 状态时是否 为死锁状态。简化方法如下: (1) 在资源分配图中,找出一个既不阻塞又非独立的进程结点 Pi。在顺利的情况下,Pi 可获得所需资源而继续运行,直至运行完毕,再释放其所占有的全部资源,这相当于消去 pi 所求的请求边和分配边,使之成为孤立的结点。在图 3-21(a)中,将 p1 的两个分配边和一 个请求边消去,便形成图(b)所示的情况。 P1 P1 P1 P2 P2 P2 (a) (b) (c) 图 3-21 资源分配图的简化 第三章 处理机调度与死锁 ·113· (2) p1 释放资源后,便可使 p2 获得资源而继续运行,直至 p2 完成后又释放出它所占有 的全部资源,形成图(c)所示的情况。 (3) 在进行一系列的简化后,若能消去图中所有的边,使所有的进程结点都成为孤立结 点,则称该图是可完全简化的;若不能通过任何过程使该图完全简化,则称该图是不可完 全简化的。 对于较复杂的资源分配图,可能有多个既未阻塞,又非孤立的进程结点,不同的简化 顺序是否会得到不同的简化图?有关文献已经证明,所有的简化顺序,都将得到相同的不 可简化图。同样可以证明:S 为死锁状态的充分条件是:当且仅当 S 状态的资源分配图是不 可完全简化的。该充分条件被称为死锁定理。 3.死锁检测中的数据结构 死锁检测中的数据结构类似于银行家算法中的数据结构: (1) 可利用资源向量 Available,它表示了 m 类资源中每一类资源的可用数目。 (2) 把不占用资源的进程(向量 Allocationi:=0)记入 L 表中,即 Li∪L。 (3) 从进程集合中找到一个 Requesti≤Work 的进程,做如下处理: ① 将其资源分配图简化,释放出资源,增加工作向量 Work:=Work + Allocation i。 ② 将它记入 L 表中。 (4) 若不能把所有进程都记入 L 表中,便表明系统状态 S 的资源分配图是不可完全简化 的。因此,该系统状态将发生死锁。 Work:=Available; L:={Li |Allocation i=0∩Request i=0} for all Li ∉ L do begin for all Request i≤Work do begin Work :=Work + Allocation i; Li∪L; end end deadlock :=┓(L={p1,p2,…,pn}); 3.7.2 死锁的解除 当发现有进程死锁时,便应立即把它们从死锁状态中解脱出来。常采用解除死锁的两 种方法是: (1) 剥夺资源。从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态。 (2) 撤消进程。最简单的撤消进程的方法是使全部死锁进程都夭折掉;稍微温和一点的 方法是按照某种顺序逐个地撤消进程,直至有足够的资源可用,使死锁状态消除为止。 在出现死锁时,可采用各种策略来撤消进程。例如,为解除死锁状态所需撤消的进 程数目最小;或者,撤消进程所付出的代价最小等。一种付出最小代价的方法如图 3-22 所示。 ·114· 计算机操作系统 P1(CU1) U1 … P2 P3 … Pk S P1(CUd) U2 P1(CUk) … Uk Pk V12 V13 … V1k P2 P4 … Pk V21 V22 … V2k P1 P4 … Pk Vk1 Vk2 … Vkk W132 W134 … W13k W231 W234 … W23k Wk21 Wk22 … Wk2k 图 3-22 付出代价最小的死锁解除方法 假定在死锁状态时,有死锁进程 P1,P2,…,Pk。首先,撤消进程 P1,使系统状态由 S→U1,付出的代价为 CU1,然后,仍然从 S 状态中撤消进程 P2,使状态由 S→U2,其代价 为 CU2,…,如此下去可得到状态 U1,U2,…,Un。若此时系统仍处于死锁状态,需再进 一步撤消进程,如此下去,直至解除死锁状态为止。这种方法为解除死锁状态可能付出的 代价将是 k(k-1)(k-2)…/2C。显然,所花费的代价很大,因此,这是一种很不实际的方法。 一个比较有效的方法是对死锁状态 S 做如下处理:从死锁状态 S 中先撤消一个死锁进 程 P1,使系统状态由 S 演变成 U1,将 P1 记入被撤消进程的集合 d(T)中,并把所付出的代价 C1 加入到 rc(T)中;对死锁进程 P2、P3 等重复上述过程,得到状态 U1,U2,…,Ui,Un, 然后,再按撤消进程时所花费代价的大小,把它插入到由 S 状态所演变的新状态的队列 L 中。显然,队列 L 中的第一个状态 U1 是由 S 状态花最小代价撤消一个进程所演变成的状态。 在撤消一个进程后,若系统仍处于死锁状态,则再从 U1 状态按照上述处理方式再依次地撤 消一个进程,得到 U1′ , U′2 , U′3 ,…, U′k 状态,再从 U′ 状态中选取一个代价最小的 U′j , 如此下去,直到死锁状态解除为止。为把系统从死锁状态中解脱出来,所花费的代价可表 示为: R(S)min = min{CUi} + min{CUj} + min{CUk} + … 习题 1. 高级调度与低级调度的主要任务是什么?为什么要引入中级调度? 2. 何谓作业、作业步和作业流? 3. 在什么情况下需要使用作业控制块 JCB?其中包含了哪些内容? 4. 在作业调度中应如何确定接纳多少个作业和接纳哪些作业? 5. 试说明低级调度的主要功能。 6. 在抢占调度方式中,抢占的原则是什么? 7. 在选择调度方式和调度算法时,应遵循的准则是什么? 8. 在批处理系统、分时系统和实时系统中,各采用哪几种进程(作业)调度算法? 第三章 处理机调度与死锁 ·115· 9. 何谓静态和动态优先级?确定静态优先级的依据是什么? 10. 试比较 FCFS 和 SPF 两种进程调度算法。 11. 在时间片轮转法中,应如何确定时间片的大小? 12. 通过一个例子来说明通常的优先级调度算法不能适用于实时系统? 13. 为什么说多级反馈队列调度算法能较好地满足各方面用户的需要? 14. 为什么在实时系统中,要求系统(尤其是 CPU)具有较强的处理能力? 15. 按调度方式可将实时调度算法分为哪几种? 16. 什么是最早截止时间优先调度算法?举例说明之。 17. 什么是最低松弛度优先调度算法?举例说明之。 18. 何谓死锁?产生死锁的原因和必要条件是什么? 19. 在解决死锁问题的几个方法中,哪种方法最易于实现?哪种方法使资源利用率 最高? 20. 请详细说明可通过哪些途径预防死锁。 21. 在银行家算法的例子中,如果 P0 发出的请求向量由 Request(0,2,0)改为 Request(0,1,0),问系统可否将资源分配给它? 22. 在银行家算法中,若出现下述资源分配情况: Process P0 P1 P2 P3 P4 Allocation 0032 1000 1354 0332 0014 Need 0012 1750 2356 0652 0656 Available 1622 试问: (1) 该状态是否安全? (2) 若进程 P2 提出请求 Request(1,2,2,2)后,系统能否将资源分配给它? ·116· 计算机操作系统 第四章 存 储 器 管 理 存储器是计算机系统的重要组成部分。近年来,存储器容量虽然一直在不断扩大,但 仍不能满足现代软件发展的需要,因此,存储器仍然是一种宝贵而又紧俏的资源。如何对 它加以有效的管理,不仅直接影响到存储器的利用率,而且还对系统性能有重大影响。存 储器管理的主要对象是内存。由于对外存的管理与对内存的管理相类似,只是它们的用途 不同,即外存主要用来存放文件,所以我们把对外存的管理放在文件管理一章介绍。 4.1 存储器的层次结构 在理想情况下存储器的速度应当非常快,能跟上处理机的速度,容量也非常大而且价 格还应很便宜。但目前无法同时满足这样三个条件。于是在现代计算机系统中,存储部件 通常是采用层次结构来组织的。 4.1.1 多级存储器结构 对于通用计算机而言,存储层次至少应具有三级:最高层为 CPU 寄存器,中间为主存, 最底层是辅存。在较高档的计算机中,还可以根据具体的功能分工细划为寄存器、高速缓 存、主存储器、磁盘缓存、固定磁盘、可移动存储介质等 6 层。如图 4-1 所示,在存储层次 中越往上,存储介质的访问速度越快,价格也越高,相对存储容量也越小。其中,寄存器、 高速缓存、主存储器和磁盘缓存均属于操作系统存储管理的管辖范畴,掉电后它们存储的 信息不再存在。固定磁盘和可移动存储介质属于设备管理的管辖范畴,它们存储的信息将 被长期保存。 CPU寄存器 主存 辅存 寄存器 高速缓存 主存 磁盘缓存 磁盘 可移动存储介质 图 4-1 计算机系统存储层次示意 在计算机系统存储层次中,寄存器和主存储器又被称为可执行存储器,存放于其中的 信息与存放于辅存中的信息相比较而言,计算机所采用的访问机制是不同的,所需耗费的 时间也是不同的。进程可以在很少的时钟周期内使用一条 load 或 store 指令对可执行存储器 第四章 存 储 器 管 理 ·117· 进行访问,但对辅存的访问则需要通过 I/O 设备来实现,因此,访问中将涉及到中断、设备 驱动程序以及物理设备的运行,所需耗费的时间远远高于对可执行存储器访问的时间,一 般相差 3 个数量级甚至更多。 对于不同层次的存储介质,由操作系统进行统一管理。操作系统的存储管理,负责对 可执行存储器的分配、回收以及提供在存储层次间数据移动的管理机制,例如主存与磁盘 缓存、高速缓存与主存间的数据移动等。在设备和文件管理中,根据用户的需求提供对辅 存的管理机制。本章主要讨论有关存储管理部分的问题,对于辅存部分,则放在以后的章 节中进行。 4.1.2 主存储器与寄存器 1.主存储器 主存储器(简称内存或主存)是计算机系统中一个主要部件,用于保存进程运行时的程序 和数据,也称可执行存储器,其容量对于当前的微机系统和大中型机,可能一般为数十 MB 到数 GB,而且容量还在不断增加,而嵌入式计算机系统一般仅有几十 KB 到几 MB。CPU 的控制部件只能从主存储器中取得指令和数据,数据能够从主存储器读取并将它们装入到 寄存器中,或者从寄存器存入到主存储器。CPU 与外围设备交换的信息一般也依托于主存 储器地址空间。由于主存储器的访问速度远低于 CPU 执行指令的速度,为缓和这一矛盾, 在计算机系统中引入了寄存器和高速缓存。 2.寄存器 寄存器访问速度最快,完全能与 CPU 协调工作,但价格却十分昂贵,因此容量不可能 做得很大。寄存器的长度一般以字(word)为单位。寄存器的数目,对于当前的微机系统和大 中型机,可能有几十个甚至上百个;而嵌入式计算机系统一般仅有几个到几十个。寄存器 用于加速存储器的访问速度,如用寄存器存放操作数,或用作地址寄存器加快地址转换速 度等。 4.1.3 高速缓存和磁盘缓存 1.高速缓存 高速缓存是现代计算机结构中的一个重要部件,其容量大于或远大于寄存器,而比内 存约小两到三个数量级左右,从几十 KB 到几 MB,访问速度快于主存储器。 根据程序执行的局部性原理(即程序在执行时将呈现出局部性规律,在一较短的时间内, 程序的执行仅局限于某个部分),将主存中一些经常访问的信息存放在高速缓存中,减少访 问主存储器的次数,可大幅度提高程序执行速度。通常,进程的程序和数据是存放在主存 储器中,每当使用时,被临时复制到一个速度较快的高速缓存中。当 CPU 访问一组特定信 息时,首先检查它是否在高速缓存中,如果已存在,可直接从中取出使用,以避免访问主 存,否则,再从主存中读出信息。如大多数计算机有指令高速缓存,用来暂存下一条欲执 行的指令,如果没有指令高速缓存,CPU 将会空等若干个周期,直到下一条指令从主存中 取出。由于高速缓存的速度越高价格也越贵,故有的计算机系统中设置了两级或多级高速 缓存。紧靠内存的一级高速缓存的速度最高,而容量最小,二级高速缓存的容量稍大,速 度也稍低。 ·118· 计算机操作系统 2.磁盘缓存 由于目前磁盘的 I/O 速度远低于对主存的访问速度,因此将频繁使用的一部分磁盘数据 和信息,暂时存放在磁盘缓存中,可减少访问磁盘的次数。磁盘缓存本身并不是一种实际 存在的存储介质,它依托于固定磁盘,提供对主存储器存储空间的扩充,即利用主存中的 存储空间,来暂存从磁盘中读出(或写入)的信息。主存也可以看做是辅存的高速缓存,因为, 辅存中的数据必须复制到主存方能使用;反之,数据也必须先存在主存中,才能输出到 辅存。 一个文件的数据可能出现在存储器层次的不同级别中,例如,一个文件数据通常被存 储在辅存中(如硬盘),当其需要运行或被访问时,就必须调入主存,也可以暂时存放在主存 的磁盘高速缓存中。大容量的辅存常常使用磁盘,磁盘数据经常备份到磁带或可移动磁盘 组上,以防止硬盘故障时丢失数据。有些系统自动地把老文件数据从辅存转储到海量存储 器中,如磁带上,这样做还能降低存储价格。 4.2 程序的装入和链接 在多道程序环境下,要使程序运行,必须先为之创建进程。而创建进程的第一件事, 便是将程序和数据装入内存。如何将一个用户源程序变为一个可在内存中执行的程序,通 常都要经过以下几个步骤:首先是要编译,由编译程序(Compiler)将用户源代码编译成若干 个目标模块(Object Module);其次是链接,由链接程序(Linker)将编译后形成的一组目标模 块,以及它们所需要的库函数链接在一起,形成一个完整的装入模块(Load Module);最后 是装入,由装入程序(Loader)将装入模块装入内存。图 4-2 示出了这样的三步过程。本节将 扼要阐述程序(含数据)的链接和装入过程。 内存 库 编译程序 产生的目 标模块 链接 程序 装入模块 装入 程序 … 第一步 第二步 图 4-2 对用户程序的处理步骤 第三步 4.2.1 程序的装入 为了阐述上的方便,我们先介绍一个无需进行链接的单个目标模块的装入过程。该目 标模块也就是装入模块。在将一个装入模块装入内存时,可以有绝对装入方式、可重定位 装入方式和动态运行时装入方式,下面分别简述之。 第四章 存 储 器 管 理 ·119· 1.绝对装入方式(Absolute Loading Mode) 在编译时,如果知道程序将驻留在内存的什么位置,那么,编译程序将产生绝对地址 的目标代码。例如,事先已知用户程序(进程)驻留在从 R 处开始的位置,则编译程序所产生 的目标模块(即装入模块)便从 R 处开始向上扩展。绝对装入程序按照装入模块中的地址,将 程序和数据装入内存。装入模块被装入内存后,由于程序中的逻辑地址与实际内存地址完 全相同,故不须对程序和数据的地址进行修改。 程序中所使用的绝对地址,既可在编译或汇编时给出,也可由程序员直接赋予。但在 由程序员直接给出绝对地址时,不仅要求程序员熟悉内存的使用情况,而且一旦程序或数 据被修改后,可能要改变程序中的所有地址。因此,通常是宁可在程序中采用符号地址, 然后在编译或汇编时,再将这些符号地址转换为绝对地址。 2.可重定位装入方式(Relocation Loading Mode) 绝对装入方式只能将目标模块装入到内存中事先指定的位置。在多道程序环境下,编 译程序不可能预知所编译的目标模块应放在内存的何处,因此,绝对装入方式只适用于单 道程序环境。在多道程序环境下,所得到的目标模块的起始地址通常是从 0 开始的,程序 中的其它地址也都是相对于起始地址计算的。此时应采用可重定位装入方式,根据内存的 当前情况,将装入模块装入到内存的适当位置。 值得注意的是,在采用可重定位装入程序将 装入模块装入内存后,会使装入模块中的所有逻 辑地址与实际装入内存的物理地址不同,图 4-3 示出了这一情况。例如,在用户程序的 1000 号 单元处有一条指令 LOAD 1,2500,该指令的功 能是将 2500 单元中的整数 365 取至寄存器 1。 但若将该用户程序装入到内存的 10000~15000 号单元而不进行地址变换,则在执行 11000 号单 0 1000 LOAD 1,2500 2500 365 10000 11000 LOAD 1,2500 12500 365 5000 作业地址空间 15000 元中的指令时,它将仍从 2500 号单元中把数据 内存空间 取至寄存器 1 而导致数据错误。由图 4-3 可见, 正确的方法应该是将取数指令中的地址 2500 修 图 4-3 作业装入内存时的情况 改成 12500,即把指令中的相对地址 2500 与本程序在内存中的起始地址 10000 相加,才得 到正确的物理地址 12500。除了数据地址应修改外,指令地址也须做同样的修改,即将指令 的相对地址 1000 与起始地址 10000 相加,得到绝对地址 11000。通常是把在装入时对目标 程序中指令和数据的修改过程称为重定位。又因为地址变换通常是在装入时一次完成的, 以后不再改变,故称为静态重定位。 3.动态运行时装入方式(Dynamic Run-time Loading) 可重定位装入方式可将装入模块装入到内存中任何允许的位置,故可用于多道程序环 境;但这种方式并不允许程序运行时在内存中移动位置。因为,程序在内存中的移动,意 味着它的物理位置发生了变化,这时必须对程序和数据的地址(是绝对地址)进行修改后方能 运行。然而,实际情况是,在运行过程中它在内存中的位置可能经常要改变,此时就应采 用动态运行时装入的方式。 ·120· 计算机操作系统 动态运行时的装入程序在把装入模块装入内存后,并不立即把装入模块中的相对地址 转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行。因此,装入内存 后的所有地址都仍是相对地址。为使地址转换不影响指令的执行速度,这种方式需要一个 重定位寄存器的支持,我们将在 4.3 节中做详细介绍。 4.2.2 程序的链接 源程序经过编译后,可得到一组目标模块,再利用链接程序将这组目标模块链接,形 成装入模块。根据链接时间的不同,可把链接分成如下三种: (1) 静态链接。在程序运行之前,先将各目标模块及它们所需的库函数,链接成一个完 整的装配模块,以后不再拆开。我们把这种事先进行链接的方式称为静态链接方式。 (2) 装入时动态链接。这是指将用户源程序编译后所得到的一组目标模块,在装入内存 时,采用边装入边链接的链接方式。 (3) 运行时动态链接。这是指对某些目标模块的链接,是在程序执行中需要该(目标)模 块时,才对它进行的链接。 1.静态链接方式(Static Linking) 我们通过一个例子来说明在实现静态链接时应解决的一些问题。在图 4-4(a)中示出了经 过编译后所得到的三个目标模块 A、B、C,它们的长度分别为 L、M 和 N。在模块 A 中有一 条语句 CALL B,用于调用模块 B。在模块 B 中有一条语句 CALL C,用于调用模块 C。B 和 C 都属于外部调用符号,在将这几个目标模块装配成一个装入模块时,须解决以下两个问题: (1) 对相对地址进行修改。在由编译程序所产生的所有目标模块中,使用的都是相对地 址,其起始地址都为 0,每个模块中的地址都是相对于起始地址计算的。在链接成一个装入 模块后,原模块 B 和 C 在装入模块的起始地址不再是 0,而分别是 L 和 L+M,所以此时须 修改模块 B 和 C 中的相对地址,即把原 B 中的所有相对地址都加上 L,把原 C 中的所有相 对地址都加上 L+M。 (2) 变换外部调用符号。将每个模块中所用的外部调用符号也都变换为相对地址,如把 B 的起始地址变换为 L,把 C 的起始地址变换为 L+M,如图 4-4(b)所示。这种先进行链接 所形成的一个完整的装入模块,又称为可执行文件。通常都不再拆开它,要运行时可直接 将它装入内存。这种事先进行链接,以后不再拆开的链接方式,称为静态链接方式。 0 模块 A CALL B; L-1 Return; 0 M-1 模块 B CALL C; Return; 0 模块 C 0 模块 A JSR“L” L-1 Return; L 模块 B JSR“L+M” L+M-1 Return; L+M 模块 C N-1 Return; L+M+-N1 Return; (a) 目标模块 (b) 装入模块 图 4-4 程序链接示意图 第四章 存 储 器 管 理 ·121· 2.装入时动态链接(Load-time Dynamic Linking) 用户源程序经编译后所得的目标模块,是在装入内存时边装入边链接的,即在装入一 个目标模块时,若发生一个外部模块调用事件,将引起装入程序去找出相应的外部目标模 块,并将它装入内存,还要按照图 4-4 所示的方式来修改目标模块中的相对地址。装入时动 态链接方式有以下优点: (1) 便于修改和更新。对于经静态链接装配在一起的装入模块,如果要修改或更新其中 的某个目标模块,则要求重新打开装入模块。这不仅是低效的,而且有时是不可能的。若 采用动态链接方式,由于各目标模块是分开存放的,所以要修改或更新各目标模块是件非 常容易的事。 (2) 便于实现对目标模块的共享。在采用静态链接方式时,每个应用模块都必须含有其 目标模块的拷贝,无法实现对目标模块的共享。但采用装入时动态链接方式,OS 则很容易 将一个目标模块链接到几个应用模块上,实现多个应用程序对该模块的共享。 3.运行时动态链接(Run-time Dynamic Linking) 在许多情况下,应用程序在运行时,每次要运行的模块可能是不相同的。但由于事先 无法知道本次要运行哪些模块,故只能是将所有可能要运行到的模块都全部装入内存,并 在装入时全部链接在一起。显然这是低效的,因为往往会有些目标模块根本就不运行。比 较典型的例子是作为错误处理用的目标模块,如果程序在整个运行过程中都不出现错误, 则显然就不会用到该模块。 近几年流行起来的运行时动态链接方式,是对上述在装入时链接方式的一种改进。这 种链接方式是将对某些模块的链接推迟到程序执行时才进行链接,亦即,在执行过程中, 当发现一个被调用模块尚未装入内存时,立即由 OS 去找到该模块并将之装入内存,把它链 接到调用者模块上。凡在执行过程中未被用到的目标模块,都不会被调入内存和被链接到 装入模块上,这样不仅可加快程序的装入过程,而且可节省大量的内存空间。 4.3 连续分配方式 连续分配方式,是指为一个用户程序分配一个连续的内存空间。这种分配方式曾被广 泛应用于 20 世纪 60~70 年代的 OS 中,它至今仍在内存分配方式中占有一席之地;又可把 连续分配方式进一步分为单一连续分配、固定分区分配、动态分区分配以及动态重定位分 区分配四种方式。 4.3.1 单一连续分配 这是最简单的一种存储管理方式,但只能用于单用户、单任务的操作系统中。采用这 种存储管理方式时,可把内存分为系统区和用户区两部分,系统区仅提供给 OS 使用,通常 是放在内存的低址部分;用户区是指除系统区以外的全部内存空间,提供给用户使用。 虽然在早期的单用户、单任务操作系统中,有不少都配置了存储器保护机构,用于防 止用户程序对操作系统的破坏,但近年来常见的几种单用户操作系统中,如 CP/M、MS-DOS 及 RT11 等,都未采取存储器保护措施。这是因为,一方面可以节省硬件,另一方面也因为 ·122· 计算机操作系统 这是可行的。在单用户环境下,机器由一用户独占,不可能存在其他用户干扰的问题;这 时可能出现的破坏行为也只是用户程序自己去破坏操作系统,其后果并不严重,只是会影 响该用户程序的运行,且操作系统也很容易通过系统的再启动而重新装入内存。 4.3.2 固定分区分配 固定分区式分配是最简单的一种可运行多道程序的存储管理方式。这是将内存用户空 间划分为若干个固定大小的区域,在每个分区中只装入一道作业,这样,把用户空间划分 为几个分区,便允许有几道作业并发运行。当有一空闲分区时,便可以再从外存的后备作 业队列中选择一个适当大小的作业装入该分区,当该作业结束时,又可再从后备作业队列 中找出另一作业调入该分区。 1.划分分区的方法 可用下述两种方法将内存的用户空间划分为若干个固定大小的分区: (1) 分区大小相等,即使所有的内存分区大小相等。其缺点是缺乏灵活性,即当程序太 小时,会造成内存空间的浪费;当程序太大时,一个分区又不足以装入该程序,致使该程 序无法运行。尽管如此,这种划分方式仍被用于利用一台计算机去控制多个相同对象的场 合,因为这些对象所需的内存空间是大小相等的。例如,炉温群控系统,就是利用一台计 算机去控制多台相同的冶炼炉。 (2) 分区大小不等。为了克服分区大小相等而缺乏灵活性的这个缺点,可把内存区划分 成含有多个较小的分区、适量的中等分区及少量的大分区。这样,便可根据程序的大小为 之分配适当的分区。 2.内存分配 为了便于内存分配,通常将分区按大小进行排队,并为之建立一张分区使用表,其中 各表项包括每个分区的起始地址、大小及状态(是否已分配),见图 4-5(a)所示。当有一用户 程序要装入时,由内存分配程序检索该表,从中找出一个能满足要求的、尚未分配的分区, 将之分配给该程序,然后将该表项中的状态置为“已分配”;若未找到大小足够的分区,则 拒绝为该用户程序分配内存。存储空间分配情况如图 4-5(b)所示。 分区号 1 2 3 大小/KB 起址/KB 12 20 32 32 64 64 状态 已分配 已分配 已分配 24 KB 32 KB 64 KB 操作系统 作业A 作业B 作业C 4 128 128 未分配 128 KB 256 KB (a) 分区说明表 (b) 存储空间分配情况 图 4-5 固定分区使用表 固定分区分配,是最早的多道程序的存储管理方式,用于 60 年代的 IBM360 的 MFT 操作系统中。由于每个分区的大小固定,必然会造成存储空间的浪费,因而现在已很少将 它用于通用的计算机中;但在某些用于控制多个相同对象的控制系统中,由于每个对象的 第四章 存 储 器 管 理 ·123· 控制程序大小相同,是事先已编好的,其所需的数据也是一定的,故仍采用固定分区式存 储管理方式。 4.3.3 动态分区分配 动态分区分配是根据进程的实际需要,动态地为之分配内存空间。在实现可变分区分 配时,将涉及到分区分配中所用的数据结构、分区分配算法和分区的分配与回收操作这样 三个问题。 1.分区分配中的数据结构 为了实现分区分配,系统中必须配置相应的数据结构,用来描述空闲分区和已分配分 区的情况,为分配提供依据。常用的数据结构有以下两种形式: (1) 空闲分区表。在系统中设置一张空闲分区表,用于记录每个空闲分区的情况。每个 空闲分区占一个表目,表目中包括分区序号、分 区始址及分区的大小等数据项。 (2) 空闲分区链。为了实现对空闲分区的分配 和链接,在每个分区的起始部分,设置一些用于 控制分区分配的信息,以及用于链接各分区所用 的前向指针;在分区尾部则设置一后向指针,通 过前、后向链接指针,可将所有的空闲分区链接 前 后 向 向 指 指 针 针 N个字节可用 N+2 N+2 成一个双向链,如图 4-6 所示。为了检索方便,在 分区尾部重复设置状态位和分区大小表目。当分 0 0 区被分配出去以后,把状态位由“0”改为“1”, 此时,前、后向指针已无意义。 图 4-6 空闲链结构 2.分区分配算法 为把一个新作业装入内存,须按照一定的分配算法,从空闲分区表或空闲分区链中选 出一分区分配给该作业。目前常用以下所述的五种分配算法。 1) 首次适应算法(first fit) 我们以空闲分区链为例来说明采用 FF 算法时的分配情况。FF 算法要求空闲分区链以 地址递增的次序链接。在分配内存时,从链首开始顺序查找,直至找到一个大小能满足要 求的空闲分区为止;然后再按照作业的大小,从该分区中划出一块内存空间分配给请求者, 余下的空闲分区仍留在空闲链中。若从链首直至链尾都不能找到一个能满足要求的分区, 则此次内存分配失败,返回。该算法倾向于优先利用内存中低址部分的空闲分区,从而保 留了高址部分的大空闲区。这给为以后到达的大作业分配大的内存空间创造了条件。其缺 点是低址部分不断被划分,会留下许多难以利用的、很小的空闲分区,而每次查找又都是 从低址部分开始,这无疑会增加查找可用空闲分区时的开销。 2) 循环首次适应算法(next fit) 该算法是由首次适应算法演变而成的。在为进程分配内存空间时,不再是每次都从链 首开始查找,而是从上次找到的空闲分区的下一个空闲分区开始查找,直至找到一个能满 足要求的空闲分区,从中划出一块与请求大小相等的内存空间分配给作业。为实现该算法, ·124· 计算机操作系统 应设置一起始查寻指针,用于指示下一次起始查寻的空闲分区,并采用循环查找方式,即 如果最后一个(链尾)空闲分区的大小仍不能满足要求,则应返回到第一个空闲分区,比较其 大小是否满足要求。找到后,应调整起始查寻指针。该算法能使内存中的空闲分区分布得 更均匀,从而减少了查找空闲分区时的开销,但这样会缺乏大的空闲分区。 3) 最佳适应算法(best fit) 所谓“最佳”是指每次为作业分配内存时,总是把能满足要求、又是最小的空闲分区 分配给作业,避免“大材小用”。为了加速寻找,该算法要求将所有的空闲分区按其容量以 从小到大的顺序形成一空闲分区链。这样,第一次找到的能满足要求的空闲区,必然是最 佳的。孤立地看,最佳适应算法似乎是最佳的,然而在宏观上却不一定。因为每次分配后 所切割下来的剩余部分总是最小的,这样,在存储器中会留下许多难以利用的小空闲区。 4) 最坏适应算法(worst fit) 最坏适应分配算法要扫描整个空闲分区表或链表,总是挑选一个最大的空闲区分割给 作业使用,其优点是可使剩下的空闲区不至于太小,产生碎片的几率最小,对中、小作业 有利,同时最坏适应分配算法查找效率很高。该算法要求将所有的空闲分区按其容量以从 大到小的顺序形成一空闲分区链,查找时只要看第一个分区能否满足作业要求。但是该算 法的缺点也是明显的,它会使存储器中缺乏大的空闲分区。最坏适应算法与前面所述的首 次适应算法、循环首次适应算法、最佳适应算法一起,也称为顺序搜索法。 5) 快速适应算法(quick fit) 该算法又称为分类搜索法,是将空闲分区根据其容量大小进行分类,对于每一类具有 相同容量的所有空闲分区,单独设立一个空闲分区链表,这样,系统中存在多个空闲分区 链表,同时在内存中设立一张管理索引表,该表的每一个表项对应了一种空闲分区类型, 并记录了该类型空闲分区链表表头的指针。空闲分区的分类是根据进程常用的空间大小进 行划分,如 2 KB、4 KB、8 KB 等,对于其它大小的分区,如 7 KB 这样的空闲区,既可以 放在 8 KB 的链表中,也可以放在一个特殊的空闲区链表中。 该算法的优点是查找效率高,仅需要根据进程的长度,寻找到能容纳它的最小空闲区 链表,并取下第一块进行分配即可。另外该算法在进行空闲分区分配时,不会对任何分区 产生分割,所以能保留大的分区,满足对大空间的需求,也不会产生内存碎片。 该算法的缺点是在分区归还主存时算法复杂,系统开销较大。此外,该算法在分配空 闲分区时是以进程为单位,一个分区只属于一个进程,因此在为进程所分配的一个分区中, 或多或少地存在一定的浪费。空闲分区划分越细,浪费则越严重,整体上会造成可观的存 储空间浪费,这是典型的以空间换时间的作法。 3.分区分配操作 在动态分区存储管理方式中,主要的操作是分配内存和回收内存。 1) 分配内存 系统应利用某种分配算法,从空闲分区链(表)中找到所需大小的分区。设请求的分区大 小为 u.size,表中每个空闲分区的大小可表示为 m.size。若 m.size-u.size≤size(size 是事先规 定的不再切割的剩余分区的大小),说明多余部分太小,可不再切割,将整个分区分配给请 求者;否则(即多余部分超过 size),从该分区中按请求的大小划分出一块内存空间分配出去, 第四章 存 储 器 管 理 ·125· 余下的部分仍留在空闲分区链(表)中。然后,将分配区的首址返回给调用者。图 4-7 示出了 分配流程。 从头开始查表 检索完否? Y 返回 N m.size>u.size? N 继续检索下一个表项 Y m.siz-eu.size≤size? N 从该分区中划出 u.siz大e 小的分区 Y 将该分区从链中移出 将该分区分配给请求者 修改有关数据结构 返回 图 4-7 内存分配流程 2) 回收内存 当进程运行完毕释放内存时,系统根据回收区的首址,从空闲区链(表)中找到相应的插 入点,此时可能出现以下四种情况之一: (1) 回收区与插入点的前一个空闲分区 F1 相邻接,见图 4-8(a)。此时应将回收区与插入 点的前一分区合并,不必为回收分区分配新表项,而只需修改其前一分区 F1 的大小。 (2) 回收分区与插入点的后一空闲分区 F2 相邻接,见图 4-8(b)。此时也可将两分区合并, 形成新的空闲分区,但用回收区的首址作为新空闲区的首址,大小为两者之和。 (3) 回收区同时与插入点的前、后两个分区邻接,见图 4-8(c)。此时将三个分区合并, 使用 F1 的表项和 F1 的首址,取消 F2 的表项,大小为三者之和。 (4) 回收区既不与 F1 邻接,又不与 F2 邻接。这时应为回收区单独建立一新表项,填写 回收区的首址和大小,并根据其首址插入到空闲链中的适当位置。 … F1 回收区 … F2 回收区 … F3 回收区 … (a) … … (b) (c) 图 4-8 内存回收时的情况 ·126· 计算机操作系统 4.3.4 伙伴系统 固定分区和动态分区方式都有不足之处。固定分区方式限制了活动进程的数目,当进 程大小与空闲分区大小不匹配时,内存空间利用率很低。动态分区方式算法复杂,回收空 闲分区时需要进行分区合并等,系统开销较大。伙伴系统方式是对以上两种内存方式的一 种折衷方案。 伙伴系统规定,无论已分配分区或空闲分区,其大小均为 2 的 k 次幂,k 为整数, l≤k≤m,其中:21 表示分配的最小分区的大小,2m 表示分配的最大分区的大小,通常 2m 是整个可分配内存的大小。 假设系统的可利用空间容量为 2m 个字,则系统开始运行时,整个内存区是一个大小为 2m 的空闲分区。在系统运行过程中,由于不断的划分,可能会形成若干个不连续的空闲分区, 将这些空闲分区根据分区的大小进行分类,对于每一类具有相同大小的所有空闲分区,单独 设立一个空闲分区双向链表。这样,不同大小的空闲分区形成了 k(0≤k≤m)个空闲分区链表。 当需要为进程分配一个长度为 n 的存储空间时,首先计算一个 i 值,使 2i-1TL,表示段号太大,是访问越界,于是产生越界中断信号;若未越界, 则根据段表的始址和该段的段号,计算出该段对应段表项的位置,从中读出该段在内存的 起始地址,然后,再检查段内地址 d 是否超过该段的段长 SL。若超过,即 d>SL,同样发出 越界中断信号;若未越界,则将该段的基址 d 与段内地址相加,即可得到要访问的内存物 理地址。 图 4-18 示出了分段系统的地址变换过程。 ·138· 计算机操作系统 控制寄存器 段表始址 段表长度 越界 段号S 位移量W > 2 100 有效地址 + 段号 段长 基址 0 1K 6K 1 600 4 K 2 500 8 K 3 200 9200 + 8292 物理地址 8K 8292 8692 主存 图 4-18 分段系统的地址变换过程 像分页系统一样,当段表放在内存中时,每要访问一个数据,都须访问两次内存,从 而极大地降低了计算机的速率。解决的方法也和分页系统类似,再增设一个联想存储器, 用于保存最近常用的段表项。由于一般情况是段比页大,因而段表项的数目比页表项的数 目少,其所需的联想存储器也相对较小,便可以显著地减少存取数据的时间,比起没有地 址变换的常规存储器的存取速度来仅慢约 10%~15%。 4.分页和分段的主要区别 由上所述不难看出,分页和分段系统有许多相似之处。比如,两者都采用离散分配方 式,且都要通过地址映射机构来实现地址变换。但在概念上两者完全不同,主要表现在下 述三个方面。 (1) 页是信息的物理单位,分页是为实现离散分配方式,以消减内存的外零头,提高内 存的利用率。或者说,分页仅仅是由于系统管理的需要而不是用户的需要。段则是信息的 逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了能更好地满足用户的 需要。 (2) 页的大小固定且由系统决定,由系统把逻辑地址划分为页号和页内地址两部分,是 由机器硬件实现的,因而在系统中只能有一种大小的页面;而段的长度却不固定,决定于 用户所编写的程序,通常由编译程序在对源程序进行编译时,根据信息的性质来划分。 (3) 分页的作业地址空间是一维的,即单一的线性地址空间,程序员只需利用一个记忆 符,即可表示一个地址;而分段的作业地址空间则是二维的,程序员在标识一个地址时, 既需给出段名,又需给出段内地址。 4.5.3 信息共享 分段系统的一个突出优点,是易于实现段的共享,即允许若干个进程共享一个或多个 分段,且对段的保护也十分简单易行。在分页系统中,虽然也能实现程序和数据的共享, 但远不如分段系统来得方便。我们通过一个例子来说明这个问题。例如,有一个多用户系 统,可同时接纳 40 个用户,他们都执行一个文本编辑程序(Text Editor)。如果文本编辑程序 第四章 存 储 器 管 理 ·139· 有 160 KB 的代码和另外 40 KB 的数据区,则总共需有 8 MB 的内存空间来支持 40 个用户。 如果 160 KB 的代码是可重入的(Reentrant),则无论是在分页系统还是在分段系统中,该代 码都能被共享,在内存中只需保留一份文本编辑程序的副本,此时所需的内存空间仅为 1760 KB(40×40+160),而不是 8000 KB。假定每个页面的大小为 4 KB,那么,160 KB 的代 码将占用 40 个页面,数据区占 10 个页面。为实现代码的共享,应在每个进程的页表中都 建立 40 个页表项,它们的物理块号都是 21#~60#。在每个进程的页表中,还须为自己的数 据区建立页表项,它们的物理块号分别是 61#~70#、71#~80#、81#~90#,…,等等。图 4-19 是分页系统中共享 editor 的示意图。 … … … 进程1 ed 1 ed 2 ed 40 data 1 data 10 进程2 ed 1 ed 2 ed 40 data 1 … … … 页表 21 22 60 61 70 页表 21 22 60 71 … … … … 主存 0 ed 1 21 ed 2 22 ed 40 60 data 1 61 data 10 70 data 1 71 data 10 80 … … data 10 80 图 4-19 分页系统中共享 editor 的示意图 在分段系统中,实现共享则容易得多,只需在每个进程的段表中为文本编辑程序设置 一个段表项。图 4-20 是分段系统中共享 editor 的示意图。 进程1 editor data 1 进程2 editor data 2 段表 段长 基址 160 80 40 240 160 80 40 380 … 80 editor 240 data 1 280 380 data 2 420 图 4-20 分段系统中共享 editor 的示意图 可重入代码(Reentrant Code)又称为“纯代码”(Pure Code),是一种允许多个进程同时访 问的代码。为使各个进程所执行的代码完全相同,绝对不允许可重入代码在执行中有任何 改变。因此,可重入代码是一种不允许任何进程对它进行修改的代码。但事实上,大多数 代码在执行时都可能有些改变,例如,用于控制程序执行次数的变量以及指针、信号量及 数组等。为此,在每个进程中,都必须配以局部数据区,把在执行中可能改变的部分拷贝 到该数据区,这样,程序在执行时,只需对该数据区(属于该进程私有)中的内容进行修改, 并不去改变共享的代码,这时的可共享代码即成为可重入码。 ·140· 计算机操作系统 4.5.4 段页式存储管理方式 前面所介绍的分页和分段存储管理方式都各有其优缺点。分页系统能有效地提高内存 利用率,而分段系统则能很好地满足用户需要。如果能对两种存储管理方式“各取所长”, 则可以将两者结合成一种新的存储管理方式系统。这种新系统既具有分段系统的便于实现、 分段可共享、易于保护、可动态链接等一系列优点,又能像分页系统那样很好地解决内存 的外部碎片问题,以及可为各个分段离散地分配内存等问题。把这种结合起来形成的新系 统称为“段页式系统”。 1.基本原理 段页式系统的基本原理,是分段和分页原理的结合,即先将用户程序分成若干个段, 再把每个段分成若干个页,并为每一个段赋予一个段名。图 4-21 示出了一个作业地址空间 的结构。该作业有三个段,页面大小为 4 KB。在段页式系统中,其地址结构由段号、段内 页号及页内地址三部分所组成,如图 4-22 所示。 0 4K 8K 12K 15K 16K 主程序段 子程序段 0 4K 8K 数据段 0 4K 8K 10K 12K (a) 段号(S) 段内页号(P) 页内地址(W) (b) 图 4-21 作业地址空间和地址结构 段表寄存器 段表大小 段表始址 段号 0 1 2 3 4 状态 1 1 1 0 1 页表大小 页表始址 页号 状态 01 11 21 30 41 存储块# 操作系统 段表 页表 主存 图 4-22 利用段表和页表实现地址映射 2.地址变换过程 在段页式系统中,为了便于实现地址变换,须配置一个段表寄存器,其中存放段表始 址和段表长 TL。进行地址变换时,首先利用段号 S,将它与段表长 TL 进行比较。若 SC 时,系统对每一块 数据的处理时间为 M+T,反之则为 M+C,故可把系统对每一块数据的处理时间表示为 Max(C,T)+M。 第五章 设 备 管 理 ·173· 用户进程 (a) 处理(C) 工作区 传送(M) 缓冲区 输入(T) I/O设备 T1 T2 T3 T4 (b) M1 M2 M3 C1 C2 C3 t 图 5-11 单缓冲工作示意图 在字符设备输入时,缓冲区用于暂存用户输入的一行数据,在输入期间,用户进程被 挂起以等待数据输入完毕;在输出时,用户进程将一行数据输入到缓冲区后,继续进行处 理。当用户进程已有第二行数据输出时,如果第一行数据尚未被提取完毕,则此时用户进 程应阻塞。 2.双缓冲(Double Buffer) 为了加快输入和输出速度,提高设备利用率,人们又引入了双缓冲区机制,也称为 缓冲对换(Buffer Swapping)。在设备输入时,先将数据送入第一缓冲区,装满后便转向第 二缓冲区。此时操作系统可以从第一缓冲区中移出数据,并送入用户进程(见图 5-12)。接 着由 CPU 对数据进行计算。在双缓冲时,系统处理一块数据的时间可以粗略地认为是 Max(C,T)。如果 CT,则可使 CPU 不必等待设备输入。 对于字符设备,若采用行输入方式,则采用双缓冲通常能消除用户的等待时间,即用户在 输入完第一行之后,在 CPU 执行第一行中的命令时,用户可继续向第二缓冲区输入下一行 数据。 用户进程 缓冲区1 (a) 工作区 缓冲区2 I/O设备 T1(缓冲1) T2(缓冲2) T3(缓冲3) T4(缓冲4) (b) M1 M2 M3 M4 C1 C2 C3 C4 图 5-12 双缓冲工作示意图 ·174· 计算机操作系统 如果我们在实现两台机器之间的通信时,仅为它们配置了单缓冲,如图 5-13(a)所示, 那么,它们之间在任一时刻都只能实现单方向的数据传输。例如,只允许把数据从 A 机传 送到 B 机,或者从 B 机传送到 A 机,而绝不允许双方同时向对方发送数据。为了实现双向 数据传输,必须在两台机器中都设置两个缓冲区,一个用作发送缓冲区,另一个用作接收 缓冲区,如图 5-13(b)所示。 A机 B机 A机 B机 发送 缓冲区 接收 缓冲区 缓冲区 缓冲区 接收 缓冲区 发送 缓冲区 (a) 单缓冲 (b) 双缓冲 图 5-13 双机通信时缓冲区的设置 5.3.3 循环缓冲 当输入与输出或生产者与消费者的速度基本相匹配时,采用双缓冲能获得较好的效果, 可使生产者和消费者基本上能并行操作。但若两者的速度相差甚远,双缓冲的效果则不够 理想,不过可以随着缓冲区数量的增加,使情况有所改善。因此,又引入了多缓冲机制。 可将多个缓冲组织成循环缓冲形式。对于用作输入的循环缓冲,通常是提供给输入进程或 计算进程使用,输入进程不断向空缓冲区输入数据,而计算进程则从中提取数据进行计算。 1.循环缓冲的组成 (1) 多个缓冲区。在循环缓冲中包括多个缓冲区,其每个缓冲区的大小相同。作为输入 的多缓冲区可分为三种类型:用于装输入数据的空缓冲区 R、已装满数据的缓冲区 G 以及 计算进程正在使用的现行工作缓冲区 C,如图 5-14 所示。 G6 Nexti R 1 2R G6 Nexti R 1 2R G5 3G 4 Nextg G G5 3C 4 G Nextg Current 图 5-14 循环缓冲 (2) 多个指针。作为输入的缓冲区可设置三个指针:用于指示计算进程下一个可用缓冲 区 G 的指针 Nextg、指示输入进程下次可用的空缓冲区 R 的指针 Nexti,以及用于指示计算 进程正在使用的缓冲区 C 的指针 Current。 2.循环缓冲区的使用 计算进程和输入进程可利用下述两个过程来使用循环缓冲区。 第五章 设 备 管 理 ·175· (1) Getbuf 过程。当计算进程要使用缓冲区中的数据时,可调用 Getbuf 过程。该过程 将由指针 Nextg 所指示的缓冲区提供给进程使用,相应地,须把它改为现行工作缓冲区, 并令 Current 指针指向该缓冲区的第一个单元,同时将 Nextg 移向下一个 G 缓冲区。类似地, 每当输入进程要使用空缓冲区来装入数据时,也调用 Getbuf 过程,由该过程将指针 Nexti 所指示的缓冲区提供给输入进程使用,同时将 Nexti 指针移向下一个 R 缓冲区。 (2) Releasebuf 过程。当计算进程把 C 缓冲区中的数据提取完毕时,便调用 Releasebuf 过程,将缓冲区 C 释放。此时,把该缓冲区由当前(现行)工作缓冲区 C 改为空缓冲区 R。类 似地,当输入进程把缓冲区装满时,也应调用 Releasebuf 过程,将该缓冲区释放,并改为 G 缓冲区。 3.进程同步 使用输入循环缓冲,可使输入进程和计算进程并行执行。相应地,指针 Nexti 和指针 Nextg 将不断地沿着顺时针方向移动,这样就可能出现下述两种情况: (1) Nexti 指针追赶上 Nextg 指针。这意味着输入进程输入数据的速度大于计算进程处 理数据的速度,已把全部可用的空缓冲区装满,再无缓冲区可用。此时,输入进程应阻塞, 直到计算进程把某个缓冲区中的数据全部提取完,使之成为空缓冲区 R,并调用 Releasebuf 过程将它释放时,才将输入进程唤醒。这种情况被称为系统受计算限制。 (2) Nextg 指针追赶上 Nexti 指针。这意味着输入数据的速度低于计算进程处理数据的 速度,使全部装有输入数据的缓冲区都被抽空,再无装有数据的缓冲区供计算进程提取数 据。这时,计算进程只能阻塞,直至输入进程又装满某个缓冲区,并调用 Releasebuf 过程 将它释放时,才去唤醒计算进程。这种情况被称为系统受 I/O 限制。 5.3.4 缓冲池 上述的缓冲区仅适用于某特定的 I/O 进程和计算进程,因而它们属于专用缓冲。当系统 较大时,将会有许多这样的循环缓冲,这不仅要消耗大量的内存空间,而且其利用率不高。 为了提高缓冲区的利用率,目前广泛流行公用缓冲池(Buffer Pool),在池中设置了多个可供 若干个进程共享的缓冲区。 1.缓冲池的组成 对于既可用于输入又可用于输出的公用缓冲池,其中至少应含有以下三种类型的缓 冲区: ① 空(闲)缓冲区; ② 装满输入数据的缓冲区; ③ 装满输出数据的缓冲区。 为了管理上的方便,可将相同类型的缓冲区链成一个队列,于是可形成以下三个队列: (1) 空缓冲队列 emq。这是由空缓冲区所链成的队列。其队首指针 F(emq)和队尾指针 L(emq)分别指向该队列的首缓冲区和尾缓冲区。 (2) 输入队列 inq。这是由装满输入数据的缓冲区所链成的队列。其队首指针 F(inq)和 队尾指针 L(inq)分别指向该队列的首缓冲区和尾缓冲区。 (3) 输出队列 outq。这是由装满输出数据的缓冲区所链成的队列。其队首指针 F(outq) ·176· 计算机操作系统 和队尾指针 L(outq)分别指向该队列的首缓冲区和尾缓冲区。 除了上述三个队列外,还应具有四种工作缓冲区:① 用于收容输入数据的工作缓冲区; ② 用于提取输入数据的工作缓冲区;③ 用于收容输出数据的工作缓冲区;④ 用于提取输 出数据的工作缓冲区。 2.Getbuf 过程和 Putbuf 过程 在“数据结构”课程中,曾介绍过队列和对队列进行操作的两个过程,它们是: (1) Addbuf(type,number)过程。该过程用于将由参数 number 所指示的缓冲区 B 挂在 type 队列上。 (2) Takebuf(type)过程。该过程用于从 type 所指示的队列的队首摘下一个缓冲区。 这两个过程能否用于对缓冲池中的队列进行操作呢?答案是否定的。因为缓冲池中的 队列本身是临界资源,多个进程在访问一个队列时,既应互斥,又须同步。为此,需要对 这两个过程加以改造,以形成可用于对缓冲池中的队列进行操作的 Getbuf 和 Putbuf 过程。 为使诸进程能互斥地访问缓冲池队列,可为每一队列设置一个互斥信号量 MS(type)。 此外,为了保证诸进程同步地使用缓冲区,又为每个缓冲队列设置了一个资源信号量 RS(type)。既可实现互斥又可保证同步的 Getbuf 过程和 Putbuf 过程描述如下: Procedure Getbuf(type) begin Wait(RS(type)); Wait(MS(type)); B(number):=Takebuf(type); Signal(MS(type)); end Procedure Putbuf(type,number) begin Wait(MS(type)); Addbuf(type,number); Signal(MS(type)); Signal(RS(type)); end 3.缓冲区的工作方式 缓冲区可以工作在收容输入、提取输入、收容输出和提取输出四种工作方式下,如图 5-15 所示。 缓冲池 收容输入 提取输出 hin sout sin hout 提取输入 用 户 收容输出 程 序 图 5-15 缓冲区的工作方式 第五章 设 备 管 理 ·177· (1) 收容输入。在输入进程需要输入数据时,便调用 Getbuf(emq)过程,从空缓冲队列 emq 的队首摘下一空缓冲区,把它作为收容输入工作缓冲区 hin。然后,把数据输入其中, 装满后再调用 Putbuf(inq,hin)过程,将该缓冲区挂在输入队列 inq 上。 (2) 提取输入。当计算进程需要输入数据时,调用 Getbuf(inq)过程,从输入队列 inq 的 队首取得一个缓冲区,作为提取输入工作缓冲区(sin),计算进程从中提取数据。计算进程用 完该数据后,再调用 Putbuf(emq,sin)过程,将该缓冲区挂到空缓冲队列 emq 上。 (3) 收容输出。当计算进程需要输出时,调用 Getbuf(emq)过程从空缓冲队列 emq 的队 首取得一个空缓冲区,作为收容输出工作缓冲区 hout。当其中装满输出数据后,又调用 Putbuf(outq,hout)过程,将该缓冲区挂在 outq 末尾。 (4) 提取输出。由输出进程调用 Getbuf(outq)过程,从输出队列的队首取得一装满输出 数据的缓冲区,作为提取输出工作缓冲区 sout。在数据提取完后,再调用 Putbuf(emq,sout) 过程,将该缓冲区挂在空缓冲队列末尾。 5.4 I/O 软 件 I/O 软件的总体设计目标是高效率和通用性。前者是要确保 I/O 设备与 CPU 的并发 性,以提高资源的利用率;后者则是指尽可能地提供简单抽象、清晰而统一的接口,采用 统一标准的方法,来管理所有的设备以及所需的 I/O 操作。为了达到这一目标,通常将 I/O 软件组织成一种层次结构,低层软件用于实现与硬件相关的操作,并可屏蔽硬件的具体细 节,高层软件则主要向用户提供一个简洁、友好和规范的接口。每一层具有一个要执行的 定义明确的功能和一个与邻近层次定义明确的接口,各层的功能与接口随系统的不同 而异。 5.4.1 I/O 软件的设计目标和原则 计算机系统中包含了众多的 I/O 设备,其种类繁多,硬件构造复杂,物理特性各异,速 度慢,与 CPU 速度不匹配,并涉及到大量专用 CPU 及数字逻辑运算等细节,如寄存器、中 断、控制字符和设备字符集等,造成对设备的操作和管理非常复杂和琐碎。因此,从系统 的观点出发,采用多种技术和措施,解决由于外部设备与 CPU 速度不匹配所引起的问题, 提高主机和外设的并行工作能力,提高系统效率,成为操作系统的一个重要目标。另一方 面,对设备的操作和管理的复杂性,也给用户的使用带来了极大的困难。用户必须掌握 I/O 系统的原理,对接口和控制器及设备的物理特性要有深入了解,这就使计算机的推广应用 受到很大限制。所以,设法消除或屏蔽设备硬件内部的低级处理过程,为用户提供一个简 便、易用、抽象的逻辑设备接口,保证用户安全、方便地使用各类设备,也是 I/O 软件设计 的一个重要原则。 具体而言,I/O 软件应达到下面的几个目标: 1) 与具体设备无关 对于 I/O 系统中许多种类不同的设备,作为程序员,只需要知道如何使用这些资源来完 成所需要的操作,而无需了解设备的有关具体实现细节。例如,应用程序访问文件时,不 ·178· 计算机操作系统 必去考虑被访问的是硬盘、软盘还是 CD-ROM;对于管理软件,也无需因为 I/O 设备变化, 而重新编写涉及设备管理的程序。 为了提高 OS 的可移植性和易适应性,I/O 软件应负责屏蔽设备的具体细节,向高层软 件提供抽象的逻辑设备,并完成逻辑设备与具体物理设备的映射。 对于操作系统本身而言,应允许在不需要将整个操作系统进行重新编译的情况下,增 添新的设备驱动程序,以方便新的 I/O 设备的安装。如在 Windows 中,系统可以为新 I/O 设备自动安装和寻找驱动程序,从而实现即插即用。 2) 统一命名 要实现上述的设备无关性,其中一项重要的工作就是如何给 I/O 设备命名。不同的操作 系统有不同的命名规则,一般而言,是在系统中对各类设备采取预先设计的、统一的逻辑 名称进行命名,所有软件都以逻辑名称访问设备。这种统一命名与具体设备无关,换言之, 同一个逻辑设备的名称,在不同的情况下可能对应于不同的物理设备。 3) 对错误的处理 一般而言,错误多数是与设备紧密相关的,因此对于错误的处理,应该尽可能在接近 硬件的层面处理,在低层软件能够解决的错误就不让高层软件感知,只有低层软件解决不 了的错误才通知高层软件解决。许多情况下,错误恢复可以在低层得到解决,而高层软件 不需要知道。 4) 缓冲技术 由于 CPU 与设备之间的速度差异,无论是块设备还是字符设备,都需要使用缓冲技术。 对于不同类型的设备,其缓冲区(块)的大小是不一样的,块设备的缓冲是以数据块为单位的, 而字符设备的缓冲则以字节为单位。就是同类型的设备,其缓冲区(块)的大小也是存在差异 的,如不同的磁盘,其扇区的大小有可能不同。因此,I/O 软件应能屏蔽这种差异,向高层 软件提供统一大小的数据块或字符单元,使得高层软件能够只与逻辑块大小一致的抽象设 备进行交互。 5) 设备的分配和释放 对于系统中的共享设备,如磁盘等,可以同时为多个用户服务。对于这样的设备,应 该允许多个进程同时对其提出 I/O 请求。但对于独占设备,如键盘和打印机等,在某一段时 间只能供一个用户使用,对其分配和释放的不当,将引起混乱,甚至死锁。对于独占设备 和共享设备带来的许多问题,I/O 软件必须能够同时进行妥善的解决。 6) I/O 控制方式 针对具有不同传输速率的设备,综合系统效率和系统代价等因素,合理选择 I/O 控制方 式,如像打印机等低速设备应采用中断驱动方式,而对磁盘等高速设备则采用 DMA 控制方 式等,以提高系统的利用率。为方便用户,I/O 软件也应屏蔽这种差异,向高层软件提供统 一的操作接口。 综上所述,I/O 软件涉及的面非常宽,往下与硬件有着密切的关系,往上又与用户直接 交互,它与进程管理、存储器、文件管理等都存在着一定的联系,即它们都可能需要 I/O 软 件来实现 I/O 操作。为使十分复杂的 I/O 软件能具有清晰的结构,更好的可移植性和易适应 性,目前在 I/O 软件中已普遍采用了层次式结构,将系统中的设备操作和管理软件分为若干 第五章 设 备 管 理 ·179· 个层次,每一层都利用其下层提供的服务,完成输入、输出功能中的某些子功能,并屏蔽 这些功能实现的细节,向高层提供服务。 在层次式结构的 I/O 软件中,只要层次间的接口不变,对每个层次中的软件进行的修改 都不会引起其下层或高层代码的变更,仅最低层才会涉及到硬件的具体特性。通常把 I/O 软 件组织成四个层次,如图 5-16 所示(图中的箭头表示 I/O 的控制流)。各层次及其功能如下 所述: (1) 用户层软件:实现与用户交互的接口,用户可直接调用在用户层提供的、与 I/O 操 作有关的库函数,对设备进行操作。 (2) 设备独立性软件:负责实现与设备驱动器的统一接口、设备命名、设备的保护以及 设备的分配与释放等,同时为设备管理和数据传送提供必要的存储空间。 (3) 设备驱动程序:与硬件直接相关,负责具体实现系统对设备发出的操作指令, 驱动I/O设备工作的驱动程序。 (4) 中断处理程序:用于保存被中断进程的 CPU 环境,转入相应的中断处理程序进行 处理,处理完后再恢复被中断进程的现场后返回到被中断进程。 I/O应答 用户层软件 设备独立性软件 设备驱动程序 中断处理程序 硬件 产生I/O请求、格式化I/O、Spooling 映射、保护、分块、缓冲、分配 设置设备寄存器,检查寄存器状态 执行I/O操作 图 5-16 I/O 系统的层次及功能 例如,当一个用户进程试图从文件中读一个数据块时,需要通过系统调用以取得操作 系统的服务来完成,设备独立性软件接收到请求后,首先在高速缓存中查找相应的页面, 如果没有,则调用设备驱动程序向硬件发出一个请求,并由驱动程序负责从磁盘读取目标 数据块。当磁盘操作完成后,由硬件产生一个中断,并转入中断处理程序,检查中断原因, 提取设备状态,转入相应的设备驱动程序,唤醒用户进程以及结束此次 I/O 请求,继续用户 进程的运行。 实际上,在不同的操作系统中,这种层次的划分并不是固定的,主要是随系统具体情 况的不同,而在层次的划分以及各层的功能和接口上存在一定的差异。下面我们将从低到 高地对每个层次进行讨论。 5.4.2 中断处理程序 中断处理层的主要工作有:进行进程上下文的切换,对处理中断信号源进行测试,读 取设备状态和修改进程状态等。由于中断处理与硬件紧密相关,对用户及用户程序而言, ·180· 计算机操作系统 应该尽量加以屏蔽,故应该放在操作系统的底层进行中断处理,系统的其余部分尽可能少 地与之发生联系。当一个进程请求 I/O 操作时,该进程将被挂起,直到 I/O 设备完成 I/O 操 作后,设备控制器便向 CPU 发送一中断请求,CPU 响应后便转向中断处理程序,中断处理 程序执行相应的处理,处理完后解除相应进程的阻塞状态。 对于为每一类设备设置一个I/O进程的设备处理方式,其中断处理程序的处理过程 分成以下几个步骤。 1.唤醒被阻塞的驱动(程序)进程 当中断处理程序开始执行时,首先去唤醒处于阻塞状态的驱动(程序)进程。如果是采用 了信号量机制,则可通过执行 signal 操作,将处于阻塞状态的驱动(程序)进程唤醒;在采用 信号机制时,将发送一信号给阻塞进程。 2.保护被中断进程的 CPU 环境 通常由硬件自动将处理机状态字 PSW 和程序计数器(PC)中的内容,保存在中断保留区 (栈)中,然后把被中断进程的 CPU 现场信息(即包括所有的 CPU 寄存器,如通用寄存器、段 寄存器等内容)都压入中断栈中,因为在中断处理时可能会用到这些寄存器。图 5-17 给出了 一个简单的保护中断现场的示意图。该程序是指令在 N 位置时被中断的,程序计数器中的 内容为 N+1,所有寄存器的内容都被保留在栈中。 PSW 程序状态字 Y PC(N+1) 程序计数器 … R0 Rn 寄存器 T+M T 线指针 N N-1 用户程序 … … 开始 返回 中断服务子例程 … PSW PC(N+1) R0 Rn 中断栈 图 5-17 中断现场保护示意图 3.转入相应的设备处理程序 由处理机对各个中断源进行测试,以确定引起本次中断的 I/O 设备,并发送一应答信号 给发出中断请求的进程,使之消除该中断请求信号,然后将相应的设备中断处理程序的入 口地址装入到程序计数器中,使处理机转向中断处理程序。 4.中断处理 对于不同的设备,有不同的中断处理程序。该程序首先从设备控制器中读出设备状态, 以判别本次中断是正常完成中断,还是异常结束中断。若是前者,中断程序便进行结束处 理;若还有命令,可再向控制器发送新的命令,进行新一轮的数据传送。若是异常结束中 第五章 设 备 管 理 ·181· 断,则根据发生异常的原因做相应的处理。 5.恢复被中断进程的现场 当中断处理完成以后,便可将保存在中断栈中的被中断进程的现场信息取出,并装入 到相应的寄存器中,其中包括该程序下一次要执行的指令的地址 N+1、处理机状态字 PSW, 以及各通用寄存器和段寄存器的内容。这样,当处理机再执行本程序时,便从 N+1 处开始, 最终返回到被中断的程序。 I/O 操作完成后,驱动程序必须检查本次 I/O 操作中是否发生了错误,并向上层软件报 告,最终向调用者报告本次 I/O 的执行情况。除了上述的第 4 步外,其它各步骤对所有 I/O 设备都是相同的,因而对于某种操作系统,例如 UNIX 系统,是把这些共同的部分集中 起来,形成中断总控程序。每当要进行中断处理时,都要首先进入中断总控程序。而对于 第 4 步,则对不同设备须采用不同的设备中断处理程序继续执行。图 5-18 示出了中断处 理流程。 唤醒被阻塞的 驱动程序进程 中断请求信号 对被中断进程的 CPU环境进行保护 分析中断原因,转入 相应的中断处理程序 终端中断 处理程序 打印机中断 处理程序 … 磁盘中断 处理程序 … 恢复被中断进 程的CPU现场 返回被中断的进 程,继续执行 图 5-18 中断处理流程 5.4.3 设备驱动程序 设备驱动程序通常又称为设备处理程序,它是 I/O 进程与设备控制器之间的通信程序, 又由于它常以进程的形式存在,故以后就简称之为设备驱动进程。其主要任务是接收上层 软件发来的抽象 I/O 要求,如 read 或 write 命令,在把它转换为具体要求后,发送给设备控 制器,启动设备去执行;此外,它也将由设备控制器发来的信号传送给上层软件。由于驱 动程序与硬件密切相关,故应为每一类设备配置一种驱动程序;有时也可为非常类似的两 类设备配置一个驱动程序。例如,打印机和显示器需要不同的驱动程序,但 SCSI 磁盘驱动 程序通常可以处理不同大小和不同速度的多个 SCSI 磁盘,甚至还可以处理 SCSI CD-ROM。 ·182· 计算机操作系统 1.设备驱动程序的功能 为了实现 I/O 进程与设备控制器之间的通信,设备驱动程序应具有以下功能: (1) 接收由设备独立性软件发来的命令和参数,并将命令中的抽象要求转换为具体要 求,例如,将磁盘块号转换为磁盘的盘面、磁道号及扇区号。 (2) 检查用户 I/O 请求的合法性,了解 I/O 设备的状态,传递有关参数,设置设备的工 作方式。 (3) 发出 I/O 命令。如果设备空闲,便立即启动 I/O 设备去完成指定的 I/O 操作;如果 设备处于忙碌状态,则将请求者的请求块挂在设备队列上等待。 (4) 及时响应由控制器或通道发来的中断请求,并根据其中断类型调用相应的中断处理 程序进行处理。 (5) 对于设置有通道的计算机系统,驱动程序还应能够根据用户的 I/O 请求,自动地构 成通道程序。 2.设备处理方式 在不同的操作系统中所采用的设备处理方式并不完全相同。根据在设备处理时是否设 置进程,以及设置什么样的进程而把设备处理方式分成以下三类: (1) 为每一类设备设置一个进程,专门用于执行这类设备的 I/O 操作。比如,为所有的 交互式终端设置一个交互式终端进程;又如,为同一类型的打印机设置一个打印进程。 (2) 在整个系统中设置一个 I/O 进程,专门用于执行系统中所有各类设备的 I/O 操作。 也可以设置一个输入进程和一个输出进程,分别处理系统中所有各类设备的输入或输出 操作。 (3) 不设置专门的设备处理进程,而只为各类设备设置相应的设备处理程序(模块),供 用户进程或系统进程调用。 3.设备驱动程序的特点 设备驱动程序属于低级的系统例程,它与一般的应用程序及系统程序之间有下述明显 差异: (1) 驱动程序主要是指在请求 I/O 的进程与设备控制器之间的一个通信和转换程序。它 将进程的 I/O 请求经过转换后,传送给控制器;又把控制器中所记录的设备状态和 I/O 操作 完成情况及时地反映给请求 I/O 的进程。 (2) 驱动程序与设备控制器和 I/O 设备的硬件特性紧密相关,因而对不同类型的设备应 配置不同的驱动程序。例如,可以为相同的多个终端设置一个终端驱动程序,但有时即使 是同一类型的设备,由于其生产厂家不同,它们也可能并不完全兼容,此时也须为它们配 置不同的驱动程序。 (3) 驱动程序与 I/O 设备所采用的 I/O 控制方式紧密相关。常用的 I/O 控制方式是中断 驱动和 DMA 方式,这两种方式的驱动程序明显不同,因为后者应按数组方式启动设备及进 行中断处理。 (4) 由于驱动程序与硬件紧密相关,因而其中的一部分必须用汇编语言书写。目前有很 多驱动程序的基本部分,已经固化在 ROM 中。 (5) 驱动程序应允许可重入。一个正在运行的驱动程序常会在一次调用完成前被再次调 第五章 设 备 管 理 ·183· 用。例如,网络驱动程序正在处理一个到来的数据包时,另一个数据包可能到达。 (6) 驱动程序不允许系统调用。但是为了满足其与内核其它部分的交互,可以允许对某 些内核过程的调用,如通过调用内核过程来分配和释放内存页面作为缓冲区,以及调用其 它过程来管理 MMU 定时器、DMA 控制器、中断控制器等。 4.设备驱动程序的处理过程 不同类型的设备应有不同的设备驱动程序,但大体上它们都可以分成两部分,其中, 除了要有能够驱动 I/O 设备工作的驱动程序外,还需要有设备中断处理程序,以处理 I/O 完 成后的工作。 设备驱动程序的主要任务是启动指定设备。但在启动之前,还必须完成必要的准备工 作,如检测设备状态是否为“忙”等。在完成所有的准备工作后,才最后向设备控制器发 送一条启动命令。 以下是设备驱动程序的处理过程。 1) 将抽象要求转换为具体要求 通常在每个设备控制器中都含有若干个寄存器,分别用于暂存命令、数据和参数等。 由于用户及上层软件对设备控制器的具体情况毫无了解,因而只能向它发出抽象的要求(命 令),但这些命令无法传送给设备控制器。因此,就需要将这些抽象要求转换为具体要求。 例如,将抽象要求中的盘块号转换为磁盘的盘面、 磁道号及扇区。这一转换工作只能由驱 动程序来完成,因为在 OS 中只有驱动程序才同时了解抽象要求和设备控制器中的寄存器情 况;也只有它才知道命令、 数据和参数应分别送往哪个寄存器。 2) 检查 I/O 请求的合法性 对于任何输入设备,都是只能完成一组特定的功能,若该设备不支持这次的 I/O 请求, 则认为这次 I/O 请求非法。例如,用户试图请求从打印机输入数据,显然系统应予以拒绝。 此外,还有些设备如磁盘和终端,它们虽然都是既可读又可写的,但若在打开这些设备时 规定的是读,则用户的写请求必然被拒绝。 3) 读出和检查设备的状态 在启动某个设备进行 I/O 操作时,其前提条件应是该设备正处于空闲状态。因此在启动 设备之前,要从设备控制器的状态寄存器中,读出设备的状态。例如,为了向某设备写入 数据,此前应先检查该设备是否处于接收就绪状态,仅当它处于接收就绪状态时,才能启 动其设备控制器,否则只能等待。 4) 传送必要的参数 对于许多设备,特别是块设备,除必须向其控制器发出启动命令外,还需传送必要的 参数。例如在启动磁盘进行读/写之前,应先将本次要传送的字节数和数据应到达的主存始 址,送入控制器的相应寄存器中。 5) 工作方式的设置 有些设备可具有多种工作方式,典型情况是利用 RS-232 接口进行异步通信。在启动该 接口之前,应先按通信规程设定参数:波特率、奇偶校验方式、停止位数目及数据字节长 度等。 ·184· 计算机操作系统 6) 启动 I/O 设备 在完成上述各项准备工作之后,驱动程序可以向控制器中的命令寄存器传送相应的控 制命令。对于字符设备,若发出的是写命令,驱动程序将把一个数据传送给控制器;若发 出的是读命令,则驱动程序等待接收数据,并通过从控制器中的状态寄存器读入状态字的 方法,来确定数据是否到达。 驱动程序发出 I/O 命令后,基本的 I/O 操作是在设备控制器的控制下进行的。通常,I/O 操作所要完成的工作较多,需要一定的时间,如读/写一个盘块中的数据,此时驱动(程序) 进程把自己阻塞起来,直到中断到来时才将它唤醒。 5.4.4 设备独立性软件 1.设备独立性的概念 为了提高 OS 的可适应性和可扩展性,在现代 OS 中都毫无例外地实现了设备独立性 (Device Independence),也称为设备无关性。其基本含义是: 应用程序独立于具体使用的物 理设备。为了实现设备独立性而引入了逻辑设备和物理设备这两个概念。在应用程序中, 使用逻辑设备名称来请求使用某类设备;而系统在实际执行时,还必须使用物理设备名称。 因此,系统须具有将逻辑设备名称转换为某物理设备名称的功能,这非常类似于存储器管 理中所介绍的逻辑地址和物理地址的概念。在应用程序中所使用的是逻辑地址,而系统在 分配和使用内存时,必须使用物理地址。在实现了设备独立性的功能后,可带来以下两方 面的好处。 1) 设备分配时的灵活性 当应用程序(进程)以物理设备名称来请求使用指定的某台设备时,如果该设备已经分配 给其他进程或正在检修,而此时尽管还有几台其它的相同设备正在空闲,该进程却仍阻塞。 但若进程能以逻辑设备名称来请求某类设备时,系统可立即将该类设备中的任一台分配给 进程,仅当所有此类设备已全部分配完毕时,进程才会阻塞。 2) 易于实现 I/O 重定向 所谓 I/O 重定向,是指用于 I/O 操作的设备可以更换(即重定向),而不必改变应用程序。 例如,我们在调试一个应用程序时,可将程序的所有输出送往屏幕显示;而在程序调试完 后,如需正式将程序的运行结果打印出来,此时便须将 I/O 重定向的数据结构——逻辑设备 表中的显示终端改为打印机,而不必修改应用程序。I/O 重定向功能具有很大的实用价值, 现已被广泛地引入到各类 OS 中。 2.设备独立性软件 驱动程序是一个与硬件(或设备)紧密相关的软件。为了实现设备独立性,必须再在驱动 程序之上设置一层软件,称为设备独立性软件。至于设备独立性软件和设备驱动程序之间 的界限,根据不同的操作系统和设备有所差异,主要取决于操作系统、设备独立性和设备 驱动程序的运行效率等多方面因素的权衡,因为对于一些本应由设备独立性软件实现的功 能,可能由于效率等诸多因素,实际上设计在设备驱动程序中。总的来说,设备独立性软 件的主要功能可分为以下两个方面: (1) 执行所有设备的公有操作。这些公有操作包括: 第五章 设 备 管 理 ·185· ① 对独立设备的分配与回收; ② 将逻辑设备名映射为物理设备名,进一步可以找到相应物理设备的驱动程序; ③ 对设备进行保护,禁止用户直接访问设备; ④ 缓冲管理,即对字符设备和块设备的缓冲区进行有效的管理,以提高I/O的效率; ⑤ 差错控制,由于在I/O操作中的绝大多数错误都与设备无关,故主要由设备驱动程序 处理,而设备独立性软件只处理那些设备驱动程序无法处理的错误; ⑥ 提供独立于设备的逻辑块,不同类型的设备信息交换单位是不同的,读取和传输 速率也各不相同,如字符型设备以单个字符为单位,块设备是以一个数据块为单位,即使 同一类型的设备,其信息交换单位大小也是有差异的,如不同磁盘由于扇区大小的不同, 可能造成数据块大小的不一致,因此设备独立性软件应负责隐藏这些差异,对逻辑设备使 用并向高层软件提供大小统一的逻辑数据块。 (2) 向用户层(或文件层)软件提供统一接口。无论何种设备,它们向用户所提供的接口 应该是相同的。例如,对各种设备的读操作,在应用程序中都使用 read;而对各种设备的 写操作,也都使用 write。 3.逻辑设备名到物理设备名映射的实现 1) 逻辑设备表 为了实现设备的独立性,系统必须设置一张逻辑设备表(LUT,Logical Unit Table),用 于将应用程序中所使用的逻辑设备名映射为物理设备名。在该表的每个表目中包含了三项: 逻辑设备名、物理设备名和设备驱动程序的入口地址,如图 5-19(a)所示。当进程用逻辑设 备名请求分配 I/O 设备时,系统为它分配相应的物理设备,并在 LUT 上建立一个表目,填 上应用程序中使用的逻辑设备名和系统分配的物理设备名,以及该设备驱动程序的入口地 址。当以后进程再利用该逻辑设备名请求 I/O 操作时,系统通过查找 LUT,便可找到物理 设备和驱动程序。 逻辑设备名 物理设备名 /dev/tty 3 /dev/printer 5 驱动程序 入口地址 1024 2046 逻辑设备名 系统设备表指针 /dev/tty 3 /dev/printer 5 … … … … (a) (b) 图 5-19 逻辑设备表 2) LUT 的设置问题 LUT 的设置可采取两种方式:第一种方式是在整个系统中只设置一张 LUT。由于系统 中所有进程的设备分配情况都记录在同一张 LUT 中,因而不允许在 LUT 中具有相同的逻辑 设备名,这就要求所有用户都不使用相同的逻辑设备名。在多用户环境下这通常是难以做 到的,因而这种方式主要用于单用户系统中。第二种方式是为每个用户设置一张 LUT。每 当用户登录时,便为该用户建立一个进程,同时也为之建立一张 LUT,并将该表放入进程 的 PCB 中。由于通常在多用户系统中,都配置了系统设备表,故此时的逻辑设备表可以采 用图 5-19(b)中的格式。 ·186· 计算机操作系统 5.4.5 用户层的 I/O 软件 一般而言,大部分的 I/O 软件都在操作系统内部,但仍有一小部分在用户层,包括与用 户程序链接在一起的库函数,以及完全运行于内核之外的一些程序。 用户层软件必须通过一组系统调用来取得操作系统服务。在现代的高级语言以及 C 语 言中,通常提供了与各系统调用一一对应的库函数,用户程序通过调用对应的库函数使用 系统调用。这些库函数与调用程序连接在一起,包含在运行时装入在内存的二进制程序中, 如 C 语言中的库函数 write 等,显然这些库函数的集合也是 I/O 系统的组成部分。但在许多 现代操作系统中,系统调用本身已经采用 C 语言编写,并以函数形式提供,所以在使用 C 语言编写的用户程序中,可以直接使用这些系统调用。 另外,在操作系统中还有一些程序,如下面章节我们将要论述的 Spooling 系统以及在 网络传输文件时常使用的守护进程等,就是完全运行在内核之外的程序,但它们仍归属于 I/O 系统。 5.5 设 备 分 配 在多道程序环境下,系统中的设备供所有进程共享。为防止诸进程对系统资源的无序 竞争,特规定系统设备不允许用户自行使用,必须由系统统一分配。每当进程向系统提出 I/O 请求时,只要是可能和安全的,设备分配程序便按照一定的策略,把设备分配给请求用 户(进程)。在有的系统中,为了确保在 CPU 与设备之间能进行通信,还应分配相应的控制 器和通道。为了实现设备分配,必须在系统中设置相应的数据结构。 5.5.1 设备分配中的数据结构 在进行设备分配时,通常都需要借助于一些表格的帮助。在表格中记录了相应设备或 控制器的状态及对设备或控制器进行控制所需的信息。在进行设备分配时所需的数据结构 (表格)有:设备控制表、控制器控制表、通道控制表和系统设备表等。 1.设备控制表(DCT) 系统为每一个设备都配置了一张设备控制表,用于记录本设备的情况,如图 5-20 所示。 设 DCT 1 备 控 制 DCT 2 表 集 合 DCT n 设备类型:type 设备标识符:deviceid 设备状态:等待/不等待 忙/闲 指向控制器表的指针 重复执行次数或时间 设备队列的队首指针 图 5-20 设备控制表 设备控制表中,除了有用于指示设备类型的字段 type 和设备标识字段 deviceid 外,还 应含有下列字段: 第五章 设 备 管 理 ·187· (1) 设备队列队首指针。凡因请求本设备而未得到满足的进程,其 PCB 都应按照一定 的策略排成一个队列,称该队列为设备请求队列或简称设备队列。其队首指针指向队首 PCB。在有的系统中还设置了队尾指针。 (2) 设备状态。当设备自身正处于使用状态时,应将设备的忙/闲标志置“1”。若与该 设备相连接的控制器或通道正忙,也不能启动该设备,此时则应将设备的等待标志置“1”。 (3) 与设备连接的控制器表指针。该指针指向该设备所连接的控制器的控制表。在设备 到主机之间具有多条通路的情况下,一个设备将与多个控制器相连接。此时,在 DCT 中还 应设置多个控制器表指针。 (4) 重复执行次数。由于外部设备在传送数据时,较易发生数据传送错误,因而在许多 系统中,如果发生传送错误,并不立即认为传送失败,而是令它重新传送,并由系统规定 设备在工作中发生错误时应重复执行的次数。在重复执行时,若能恢复正常传送,则仍认 为传送成功。仅当屡次失败,致使重复执行次数达到规定值而传送仍不成功时,才认为传 送失败。 2.控制器控制表、通道控制表和系统设备表 (1) 控制器控制表(COCT)。系统为每一个控制器都设置了一张用于记录本控制器情况 的控制器控制表,如图 5-21(a)所示。 (2) 通道控制表(CHCT)。每个通道都配有一张通道控制表,如图 5-21(b)所示。 控制器标识符:controllerid 控制器状态:忙/闲 与控制器连接的通道表指针 控制器队列的队首指针 控制器队列的队尾指针 通道标识符:channelid 通道状态:忙/闲 与通道连接的控制器表首址 通道队列的队首指针 通道队列的队尾指针 … … 表目1 表目i 设备类 设备标识符 DCT 驱动程序入口 (a) 控制器表COCT (b) 通道表CHCT 图 5-21 COCT、CHCT 和 SDT (c) 系统设备表SDT (3) 系统设备表(SDT)。这是系统范围的数据结构,其中记录了系统中全部设备的情况。 每个设备占一个表目,其中包括有设备类型、设备标识符、设备控制表及设备驱动程序的 入口等项,如图 5-21(c)所示。 5.5.2 设备分配时应考虑的因素 为了使系统有条不紊地工作,系统在分配设备时,应考虑这样几个因素: ① 设备的固 有属性;② 设备分配算法;③ 设备分配时的安全性;④ 设备独立性。本小节介绍前三个 问题,下一小节专门介绍设备独立性问题。 1.设备的固有属性 在分配设备时,首先应考虑与设备分配有关的设备属性。设备的固有属性可分成三种: 第一种是独占性,是指这种设备在一段时间内只允许一个进程独占,此即第二章所说的“临 界资源”;第二种是共享性,指这种设备允许多个进程同时共享;第三种是可虚拟设备,指 设备本身虽是独占设备,但经过某种技术处理,可以把它改造成虚拟设备。对上述的独占、 ·188· 计算机操作系统 共享、可虚拟三种设备应采取不同的分配策略。 (1) 独占设备。对于独占设备,应采用独享分配策略,即将一个设备分配给某进程后, 便由该进程独占,直至该进程完成或释放该设备,然后,系统才能再将该设备分配给其他 进程使用。这种分配策略的缺点是,设备得不到充分利用,而且还可能引起死锁。 (2) 共享设备。对于共享设备,可同时分配给多个进程使用,此时须注意对这些进程访 问该设备的先后次序进行合理的调度。 (3) 可虚拟设备。由于可虚拟设备是指一台物理设备在采用虚拟技术后,可变成多台逻 辑上的所谓虚拟设备,因而说,一台可虚拟设备是可共享的设备,可以将它同时分配给多 个进程使用,并对这些访问该(物理)设备的先后次序进行控制。 2.设备分配算法 对设备进行分配的算法,与进程调度的算法有些相似之处,但前者相对简单,通常只 采用以下两种分配算法: (1) 先来先服务。当有多个进程对同一设备提出 I/O 请求时,该算法是根据诸进程对某 设备提出请求的先后次序,将这些进程排成一个设备请求队列,设备分配程序总是把设备 首先分配给队首进程。 (2) 优先级高者优先。在进程调度中的这种策略,是优先权高的进程优先获得处理机。 如果对这种高优先权进程所提出的 I/O 请求也赋予高优先权,显然有助于这种进程尽快完 成。在利用该算法形成设备队列时,将优先权高的进程排在设备队列前面,而对于优先级 相同的 I/O 请求,则按先来先服务原则排队。 3.设备分配中的安全性 从进程运行的安全性考虑,设备分配有以下两种方式。 1) 安全分配方式 在这种分配方式中,每当进程发出 I/O 请求后,便进入阻塞状态,直到其 I/O 操作完成 时才被唤醒。在采用这种分配策略时,一旦进程已经获得某种设备(资源)后便阻塞,使该进 程不可能再请求任何资源,而在它运行时又不保持任何资源。因此,这种分配方式已经摒 弃了造成死锁的四个必要条件之一的“请求和保持”条件,从而使设备分配是安全的。其 缺点是进程进展缓慢,即 CPU 与 I/O 设备是串行工作的。 2) 不安全分配方式 在这种分配方式中,进程在发出 I/O 请求后仍继续运行,需要时又发出第二个 I/O 请求、 第三个 I/O 请求等。仅当进程所请求的设备已被另一进程占用时,请求进程才进入阻塞状态。 这种分配方式的优点是,一个进程可同时操作多个设备,使进程推进迅速。其缺点是分配 不安全,因为它可能具备“请求和保持”条件,从而可能造成死锁。因此,在设备分配程 序中,还应再增加一个功能,以用于对本次的设备分配是否会发生死锁进行安全性计算, 仅当计算结果说明分配是安全的情况下才进行设备分配。 5.5.3 独占设备的分配程序 1.基本的设备分配程序 下面我们通过一个具有 I/O 通道的系统案例,来介绍设备分配过程。当某进程提出 I/O 第五章 设 备 管 理 ·189· 请求后,系统的设备分配程序可按下述步骤进行设备分配。 1) 分配设备 首先根据 I/O 请求中的物理设备名,查找系统设备表(SDT),从中找出该设备的 DCT, 再根据 DCT 中的设备状态字段,可知该设备是否正忙。若忙,便将请求 I/O 进程的 PCB 挂在设备队列上;否则,便按照一定的算法来计算本次设备分配的安全性。如果不会导 致系统进入不安全状态,便将设备分配给请求进程;否则,仍将其 PCB 插入设备等待 队列。 2) 分配控制器 在系统把设备分配给请求 I/O 的进程后,再到其 DCT 中找出与该设备连接的控制器的 COCT,从 COCT 的状态字段中可知该控制器是否忙碌。若忙,便将请求 I/O 进程的 PCB 挂在该控制器的等待队列上;否则,便将该控制器分配给进程。 3) 分配通道 在该 COCT 中又可找到与该控制器连接的通道的 CHCT,再根据 CHCT 内的状态信息, 可知该通道是否忙碌。若忙,便将请求 I/O 的进程挂在该通道的等待队列上;否则,将该通 道分配给进程。只有在设备、 控制器和通道三者都分配成功时,这次的设备分配才算成功。 然后,便可启动该 I/O 设备进行数据传送。 2.设备分配程序的改进 仔细研究上述基本的设备分配程序后可以发现: ① 进程是以物理设备名来提出 I/O 请 求的;② 采用的是单通路的 I/O 系统结构,容易产生“瓶颈”现象。为此,应从以下两方 面对基本的设备分配程序加以改进,以使独占设备的分配程序具有更强的灵活性,并提高 分配的成功率。 1) 增加设备的独立性 为了获得设备的独立性,进程应使用逻辑设备名请求 I/O。这样,系统首先从 SDT 中 找出第一个该类设备的 DCT。若该设备忙,又查找第二个该类设备的 DCT,仅当所有该类 设备都忙时,才把进程挂在该类设备的等待队列上;而只要有一个该类设备可用,系统便 进一步计算分配该设备的安全性。 2) 考虑多通路情况 为了防止在 I/O 系统中出现“瓶颈”现象,通常都采用多通路的 I/O 系统结构。此时对 控制器和通道的分配同样要经过几次反复,即若设备(控制器)所连接的第一个控制器(通道) 忙时,应查看其所连接的第二个控制器(通道),仅当所有的控制器(通道)都忙时,此次的控 制器(通道)分配才算失败,才把进程挂在控制器(通道)的等待队列上。而只要有一个控制器 (通道)可用,系统便可将它分配给进程。 5.5.4 SPOOLing 技术 如前所述,虚拟性是 OS 的四大特征之一。如果说可以通过多道程序技术将一台物理 CPU 虚拟为多台逻辑 CPU,从而允许多个用户共享一台主机,那么,通过 SPOOLing 技术 便可将一台物理 I/O 设备虚拟为多台逻辑 I/O 设备,同样允许多个用户共享一台物理 I/O 设备。 ·190· 计算机操作系统 1.什么是 SPOOLing 为了缓和 CPU 的高速性与 I/O 设备低速性间的矛盾而引入了脱机输入、脱机输出技术。 该技术是利用专门的外围控制机,将低速 I/O 设备上的数据传送到高速磁盘上;或者相反。 事实上,当系统中引入了多道程序技术后,完全可以利用其中的一道程序,来模拟脱机输 入时的外围控制机功能,把低速 I/O 设备上的数据传送到高速磁盘上;再用另一道程序来模 拟脱机输出时外围控制机的功能,把数据从磁盘传送到低速输出设备上。这样,便可在主 机的直接控制下,实现脱机输入、输出功能。此时的外围操作与 CPU 对数据的处理同时进 行,我们把这种在联机情况下实现的同时外围操作称为 SPOOLing(Simultaneaus Periphernal Operating On Line),或称为假脱机操作。 2.SPOOLing 系统的组成 由上所述得知,SPOOLing 技术是对脱机输入、输出系统的模拟。相应地,SPOOLing 系统必须建立在具有多道程序功能的操作系统上,而且还应有高速随机外存的支持,这通 常是采用磁盘存储技术。 SPOOLing 系统主要有以下三部分: (1) 输入井和输出井。这是在磁盘上开辟的两个大存储空间。输入井是模拟脱机输入时 的磁盘设备,用于暂存 I/O 设备输入的数据;输出井是模拟脱机输出时的磁盘,用于暂存用 户程序的输出数据。 (2) 输入缓冲区和输出缓冲区。为了缓和 CPU 和磁盘之间速度不匹配的矛盾,在内存 中要开辟两个缓冲区:输入缓冲区和输出缓冲区。输入缓冲区用于暂存由输入设备送来的 数据,以后再传送到输入井。输出缓冲区用于暂存从输出井送来的数据,以后再传送给输 出设备。 (3) 输入进程 SPi 和输出进程 SPo。这里利用两个进程来模拟脱机 I/O 时的外围控制机。 其中,进程 SPi 模拟脱机输入时的外围控制机,将用户要求的数据从输入机通过输入缓冲区 再送到输入井,当 CPU 需要输入数据时,直接从输入井读入内存;进程 SPo 模拟脱机输出 时的外围控制机,把用户要求输出的数据先从内存送到输出井,待输出设备空闲时,再将 输出井中的数据经过输出缓冲区送到输出设备上。 图 5-22 示出了 SPOOLing 系统的组成。 输入设备 输出设备 输入进程SPi 输出进程SPo 输入缓冲区Bi 输出缓冲区Bo 磁盘 输入井 输出井 图 5-22 SPOOLing 系统的组成 3.共享打印机 打印机是经常要用到的输出设备,属于独占设备。利用 SPOOLing 技术,可将之改造 第五章 设 备 管 理 ·191· 为一台可供多个用户共享的设备,从而提高设备的利用率,也方便了用户。共享打印机技 术已被广泛地用于多用户系统和局域网络中。当用户进程请求打印输出时,SPOOLing 系统 同意为它打印输出,但并不真正立即把打印机分配给该用户进程,而只为它做两件事:① 由 输出进程在输出井中为之申请一个空闲磁盘块区,并将要打印的数据送入其中;② 输出进 程再为用户进程申请一张空白的用户请求打印表,并将用户的打印要求填入其中,再将该 表挂到请求打印队列上。如果还有进程要求打印输出,系统仍可接受该请求,也同样为该 进程做上述两件事。 如果打印机空闲,输出进程将从请求打印队列的队首取出一张请求打印表,根据表中 的要求将要打印的数据,从输出井传送到内存缓冲区,再由打印机进行打印。打印完后, 输出进程再查看请求打印队列中是否还有等待打印的请求表。若有,又取出队列中的第一 张表,并根据其中的要求进行打印,如此下去,直至请求打印队列为空,输出进程才将自 己阻塞起来。仅当下次再有打印请求时,输出进程才被唤醒。 4.SPOOLing 系统的特点 SPOOLing 系统具有如下主要特点: (1) 提高了 I/O 的速度。这里,对数据所进行的 I/O 操作,已从对低速 I/O 设备进行的 I/O 操作,演变为对输入井或输出井中数据的存取,如同脱机输入输出一样,提高了 I/O 速 度,缓和了 CPU 与低速 I/O 设备之间速度不匹配的矛盾。 (2) 将独占设备改造为共享设备。因为在 SPOOLing 系统中,实际上并没为任何进程分 配设备,而只是在输入井或输出井中为进程分配一个存储区和建立一张 I/O 请求表。这样, 便把独占设备改造为共享设备。 (3) 实现了虚拟设备功能。宏观上,虽然是多个进程在同时使用一台独占设备,而对于 每一个进程而言,他们都会认为自己是独占了一个设备。当然,该设备只是逻辑上的设备。 SPOOLing 系统实现了将独占设备变换为若干台对应的逻辑设备的功能。 5.6 磁盘存储器的管理 磁盘存储器不仅容量大,存取速度快,而且可以实现随机存取,是当前存放大量程序 和数据的理想设备,故在现代计算机系统中,都配置了磁盘存储器,并以它为主来存放文 件。这样,对文件的操作,都将涉及到对磁盘的访问。磁盘 I/O 速度的高低和磁盘系统的可 靠性,都将直接影响到系统性能。因此,设法改善磁盘系统的性能,已成为现代操作系统 的重要任务之一。 5.6.1 磁盘性能简述 磁盘设备是一种相当复杂的机电设备,有专门的课程对它进行详细讲述。在此,仅对 磁盘的某些性能,如数据的组织、 磁盘的类型和访问时间等方面做扼要的介绍。 1.数据的组织和格式 磁盘设备可包括一或多个物理盘片,每个磁盘片分一个或两个存储面(surface)(见图 ·192· 计算机操作系统 5-23(a)),每个磁盘面被组织成若干个同心环,这种环称为磁道(track),各磁道之间留有必 要的间隙。为使处理简单起见,在每条磁道上可存储相同数目的二进制位。这样,磁盘密 度即每英寸中所存储的位数,显然是内层磁道的密度较外层磁道的密度高。每条磁道又被 逻辑上划分成若干个扇区(sectors),软盘大约为 8~32 个扇区,硬盘则可多达数百个,图 5-23(b)显示了一个磁道分成 8 个扇区。一个扇区称为一个盘块(或数据块),常常叫做磁盘扇 区。各扇区之间保留一定的间隙。 盘面9 盘面8 盘面7 盘面6 盘面5 盘面4 盘面3 盘面2 盘面1 盘面0 扇区 读写磁头 磁道 磁道间隔 扇区间隔 轴心 主杆 (a) 磁盘驱动器的结构 (b) 磁盘的数据布局 图 5-23 磁盘的结构和布局 一个物理记录存储在一个扇区上,磁盘上存储的物理记录块数目是由扇区数、磁道数 以及磁盘面数所决定的。例如,一个 10 GB 容量的磁盘,有 8 个双面可存储盘片,共 16 个 存储面(盘面),每面有 16 383 个磁道(也称柱面),63 个扇区。 为了提高磁盘的存储容量,充分利用磁盘外面磁道的存储能力,现代磁盘不再把内外 磁道划分为相同数目的扇区,而是利用外层磁道容量较内层磁道大的特点,将盘面划分成 若干条环带,使得同一环带内的所有磁道具有相同的扇区数。显然,外层环带的磁道拥有 较内层环带的磁道更多的扇区。为了减少这种磁道和扇区在盘面分布的几何形式变化对驱 动程序的影响,大多数现代磁盘都隐藏了这些细节,向操作系统提供虚拟几何的磁盘规格, 而不是实际的物理几何规格。 为了在磁盘上存储数据,必须先将磁盘低级格式化。图 5-24 示出了一种温盘(温切斯特 盘)中一条磁道格式化的情况。其中每条磁道含有 30 个固定大小的扇区,每个扇区容量 为 600 个字节,其中 512 个字节存放数据,其余的用于存放控制信息。每个扇区包括两个 字段: (1) 标识符字段,其中一个字节的 SYNCH 具有特定的位图像,作为该字段的定界符, 利用磁道号、 磁头号及扇区号三者来标识一个扇区;CRC 字段用于段校验。 (2) 数据字段,其中可存放 512 个字节的数据。 第五章 设 备 管 理 ·193· Sector Physical Sector 0 Physical Sector 1 Physical Sector 29 ID Data ID Data Gap FieldGap FieldGap Gap FieldGap FieldGap 1 0 2 0 3 1 29 2 29 3 Byte 17 7 41 515 20 17 7 41 515 20 ID Data Gap FieldGap FieldGap 1 29 2 29 3 17 7 41 515 20 Synch Track Byte # Head # Se#ctorCRC Byte 1 2 1 1 3 Synch Byte Data CRC 1 512 2 600 Byte/Sector 图 5-24 磁盘的格式化 磁盘格式化完成后,一般要对磁盘分区。在逻辑上,每个分区就是一个独立的逻辑磁 盘。每个分区的起始扇区和大小都记录在磁盘 0 扇区的主引导记录分区表所包含的分区表 中。在这个分区表中必须有一个分区被标记成活动的,以保证能够从硬盘引导系统。 但是,在真正可以使用磁盘前,还需要对磁盘进行一次高级格式化,即设置一个引导块、 空闲存储管理、根目录和一个空文件系统,同时在分区表中标记该分区所使用的文件系统。 2.磁盘的类型 对磁盘,可以从不同的角度进行分类。最常见的有:将磁盘分成硬盘和软盘、单片盘和 多片盘、固定头磁盘和活动头(移动头)磁盘等。下面仅对固定头磁盘和移动头磁盘做些介绍。 1) 固定头磁盘 这种磁盘在每条磁道上都有一读/写磁头,所有的磁头都被装在一刚性磁臂中。通过这 些磁头可访问所有各磁道,并进行并行读/写,有效地提高了磁盘的 I/O 速度。这种结构的 磁盘主要用于大容量磁盘上。 2) 移动头磁盘 每一个盘面仅配有一个磁头,也被装入磁臂中。为能访问该盘面上的所有磁道,该磁 头必须能移动以进行寻道。可见,移动磁头仅能以串行方式读/写,致使其 I/O 速度较慢; 但由于其结构简单,故仍广泛应用于中小型磁盘设备中。在微型机上配置的温盘和软盘都 采用移动磁头结构,故本节主要针对这类磁盘的 I/O 进行讨论。 3.磁盘访问时间 磁盘设备在工作时以恒定速率旋转。为了读或写,磁头必须能移动到所要求的磁道上, 并等待所要求的扇区的开始位置旋转到磁头下,然后再开始读或写数据。故可把对磁盘的 访问时间分成以下三部分。 1) 寻道时间 Ts 这是指把磁臂(磁头)移动到指定磁道上所经历的时间。该时间是启动磁臂的时间 s 与磁 头移动 n 条磁道所花费的时间之和,即 Ts = m × n + s 其中,m 是一常数,与磁盘驱动器的速度有关。对于一般磁盘,m = 0.2;对于高速磁盘, ·194· 计算机操作系统 m≤0.1,磁臂的启动时间约为 2 ms。这样,对于一般的温盘,其寻道时间将随寻道距离的 增加而增大,大体上是 5~30 ms。 2) 旋转延迟时间 Tr 这是指定扇区移动到磁头下面所经历的时间。不同的磁盘类型中,旋转速度至少相差 一个数量级,如软盘为 300 r/min,硬盘一般为 7200~15 000 r/min,甚至更高。对于磁盘旋 转延迟时间而言,如硬盘,旋转速度为 15 000 r/min,每转需时 4 ms,平均旋转延迟时间 Tr 为 2 ms;而软盘,其旋转速度为 300 r/min 或 600 r/min,这样,平均 Tr 为 50~100 ms。 3) 传输时间 Tt 这是指把数据从磁盘读出或向磁盘写入数据所经历的时间。Tt 的大小与每次所读/写的 字节数 b 和旋转速度有关: Tt = b rN 其中,r 为磁盘每秒钟的转数;N 为一条磁道上的字节数,当一次读/写的字节数相当于半条 磁道上的字节数时,Tt 与 Tr 相同。因此,可将访问时间 Ta 表示为 Ta = Ts + 1 2r + b rN 由上式可以看出,在访问时间中,寻道时间和旋转延迟时间基本上都与所读/写数据的 多少无关,而且它通常占据了访问时间中的大头。例如,我们假定寻道时间和旋转延迟时 间平均为 20 ms,而磁盘的传输速率为 10 MB/s,如果要传输 10 KB 的数据,此时总的访问 时间为 21 ms,可见传输时间所占比例是非常小的。当传输 100 KB 数据时,其访问时间也 只是 30 ms,即当传输的数据量增大 10 倍时,访问时间只增加约 50%。目前磁盘的传输速 率已达 80 MB/s 以上,数据传输时间所占的比例更低。可见,适当地集中数据(不要太零散) 传输,将有利于提高传输效率。 5.6.2 磁盘调度 磁盘是可供多个进程共享的设备,当有多个进程都要求访问磁盘时,应采用一种最佳 调度算法,以使各进程对磁盘的平均访问时间最小。由于在访问磁盘的时间中,主要是寻 道时间,因此,磁盘调度的目标是使磁盘的平均寻道时间最少。目前常用的磁盘调度算法 有先来先服务、最短寻道时间优先及扫描等算法。下面逐一介绍。 1.先来先服务(FCFS,First Come First Served) 这是一种最简单的磁盘调度算法。它根据进程请求访问磁盘的先后次序进行调度。此 算法的优点是公平、简单,且每个进程的请求都能依次地得到处理,不会出现某一进程的 请求长期得不到满足的情况。但此算法由于未对寻道进行优化,致使平均寻道时间可能较 长。图 5-25 示出了有 9 个进程先后提出磁盘 I/O 请求时,按 FCFS 算法进行调度的情况。 这里将进程号(请求者)按他们发出请求的先后次序排队。这样,平均寻道距离为 55.3 条磁道, 与后面即将讲到的几种调度算法相比,其平均寻道距离较大,故 FCFS 算法仅适用于请求磁 盘 I/O 的进程数目较少的场合。 第五章 设 备 管 理 ·195· (从 100 号磁道开始) 被访问的下 移动距离 一个磁道号 (磁道数) 55 45 58 3 39 19 18 21 90 72 160 70 150 10 38 112 184 146 平均寻道长度: 55.3 图 5-25 FCFS 调度算法 2.最短寻道时间优先(SSTF,Shortest Seek Time First) 该算法选择这样的进程:其要求访问的磁道与当前磁头所在的磁道距离最近,以使每 次的寻道时间最短。但这种算法不能保证平均寻道时间最短。图 5-26 示出了按 SSTF 算法 进行调度时,各进程被调度的次序、每次磁头移动的距离,以及 9 次调度磁头平均移动的 距离。比较图 5-25 和图 5-26 可以看出,SSTF 算法的平均每次磁头移动距离明显低于 FCFS 的距离,因而 SSTF 较之 FCFS 有更好的寻道性能,故过去曾一度被广泛采用。 (从 100 号磁道开始) 被访问的下 移动距离 一个磁道号 (磁道数) 90 10 58 32 55 3 39 16 38 1 18 20 150 132 160 10 184 24 平均寻道长度: 27.5 图 5-26 SSTF 调度算法 3.扫描(SCAN)算法 1) 进程“饥饿”现象 SSTF 算法虽然能获得较好的寻道性能,但却可能导致某个进程发生“饥饿”(Starvation) ·196· 计算机操作系统 现象。因为只要不断有新进程的请求到达,且其所要访问的磁道与磁头当前所在磁道的距 离较近,这种新进程的 I/O 请求必然优先满足。对 SSTF 算法略加修改后所形成的 SCAN 算 法,即可防止老进程出现“饥饿”现象。 2) SCAN 算法 该算法不仅考虑到欲访问的磁道与当前磁道间的距离,更优先考虑的是磁头当前的移 动方向。例如,当磁头正在自里向外移动时,SCAN 算法所考虑的下一个访问对象,应是其 欲访问的磁道既在当前磁道之外,又是距离最近的。这样自里向外地访问,直至再无更外 的磁道需要访问时,才将磁臂换向为自外向里移动。这时,同样也是每次选择这样的进程 来调度,即要访问的磁道在当前位置内距离最近者,这样,磁头又逐步地从外向里移动, 直至再无更里面的磁道要访问,从而避免了出现“饥饿”现象。由于在这种算法中磁头移 动的规律颇似电梯的运行,因而又常称之为电梯调度算法。图 5-27 示出了按 SCAN 算法对 9 个进程进行调度及磁头移动的情况。 (从 100#磁道开始,向磁道号增加方向访问) 被访问的下 移动距离 一个磁道号 (磁道数) 150 50 160 10 184 24 90 94 58 32 55 3 39 16 38 1 18 20 平均寻道长度:27.8 图 5-27 SCAN 调度算法示例 4.循环扫描(CSCAN)算法 SCAN 算法既能获得较好的寻道性能,又能防止“饥饿”现象,故被广泛用于大、中、 小型机器和网络中的磁盘调度。但 SCAN 也存在这样的问题:当磁头刚从里向外移动而越 过了某一磁道时,恰好又有一进程请求访问此磁道,这时,该进程必须等待,待磁头继续 从里向外,然后再从外向里扫描完所有要访问的磁道后,才处理该进程的请求,致使该进 程的请求被大大地推迟。为了减少这种延迟,CSCAN 算法规定磁头单向移动,例如,只是 自里向外移动,当磁头移到最外的磁道并访问后,磁头立即返回到最里的欲访问的磁道, 亦即将最小磁道号紧接着最大磁道号构成循环,进行循环扫描。采用循环扫描方式后,上 述请求进程的请求延迟将从原来的 2T 减为 T + Smax,其中,T 为由里向外或由外向里单向扫 描完要访问的磁道所需的寻道时间,而 Smax 是将磁头从最外面被访问的磁道直接移到最里 面欲访问的磁道(或相反)的寻道时间。图 5-28 示出了 CSCAN 算法对 9 个进程调度的次序及 每次磁头移动的距离。 第五章 设 备 管 理 ·197· (从 100#磁道开始,向磁道号增加方向访问) 被访问的下 移动距离 一个磁道号 (磁道数) 150 50 160 10 184 24 18 166 38 20 39 1 55 16 58 3 90 32 平均寻道长度: 35.8 图 5-28 CSCAN 调度算法示例 5.NStepSCAN 和 FSCAN 调度算法 1) NStepSCAN 算法 在 SSTF、 SCAN 及 CSCAN 几种调度算法中,都可能会出现磁臂停留在某处不动的情 况,例如,有一个或几个进程对某一磁道有较高的访问频率,即这个(些)进程反复请求对某 一磁道的 I/O 操作,从而垄断了整个磁盘设备。我们把这一现象称为“磁臂粘着” (Armstickiness)。在高密度磁盘上容易出现此情况。N 步 SCAN 算法是将磁盘请求队列分成 若干个长度为 N 的子队列,磁盘调度将按 FCFS 算法依次处理这些子队列。而每处理一个 队列时又是按 SCAN 算法,对一个队列处理完后,再处理其他队列。当正在处理某子队列 时,如果又出现新的磁盘 I/O 请求,便将新请求进程放入其他队列,这样就可避免出现粘着 现象。当 N 值取得很大时,会使 N 步扫描法的性能接近于 SCAN 算法的性能;当 N=1 时, N 步 SCAN 算法便蜕化为 FCFS 算法。 2) FSCAN 算法 FSCAN 算法实质上是 N 步 SCAN 算法的简化,即 FSCAN 只将磁盘请求队列分成两个 子队列。一个是由当前所有请求磁盘 I/O 的进程形成的队列,由磁盘调度按 SCAN 算法进 行处理。在扫描期间,将新出现的所有请求磁盘 I/O 的进程,放入另一个等待处理的请求队 列。这样,所有的新请求都将被推迟到下一次扫描时处理。 5.6.3 磁盘高速缓存 目前,磁盘的 I/O 速度远低于对内存的访问速度,通常要低上 4~6 个数量级。因此, 磁盘的 I/O 已成为计算机系统的瓶颈。于是,人们便千方百计地去提高磁盘 I/O 的速度,其 中最主要的技术便是采用磁盘高速缓存(Disk Cache)。 1.磁盘高速缓存的形式 这里所说的磁盘高速缓存,并非通常意义下的内存和 CPU 之间所增设的一个小容量高 速存储器,而是指利用内存中的存储空间来暂存从磁盘中读出的一系列盘块中的信息。因 ·198· 计算机操作系统 此,这里的高速缓存是一组在逻辑上属于磁盘,而物理上是驻留在内存中的盘块。高速缓 存在内存中可分成两种形式。第一种是在内存中开辟一个单独的存储空间来作为磁盘高速 缓存,其大小是固定的,不会受应用程序多少的影响;第二种是把所有未利用的内存空间 变为一个缓冲池,供请求分页系统和磁盘 I/O 时(作为磁盘高速缓存)共享。此时,高速缓存 的大小显然不再是固定的。当磁盘 I/O 的频繁程度较高时,该缓冲池可能包含更多的内存空 间;而在应用程序运行得较多时,该缓冲池可能只剩下较少的内存空间。 2.数据交付方式 数据交付(Data Delivery)是指将磁盘高速缓存中的数据传送给请求者进程。当有一进程 请求访问某个盘块中的数据时,由核心先去查看磁盘高速缓冲器,看其中是否存在进程所 需访问的盘块数据的拷贝。若有其拷贝,便直接从高速缓存中提取数据交付给请求者进程, 这样,就避免了访盘操作,从而使本次访问速度提高 4~6 个数量级;否则,应先从磁盘中 将所要访问的数据读入并交付给请求者进程,同时也将数据送高速缓存。当以后又需要访 问该盘块的数据时,便可直接从高速缓存中提取。 系统可以采取两种方式将数据交付给请求进程: (1) 数据交付。这是直接将高速缓存中的数据,传送到请求者进程的内存工作区中。 (2) 指针交付。这是只将指向高速缓存中某区域的指针交付给请求者进程。 后一种方式由于所传送的数据量少,因而节省了数据从磁盘高速缓存到进程的内存工 作区的时间。 3.置换算法 如同请求调页(段)一样,在将磁盘中的盘块数据读入高速缓存时,同样会出现因高速缓 存中已装满盘块数据而需要将该数据先换出的问题。相应地,也必然存在着采用哪种置换 算法的问题。较常用的置换算法仍然是最近最久未使用算法 LRU、最近未使用算法 NRU 及 最少使用算法 LFU 等。 由于请求调页中的联想存储器与高速缓存(磁盘 I/O 中)的工作情况不同,因而使得在置 换算法中所应考虑的问题也有所差异。因此,现在不少系统在设计其高速缓存的置换算法 时,除了考虑到最近最久未使用这一原则外,还考虑了以下几点: 1) 访问频率 通常,每执行一条指令时,便可能访问一次联想存储器,亦即联想存储器的访问频率, 基本上与指令执行的频率相当。而对高速缓存的访问频率,则与磁盘 I/O 的频率相当。因此, 对联想存储器的访问频率远远高于对高速缓存的访问频率。 2) 可预见性 在高速缓存中的各盘块数据,有哪些数据可能在较长时间内不会再被访问,又有哪些 数据可能很快就再被访问,会有相当一部分是可预知的。例如,对二次地址及目录块等, 在它被访问后,可能会很久都不再被访问。又如,正在写入数据的未满盘块,可能会很快 又被访问。 3) 数据的一致性 由于高速缓存是做在内存中的,而内存一般又是一种易失性的存储器,一旦系统发生 故障,存放在高速缓存中的数据将会丢失;而其中有些盘块(如索引结点盘块)中的数据已被 第五章 设 备 管 理 ·199· 修改,但尚未拷回磁盘,因此,当系统发生故障后,可能会造成数据的不一致性。 基于上述考虑,在有的系统中便将高速缓存中的所有盘块数据拉成一条 LRU 链。对于 那些会严重影响到数据一致性的盘块数据和很久都可能不再使用的盘块数据,都放在 LRU 链的头部,使它们能被优先写回磁盘,以减少发生数据不一致性的概率,或者可以尽早地 腾出高速缓存的空间。对于那些可能在不久之后便要再使用的盘块数据,应挂在 LRU 链的 尾部,以便在不久以后需要时,只要该数据块尚未从链中移至链首而被写回磁盘,便可直 接到高速缓存中(即 LRU 链中)去找到它们。 4.周期性地写回磁盘 还有一种情况值得注意: 那就是根据 LRU 算法,那些经常要被访问的盘块数据,可能 会一直保留在高速缓存中,长期不会被写回磁盘。(注意,LRU 链意味着链中任一元素在被 访问之后,总是又被挂到链尾而不被写回磁盘;只是一直未被访问的元素,才有可能移到 链首,而被写回磁盘。)例如,一位学者一上班便开始撰写论文,并边写边修改,他正在写 作的论文就一直保存在高速缓存的 LRU 链中。如果在快下班时,系统突然发生故障,这样, 存放在高速缓存中的已写论文将随之消失,致使他枉费了一天的劳动。 为了解决这一问题,在 UNIX 系统中专门增设了一个修改(update)程序,使之在后台运 行,该程序周期性地调用一个系统调用 SYNC。该调用的主要功能是强制性地将所有在高速 缓存中已修改的盘块数据写回磁盘。一般是把两次调用 SYNC 的时间间隔定为 30 s。这样, 因系统故障所造成的工作损失不会超过 30 s 的劳动量。而在 MS-DOS 中所采用的方法是: 只 要高速缓存中的某盘块数据被修改,便立即将它写回磁盘,并将这种高速缓存称为“写穿 透、高速缓存”(write-through cache)。MS-DOS 所采用的写回方式,几乎不会造成数据的丢 失,但须频繁地启动磁盘。 5.6.4 提高磁盘 I/O 速度的其它方法 在系统中设置了磁盘高速缓存后,能显著地减少等待磁盘 I/O 的时间。本小节再介绍几 种能有效地提高磁盘 I/O 速度的方法,这些方法已被许多系统采用。 1.提前读(Read-ahead) 用户(进程)对文件进行访问时,经常采用顺序访问方式,即顺序地访问文件各盘块的数 据。在这种情况下,在读当前块时可以预知下一次要读的盘块。因此,可以采取预先读方 式,即在读当前块的同时,还要求将下一个盘块(提前读的块)中的数据也读入缓冲区。这样, 当下一次要读该盘块中的数据时,由于该数据已被提前读入缓冲区,因而此时便可直接从 缓冲区中取得下一盘块的数据,而不需再去启动磁盘 I/O,从而大大减少了读数据的时间。 这也就等效于提高了磁盘 I/O 的速度。“提前读”功能已被广泛采用,如在 UNIX 系统、OS/2, 以及在 3 Plus 和 Netware 等的网络 OS 中,都已采用该功能。 2.延迟写 延迟写是指在缓冲区 A 中的数据,本应立即写回磁盘,但考虑到该缓冲区中的数据在不 久之后可能还会再被本进程或其它进程访问(共享资源),因而并不立即将该缓冲区 A 中的数 据写入磁盘,而是将它挂在空闲缓冲区队列的末尾。随着空闲缓冲区的使用,缓冲区也缓缓 往前移动,直至移到空闲缓冲队列之首。当再有进程申请到该缓冲区时,才将该缓冲区中的 ·200· 计算机操作系统 数据写入磁盘,而把该缓冲区作为空闲缓冲区分配出去。当该缓冲区 A 仍在队列中时,任何 访问该数据的进程,都可直接读出其中的数据而不必去访问磁盘。这样,又可进一步减小等 效的磁盘 I/O 时间。同样,“延迟写”功能已在 UNIX 系统、OS/2 等 OS 中被广泛采用。 3.优化物理块的分布 另一种提高磁盘 I/O 速度的重要措施是优化文件物理块的分布,使磁头的移动距离最 小。虽然链接分配和索引分配方式都允许将一个文件的物理块分散在磁盘的任意位置,但 如果将一个文件的多个物理块安排得过于分散,会增加磁头的移动距离。例如,将文件的 第一个盘块安排在最里的一条磁道上,而把第二个盘块安排在最外的一条磁道上,这样, 在读完第一个盘块后转去读第二个盘块时,磁头要从最里的磁道移到最外的磁道上。如果 我们将这两个数据块安排在属于同一条磁道的两个盘块上,显然会由于消除了磁头在磁道 间的移动,而大大提高对这两个盘块的访问速度。 对文件盘块位置的优化,应在为文件分配盘块时进行。如果系统中的空白存储空间是 采用位示图方式表示的,则要将同属于一个文件的盘块安排在同一条磁道上或相邻的磁道 上是十分容易的事。这时,只要从位示图中找到一片相邻接的多个空闲盘块即可。但当系 统采用线性表(链)法来组织空闲存储空间时,要为一文件分配多个相邻接的盘块,就要困难 一些。此时,我们可以将在同一条磁道上的若干个盘块组成一簇,例如,一簇包括 4 个盘 块,在分配存储空间时,以簇为单位进行分配。这样就可以保证在访问这几个盘块时,不 必移动磁头或者仅移动一条磁道的距离,从而减少了磁头的平均移动距离。 4.虚拟盘 所谓虚拟盘,是指利用内存空间去仿真磁盘,又称为 RAM 盘。该盘的设备驱动程序也 可以接受所有标准的磁盘操作,但这些操作的执行,不是在磁盘上而是在内存中。这些对 用户都是透明的。换言之,用户并不会发现这与真正的磁盘操作有什么不同,而仅仅是略 微快些而已。虚拟盘的主要问题是:它是易失性存储器,故一旦系统或电源发生故障,或 系统再启动时,原来保存在虚拟盘中的数据将会丢失。因此,虚拟盘通常用于存放临时文 件,如编译程序所产生的目标程序等。虚拟盘与磁盘高速缓存的主要区别在于: 虚拟盘中的 内容完全由用户控制,而高速磁盘缓存中的内容则是由 OS 控制的。例如,RAM 盘在开始 时是空的,仅当用户(程序)在 RAM 盘中创建了文件后,RAM 盘中才有内容。 5.6.5 廉价磁盘冗余阵列 廉价磁盘冗余阵列(RAID,Redundant Array of Inexpensive Disk)是 1987 年由美国加利福 尼亚大学伯克莱分校提出的,现在已开始广泛地应用于大、中型计算机系统和计算机网络 中。它是利用一台磁盘阵列控制器,来统一管理和控制一组(几台到几十台)磁盘驱动器,组 成一个高度可靠的、快速的大容量磁盘系统。 1.并行交叉存取 为了提高对磁盘的访问速度,已把在大、中型机中应用的交叉存取(Interleave)技术应用 到了磁盘存储系统中。在该系统中,有多台磁盘驱动器,系统将每一盘块中的数据分为若 干个子盘块数据,再把每一个子盘块的数据分别存储到各个不同磁盘中的相同位置上。在 以后,当要将一个盘块的数据传送到内存时,采取并行传输方式,将各个盘块中的子盘块 第五章 设 备 管 理 ·201· 数据同时向内存中传输,从而使传输时间大大减少。例如,在存放一个文件时,可将该文 件中的第一个数据子块放在第一个磁盘驱动器上;将文件的第二个数据子块放在第二个磁 盘上;……;将第 N 个数据子块,放在第 N 个驱动器上。以后在读取数据时,采取并行读 取方式,即同时从第 1~N 个数据子块读出数据,这样便把磁盘 I/O 的速度提高了 N-1 倍。 图 5-29 示出了磁盘并行交叉存取方式。 1 2 3 N … 图 5-29 磁盘并行交叉存取方式 2.RAID 的分级 RAID 在刚被推出时,是分成 6 级的,即 RAID 0 级至 RAID 5 级,后来又增加了 RAID 6 级和 RAID 7 级。 (1) RAID 0 级。本级仅提供了并行交叉存取。它虽能有效地提高磁盘 I/O 速度,但并 无冗余校验功能,致使磁盘系统的可靠性不好。只要阵列中有一个磁盘损坏,便会造成不 可弥补的数据丢失,故较少使用。 (2) RAID 1 级。它具有磁盘镜像功能,例如,当磁盘阵列中具有 8 个盘时,可利用其 中 4 个作为数据盘,另外 4 个作为镜像盘,在每次访问磁盘时,可利用并行读、写特性, 将数据分块同时写入主盘和镜像盘。故其比传统的镜像盘速度快,但其磁盘容量的利用率 只有 50%,它是以牺牲磁盘容量为代价的。 (3) RAID 3 级。这是具有并行传输功能的磁盘阵列。它利用一台奇偶校验盘来完成数 据的校验功能,比起磁盘镜像,它减少了所需要的冗余磁盘数。例如,当阵列中只有 7 个 盘时,可利用 6 个盘作数据盘,一个盘作校验盘。磁盘的利用率为 6/7。RAID 3 级经常用 于科学计算和图像处理。 (4) RAID 5 级。这是一种具有独立传送功能的磁盘阵列。每个驱动器都各有自己独立 的数据通路,独立地进行读/写,且无专门的校验盘。用来进行纠错的校验信息,是以螺旋 (Spiral)方式散布在所有数据盘上。RAID 5 级常用于 I/O 较频繁的事务处理中。 (5) RAID 6 级和 RAID 7 级。这是强化了的 RAID。在 RAID 6 级的阵列中,设置了一 个专用的、可快速访问的异步校验盘。该盘具有独立的数据访问通路,具有比 RAID 3 级及 RAID 5 级更好的性能,但其性能改进得很有限,且价格昂贵。RAID 7 级是对 RAID 6 级的 改进,在该阵列中的所有磁盘,都具有较高的传输速率和优异的性能,是目前最高档次的 磁盘阵列,但其价格也较高。 3.RAID 的优点 RAID 自 1988 年问世后,便引起了人们的普遍关注,并很快地流行起来。这主要是因 为 RAID 具有下述一系列明显的优点: (1) 可靠性高。RAID 最大的特点就是它的高可靠性。除了 RAID 0 级外,其余各级都采 用了容错技术。当阵列中某一磁盘损坏时,并不会造成数据的丢失,因为它既可实现磁盘镜 像,又可实现磁盘双工,还可实现其它的冗余方式。所以此时可根据其它未损坏磁盘中的信 ·202· 计算机操作系统 息,来恢复已损坏的盘中的信息。它与单台磁盘机相比,其可靠性高出了一个数量级。 (2) 磁盘 I/O 速度高。由于磁盘阵列可采取并行交叉存取方式,故可将磁盘 I/O 速度提 高 N-1 倍(N 为磁盘数目)。或者说,磁盘阵列可将磁盘 I/O 速度提高数倍至数十倍。 (3) 性能/价格比高。利用 RAID 技术来实现大容量高速存储器时,其体积与具有相同容 量和速度的大型磁盘系统相比,只是后者的 1/3,价格也只是后者的 1/3,且可靠性高。换 言之,它仅以牺牲 1/N 的容量为代价,换取了高可靠性;而不像磁盘镜像及磁盘双工那样, 须付出 50%容量的代价。 习题 1. 试说明设备控制器的组成。 2. 为了实现 CPU 与设备控制器间的通信,设备控制器应具备哪些功能? 3. 什么是字节多路通道?什么是数组选择通道和数组多路通道? 4. 如何解决因通道不足而产生的瓶颈问题? 5. 试对 VESA 及 PCI 两种总线进行比较。 6. 试说明推动 I/O 控制发展的主要因素是什么? 7. 有哪几种 I/O 控制方式?各适用于何种场合? 8. 试说明 DMA 的工作流程。 9. 引入缓冲的主要原因是什么? 10. 在单缓冲情况下,为什么系统对一块数据的处理时间为 max(C,T) + M ? 11. 为什么在双缓冲情况下,系统对一块数据的处理时间为 max(T,C) ? 12. 试绘图说明把多缓冲用于输出时的情况。 13. 试说明收容输入工作缓冲区和提取输出工作缓冲区的工作情况。 14. 何谓安全分配方式和不安全分配方式? 15. 为何要引入设备独立性?如何实现设备的独立性? 16. 在考虑到设备的独立性时,应如何分配独享设备? 17. 何谓设备虚拟?实现设备虚拟时所依赖的关键技术是什么? 18. 试说明 SPOOLing 系统的组成。 19. 在实现后台打印时,SPOOLing 系统应为请求 I/O 的进程提供哪些服务? 20. 试说明设备驱动程序具有哪些特点。 21. 试说明设备驱动程序应完成哪些功能。 22. 设备中断处理程序通常需完成哪些工作? 23. 磁盘访问时间由哪几部分组成?每部分时间应如何计算? 24. 目前常用的磁盘调度算法有哪几种?每种算法优先考虑的问题是什么? 25. 为什么要引入磁盘高速缓冲?何谓磁盘高速缓冲? 26. 在设计磁盘高速缓冲时,如何实现数据交付? 27. 何谓提前读、延迟写和虚拟盘? 28. 廉价磁盘冗余阵列是如何提高对磁盘的访问速度和可靠性的? 第六章 文 件 管 理 ·203· 第六章 文 件 管 理 在现代计算机系统中,要用到大量的程序和数据,因内存容量有限,且不能长期保存, 故而平时总是把它们以文件的形式存放在外存中,需要时再随时将它们调入内存。如果由 用户直接管理外存上的文件,不仅要求用户熟悉外存特性,了解各种文件的属性,以及它 们在外存上的位置,而且在多用户环境下,还必须能保持数据的安全性和一致性。显然, 这是用户所不能胜任、也不愿意承担的工作。于是,取而代之的便是在操作系统中又增加 了文件管理功能,即构成一个文件系统,负责管理在外存上的文件,并把对文件的存取、 共享和保护等手段提供给用户。这不仅方便了用户,保证了文件的安全性,还可有效地提 高系统资源的利用率。 6.1 文件和文件系统 在现代 OS 中,几乎毫无例外地是通过文件系统来组织和管理在计算机中所存储的大量 程序和数据的;或者说,文件系统的管理功能,是通过把它所管理的程序和数据组织成一 系列文件的方法来实现的。而文件则是指具有文件名的若干相关元素的集合。元素通常是 记录,而记录又是一组有意义的数据项的集合。可见,基于文件系统的概念,可以把数据 组成分为数据项、记录和文件三级。 6.1.1 文件、记录和数据项 1.数据项 在文件系统中,数据项是最低级的数据组织形式,可把它分成以下两种类型: (1) 基本数据项。这是用于描述一个对象的某种属性的字符集,是数据组织中可以命名 的最小逻辑数据单位,即原子数据,又称为数据元素或字段。它的命名往往与其属性一致。 例如,用于描述一个学生的基本数据项有学号、姓名、年龄、所在班级等。 (2) 组合数据项。它是由若干个基本数据项组成的,简称组项。例如,经理便是个组项, 它由正经理和副经理两个基本项组成。又如,工资也是个组项,它可由基本工资、工龄工 资和奖励工资等基本项所组成。 基本数据项除了数据名外,还应有数据类型。因为基本项仅是描述某个对象的属性, 根据属性的不同,需要用不同的数据类型来描述。例如,在描述学生的学号时,应使用整 数;描述学生的姓名则应使用字符串(含汉字);描述性别时,可用逻辑变量或汉字。可见, 由数据项的名字和类型两者共同定义了一个数据项的“型”。而表征一个实体在数据项上的 数据则称为“值”。例如,学号/30211、姓名/王有年、性别/男等。 ·204· 计算机操作系统 2.记录 记录是一组相关数据项的集合,用于描述一个对象在某方面的属性。一个记录应包含 哪些数据项,取决于需要描述对象的哪个方面。而一个对象,由于他所处的环境不同可把 他作为不同的对象。例如,一个学生,当把他作为班上的一名学生时,对他的描述应使用 学号、姓名、年龄及所在系班,也可能还包括他所学过的课程的名称、成绩等数据项。但 若把学生作为一个医疗对象时,对他描述的数据项则应使用诸如病历号、姓名、性别、出 生年月、身高、体重、血压及病史等项。 在诸多记录中,为了能惟一地标识一个记录,必须在一个记录的各个数据项中,确定 出一个或几个数据项,把它们的集合称为关键字(key)。或者说,关键字是惟一能标识一个 记录的数据项。通常,只需用一个数据项作为关键字。例如,前面的病历号或学号便可用 来从诸多记录中标识出惟一的一个记录。然而有时找不到这样的数据项,只好把几个数据 项定为能在诸多记录中惟一地标识出某个记录的关键字。 3.文件 文件是指由创建者所定义的、具有文件名的一组相关元素的集合,可分为有结构文件 和无结构文件两种。在有结构的文件中,文件由若干个相关记录组成;而无结构文件则被 看成是一个字符流。文件在文件系统中是一个最大的数据单位,它描述了一个对象集。例 如,可以将一个班的学生记录作为一个文件。一个文件必须要有一个文件名,它通常是由 一串 ASCII 码或(和)汉字构成的,名字的长度因系统不同而异。如在有的系统中把名字规定 为 8 个字符,而在有的系统中又规定可用 14 个字符。用户利用文件名来访问文件。 此外,文件应具有自己的属性,属性可以包括: (1) 文件类型。可以从不同的角度来规定文件的类型,如源文件、目标文件及可执行文 件等。 (2) 文件长度。文件长度指文件的当前长度,长度的单位可以是字节、字或块,也可能 是最大允许的长度。 (3) 文件的物理位置。该项属性通常是用于指示文件在哪一个设备上及在该设备的哪个 位置的指针。 (4) 文件的建立时间。这是指文件最后一次的修改时间等。 图 6-1 示出了文件、记录和数据项之间的层次关系。 文件 记录1 记录2 … 记录n 数据项1 数据项2 … 数据项n 图 6-1 文件、记录和数据项之间的层次关系 第六章 文 件 管 理 ·205· 6.1.2 文件类型和文件系统模型 1.文件类型 为了便于管理和控制文件而将文件分成若干种类型。由于不同系统对文件的管理方式 不同,因而它们对文件的分类方法也有很大差异。为了方便系统和用户了解文件的类型, 在许多 OS 中都把文件类型作为扩展名而缀在文件名的后面,在文件名和扩展名之间用“.” 号隔开。下面是常用的几种文件分类方法。 1) 按用途分类 根据文件的性质和用途的不同,可将文件分为三类: (1) 系统文件。这是指由系统软件构成的文件。大多数的系统文件只允许用户调用,但 不允许用户去读,更不允许修改;有的系统文件不直接对用户开放。 (2) 用户文件。指由用户的源代码、目标文件、可执行文件或数据等所构成的文件。用 户将这些文件委托给系统保管。 (3) 库文件。这是由标准子例程及常用的例程等所构成的文件。这类文件允许用户调用, 但不允许修改。 2) 按文件中数据的形式分类 按这种方式分类,也可把文件分为三类: (1) 源文件。这是指由源程序和数据构成的文件。通常由终端或输入设备输入的源程序 和数据所形成的文件都属于源文件。它通常是由 ASCII 码或汉字所组成的。 (2) 目标文件。这是指把源程序经过相应语言的编译程序编译过,但尚未经过链接程序 链接的目标代码所构成的文件。它属于二进制文件。通常,目标文件所使用的后缀名是 “.obj”。 (3) 可执行文件。这是指把编译后所产生的目标代码再经过链接程序链接后所形成的 文件。 3) 按存取控制属性分类 根据系统管理员或用户所规定的存取控制属性,可将文件分为三类: (1) 只执行文件。该类文件只允许被核准的用户调用执行,既不允许读,更不允许写。 (2) 只读文件。该类文件只允许文件主及被核准的用户去读,但不允许写。 (3) 读写文件。这是指允许文件主和被核准的用户去读或写的文件。 4) 按组织形式和处理方式分类 根据文件的组织形式和系统对其的处理方式,可将文件分为三类: (1) 普通文件:由 ASCII 码或二进制码组成的字符文件。一般用户建立的源程序文件、 数据文件、目标代码文件及操作系统自身代码文件、库文件、实用程序文件等都是普通文 件,它们通常存储在外存储设备上。 (2) 目录文件:由文件目录组成的,用来管理和实现文件系统功能的系统文件,通过目 录文件可以对其它文件的信息进行检索。由于目录文件也是由字符序列构成,因此对其可 进行与普通文件一样的种种文件操作。 (3) 特殊文件:特指系统中的各类 I/O 设备。为了便于统一管理,系统将所有的 ·206· 计算机操作系统 输入/输出设备都视为文件,按文件方式提供给用户使用,如目录的检索、权限的验证等都 与普通文件相似,只是对这些文件的操作是和设备驱动程序紧密相连的,系统将这些操作 转为对具体设备的操作。根据设备数据交换单位的不同,又可将特殊文件分为块设备文件 和字符设备文件。前者用于磁盘、光盘或磁带等块设备的 I/O 操作,而后者用于终端、打 印机等字符设备的 I/O 操作。 2.文件系统模型 图 6-2 示出了文件系统的模型。可将该模型分为三个层次,其最底层是对象及其属性; 中间层是对对象进行操纵和管理的软件集合;最高层是文件系统提供给用户的接口。 用户(程序) ↓↓↓↓ 文件系统接口 对对象操纵和管理的软件集合 对象及其属性 图 6-2 文件系统模型 1) 对象及其属性 文件管理系统管理的对象有:① 文件。它作为文件管理的直接对象。② 目录。为了 方便用户对文件的存取和检索,在文件系统中必须配置目录,每个目录项中,必须含有文 件名及该文件所在的物理地址(或指针)。对目录的组织和管理是方便用户和提高对文件存取 速度的关键。③ 磁盘(磁带)存储空间。文件和目录必定占用存储空间,对这部分空间的有 效管理,不仅能提高外存的利用率,而且能提高对文件的存取速度。 2) 对对象操纵和管理的软件集合 这是文件管理系统的核心部分。文件系统的功能大多是在这一层实现的,其中包括: 对 文件存储空间的管理、对文件目录的管理、用于将文件的逻辑地址转换为物理地址的机制、 对文件读和写的管理,以及对文件的共享与保护等功能。 3) 文件系统的接口 为方便用户使用文件系统,文件系统通常向用户提供两种类型的接口: (1) 命令接口。 这是指作为用户与文件系统交互的接口。 用户可通过键盘终端键入命 令,取得文件系统的服务。 (2) 程序接口。这是指作为用户程序与文件系统的接口。用户程序可通过系统调用来取 得文件系统的服务。 6.1.3 文件操作 用户通过文件系统所提供的系统调用实施对文件的操作。最基本的文件操作有: 创建文 件、删除文件、读文件、写文件、截断文件和设置文件的读/写位置。但对于一个实际的 OS, 为了方便用户使用文件而提供了更多的对文件的操作,如打开和关闭一个文件及改变文件 名等操作。 第六章 文 件 管 理 ·207· 1.最基本的文件操作 (1) 创建文件。在创建一个新文件时,系统首先要为新文件分配必要的外存空间,并在 文件系统的目录中,为之建立一个目录项。目录项中应记录新文件的文件名及其在外存的 地址等属性。 (2) 删除文件。当已不再需要某文件时,可将它从文件系统中删除。在删除时,系统应 先从目录中找到要删除文件的目录项,使之成为空项,然后回收该文件所占用的存储空间。 (3) 读文件。在读一个文件时,须在相应系统调用中给出文件名和应读入的内存目标地 址。此时,系统同样要查找目录,找到指定的目录项,从中得到被读文件在外存中的位置。 在目录项中,还有一个指针用于对文件的读/写。 (4) 写文件。在写一个文件时,须在相应系统调用中给出该文件名及该文件在内存中的 (源)地址。为此,也同样须先查找目录,找到指定文件的目录项,再利用目录中的写指针进 行写操作。 (5) 截断文件。如果一个文件的内容已经陈旧而需要全部更新时,一种方法是将此文件 删除,再重新创建一个新文件。但如果文件名及其属性均无改变时,则可采取另一种所谓的截 断文件的方法,此即将原有文件的长度设置为 0,或者说是放弃原有的文件内容。 (6) 设置文件的读/写位置。前述的文件读/写操作都只提供了对文件顺序存取的手段, 即每次都是从文件的始端读或写。设置文件读/写位置的操作,用于设置文件读/写指针的位 置,以便每次读/写文件时,不是从其始端而是从所设置的位置开始操作。也正因如此,才 能改顺序存取为随机存取。 2.文件的“打开”和“关闭”操作 当前 OS 所提供的大多数对文件的操作,其过程大致都是这样两步: 第一步是通过检索文 件目录来找到指定文件的属性及其在外存上的位置;第二步是对文件实施相应的操作,如读 文件或写文件等。当用户要求对一个文件实施多次读/写或其它操作时,每次都要从检索目录 开始。为了避免多次重复地检索目录,在大多数 OS 中都引入了“打开”(open)这一文件系统 调用,当用户第一次请求对某文件进行操作时,先利用 open 系统调用将该文件打开。 所谓“打开”,是指系统将指名文件的属性(包括该文件在外存上的物理位置)从外存拷 贝到内存打开文件表的一个表目中,并将该表目的编号(或称为索引)返回给用户。以后,当 用户再要求对该文件进行相应的操作时,便可利用系统所返回的索引号向系统提出操作请 求。系统这时便可直接利用该索引号到打开文件表中去查找,从而避免了对该文件的再次 检索。这样不仅节省了大量的检索开销,也显著地提高了对文件的操作速度。如果用户已 不再需要对该文件实施相应的操作时,可利用“关闭”(close)系统调用来关闭此文件,OS 将会把该文件从打开文件表中的表目上删除掉。 3.其它文件操作 为了方便用户使用文件,通常,OS 都提供了数条有关文件操作的系统调用,可将这些 调用分成若干类: 最常用的一类是有关对文件属性进行操作的,即允许用户直接设置和获得 文件的属性,如改变已存文件的文件名、改变文件的拥有者(文件主)、改变对文件的访问权, 以及查询文件的状态(包括文件类型、大小和拥有者以及对文件的访问权等);另一类是有关 目录的,如创建一个目录,删除一个目录,改变当前目录和工作目录等;此外,还有用于 ·208· 计算机操作系统 实现文件共享的系统调用和用于对文件系统进行操作的系统调用等。 值得说明的是,有许多文件操作都可以利用上述基本操作加以组合来实现。例如,创 建一个文件拷贝的操作,可利用两条基本操作来实现。其第一步是利用创建文件的系统调 用来创建一个新文件;第二步是将原有文件中的内容写入新文件中。 6.2 文件的逻辑结构 通常,文件是由一系列的记录组成的。文件系统设计的关键要素,是指将这些记录构 成一个文件的方法,以及将一个文件存储到外存上的方法。事实上,对于任何一个文件, 都存在着以下两种形式的结构: (1) 文件的逻辑结构(File Logical Structure)。这是从用户观点出发所观察到的文件组织 形式,是用户可以直接处理的数据及其结构,它独立于文件的物理特性,又称为文件组织(File Organization)。 (2) 文件的物理结构,又称为文件的存储结构,是指文件在外存上的存储组织形式。这 不仅与存储介质的存储性能有关,而且与所采用的外存分配方式有关。 无论是文件的逻辑结构,还是其物理结构,都会影响对文件的检索速度。本节只介绍 文件的逻辑结构。 对文件逻辑结构所提出的基本要求,首先是能提高检索速度,即在将大批记录组成文 件时,应有利于提高检索记录的速度和效率;其次是便于修改,即便于在文件中增加、删 除和修改一个或多个记录;第三是降低文件的存储费用,即减少文件占用的存储空间,不 要求大片的连续存储空间。 6.2.1 文件逻辑结构的类型 文件的逻辑结构可分为两大类,一类是有结构文件,这是指由一个以上的记录构成的 文件,故又把它称为记录式文件;其二是无结构文件,这是指由字符流构成的文件,故又 称为流式文件。 1.有结构文件 在记录式文件中,每个记录都用于描述实体集中的一个实体,各记录有着相同或不同 数目的数据项。记录的长度可分为定长和不定长两类。 (1) 定长记录。这是指文件中所有记录的长度都是相同的,所有记录中的各数据项都处 在记录中相同的位置,具有相同的顺序和长度。文件的长度用记录数目表示。对定长记录 的处理方便、开销小,所以这是目前较常用的一种记录格式,被广泛用于数据处理中。 (2) 变长记录。这是指文件中各记录的长度不相同。产生变长记录的原因,可能是由于 一个记录中所包含的数据项数目并不相同,如书的著作者、论文中的关键词等;也可能是 数据项本身的长度不定,例如,病历记录中的病因、病史;科技情报记录中的摘要等。不 论是哪一种,在处理前,每个记录的长度是可知的。 根据用户和系统管理上的需要,可采用多种方式来组织这些记录,形成下述的几种 文件: 第六章 文 件 管 理 ·209· (1) 顺序文件。这是由一系列记录按某种顺序排列所形成的文件。其中的记录通常是定 长记录,因而能用较快的速度查找文件中的记录。 (2) 索引文件。当记录为可变长度时,通常为之建立一张索引表,并为每个记录设置一 个表项,以加快对记录检索的速度。 (3) 索引顺序文件。这是上述两种文件构成方式的结合。它为文件建立一张索引表,为 每一组记录中的第一个记录设置一个表项。 2.无结构文件 如果说大量的数据结构和数据库是采用有结构的文件形式的话,则大量的源程序、可 执行文件、库函数等,所采用的就是无结构的文件形式,即流式文件。其长度以字节为单 位。对流式文件的访问,则是采用读/写指针来指出下一个要访问的字符。可以把流式文件 看做是记录式文件的一个特例。在 UNIX 系统中,所有的文件都被看做是流式文件,即使 是有结构文件,也被视为流式文件,系统不对文件进行格式处理。 6.2.2 顺序文件 1.逻辑记录的排序 文件是记录的集合。文件中的记录可以是任意顺序的,因此,它可以按照各种不同的 顺序进行排列。一般地,可归纳为以下两种情况: 第一种是串结构,各记录之间的顺序与关键字无关。通常的办法是由时间来决定,即 按存入时间的先后排列,最先存入的记录作为第一个记录,其次存入的为第二个记录……, 依此类推。 第二种情况是顺序结构,指文件中的所有记录按关键字(词)排列。可以按关键词的长短 从小到大排序,也可以从大到小排序;或按其英文字母顺序排序。 对顺序结构文件可有更高的检索效率,因为在检索串结构文件时,每次都必须从头开 始,逐个记录地查找,直至找到指定的记录,或查完所有的记录为止。而对顺序结构文件, 则可利用某种有效的查找算法,如折半查找法、插值查找法、跳步查找法等方法来提高检 索效率。 2.对顺序文件(Sequential File)的读/写操作 顺序文件中的记录可以是定长的,也可以是变长的。对于定长记录的顺序文件,如 果已知当前记录的逻辑地址,便很容易确定下一个记录的逻辑地址。在读一个文件时, 可设置一个读指针 Rptr,令它指向下一个记录的首地址,每当读完一个记录时,便 执行 Rptr:=Rptr + L 操作,使之指向下一个记录的首地址,其中的 L 为记录长度。类似地,在写一个文件时, 也应设置一个写指针 Wptr,使之指向要写的记录的首地址。同样,在每写完一个记录时, 又须执行以下操作: Wptr:=Wptr + L ·210· 计算机操作系统 对于变长记录的顺序文件,在顺序读或写时的情况相似,但应分别为它们设置读或写 指针,在每次读或写完一个记录后,须将读或写指针加上 Li。Li 是刚读或刚写完的记录的 长度。图 6-3 所示为定长和变长记录文件。 R0 R1 R2 R3 Rptr Ri … 0 L L0 L L R0 2L L 3L L1 R1 L 4L … Wptr iL Li L (i+1)L Ri 0 L0 L0+1 L1 L0+L1+2 i-1 ∑k=(0Lk+1) Li i ∑k=(0Lk+1) … … (a) 定长记录文件 (b) 变长记录文件 图 6-3 定长和变长记录文件 3.顺序文件的优缺点 顺序文件的最佳应用场合是在对诸记录进行批量存取时,即每次要读或写一大批记录 时。此时,对顺序文件的存取效率是所有逻辑文件中最高的;此外,也只有顺序文件才能 存储在磁带上,并能有效地工作。 在交互应用的场合,如果用户(程序)要求查找或修改单个记录,为此系统便要去逐个地 查找诸记录。这时,顺序文件所表现出来的性能就可能很差,尤其是当文件较大时,情况 更为严重。例如,有一个含有 104 个记录的顺序文件,如果对它采用顺序查找法去查找一个 指定的记录,则平均需要查找 5×103 个记录;如果是可变长记录的顺序文件,则为查找一 个记录所需付出的开销将更大,这就限制了顺序文件的长度。 顺序文件的另一个缺点是,如果想增加或删除一个记录都比较困难。为了解决这一问 题, 可以为顺序文件配置一个运行记录文件(Log File),或称为事务文件(Transaction File), 把试图增加、删除或修改的信息记录于其中,规定每隔一定时间,例如 4 小时,将运行记 录文件与原来的主文件加以合并,产生一个按关键字排序的新文件。 6.2.3 索引文件 对于定长记录文件,如果要查找第 i 个记录,可直接根据下式计算来获得第 i 个记录相 对于第一个记录首址的地址: Ai = i × L 然而,对于可变长度记录的文件,要查找其第 i 个记录时,须首先计算出该记录的首地 址。为此,须顺序地查找每个记录,从中获得相应记录的长度 Li,然后才能按下式计算出 第 i 个记录的首址。假定在每个记录前用一个字节指明该记录的长度,则 i −1 Ai = ∑ Li + i i=0 第六章 文 件 管 理 ·211· 可见,对于定长记录,除了可以方便地实现顺序存取外,还可较方便地实现直接存取。 然而,对于变长记录就较难实现直接存取了,因为用直接存取方法来访问变长记录文件中 的一个记录是十分低效的,其检索时间也很难令人接受。为了解决这一问题,可为变长记 录文件建立一张索引表,对主文件中的每个记录,在索引表中设有一个相应的表项,用于 记录该记录的长度 L 及指向该记录的指针(指向该记录在逻辑地址空间的首址)。由于索引表 是按记录键排序的,因此,索引表本身是一个定长记录的顺序文件,从而也就可以方便地 实现直接存取。图 6-4 示出了索引文件(Index File)的组织形式。 索引号 长度 m 指针 ptr R0 0 m0 R1 1 m1 … … i mi Ri … … 索引表 逻辑文件 图 6-4 索引文件的组织 在对索引文件进行检索时,首先是根据用户(程序)提供的关键字,并利用折半查找法去 检索索引表,从中找到相应的表项;再利用该表项中给出的指向记录的指针值,去访问所 需的记录。而每当要向索引文件中增加一个新记录时,便须对索引表进行修改。由于索引 文件可有较快的检索速度,故它主要用于对信息处理的及时性要求较高的场合,例如,飞 机订票系统。使用索引文件的主要问题是,它除了有主文件外,还须配置一张索引表,而 且每个记录都要有一个索引项,因此提高了存储费用。 6.2.4 索引顺序文件 索引顺序文件(Index Sequential File)可能是最常见的一种逻辑文件形式。它有效地克服 了变长记录文件不便于直接存取的缺点,而且所付出的代价也不算太大。前已述及,它是 顺序文件和索引文件相结合的产物。它将顺序文件中的所有记录分为若干个组(例如,50 个 记录为一个组);为顺序文件建立一张索引表,在索引表中为每组中的第一个记录建立一个 索引项,其中含有该记录的键值和指向该记录的指针。索引顺序文件如图 6-5 所示。 键 An Qi Bao Rong Chen Lin 逻辑地址 姓名 An Qi An Kang 其它属性 Bao Rong … 图 6-5 索引顺序文件 逻辑文件 ·212· 计算机操作系统 在对索引顺序文件进行检索时,首先也是利用用户(程序)所提供的关键字以及某种查 找算法去检索索引表,找到该记录所在记录组中第一个记录的表项,从中得到该记录组第 一个记录在主文件中的位置;然后,再利用顺序查找法去查找主文件,从中找到所要求的 记录。 如果在一个顺序文件中所含有的记录数为 N,则为检索到具有指定关键字的记录,平均 须查找 N/2 个记录;但对于索引顺序文件,则为能检索到具有指定关键字的记录,平均只 要查找 N 个记录数,因而其检索效率 S 比顺序文件约提高 N /2 倍。例如,有一个顺序 文件含有 10 000 个记录,平均须查找的记录数为 5000 个。但对于索引顺序文件,则平均只 须查找 100 个记录。可见,它的检索效率是顺序文件的 50 倍。 但对于一个非常大的文件,为找到一个记录而须查找的记录数目仍然很多,例如,对 于一个含有 106 个记录的顺序文件,当把它作为索引顺序文件时,为找到一个记录,平均须 查找 1000 个记录。为了进一步提高检索效率,可以为顺序文件建立多级索引,即为索引文 件再建立一张索引表,从而形成两级索引表。例如,对于一个含有 106 个记录的顺序文件, 可先为该文件建立一张低级索引表,每 100 个记录为一组,故低级索引表应含有 104 个表项, 而每个表项中存放顺序文件中每个组第一个记录的记录键值和指向该记录的指针,然后再 为低级索引表建立一张高级索引表。这时,也同样是每 100 个索引表项为一组,故具有 102 个表项。这里的每个表项中存放的是低级索引表每组第一个表项中的关键字和指向该表项 的指针。此时,为找到一个具有指定关键字的记录,所须查找的记录数平均为 50+50+50=150, 或者可表示为(3/2) 3 N 。其中,N 是顺序文件中记录的个数。注意,在未建立索引文件时所 需查找的记录数平均为 50 万个;对于建立了一级索引的顺序索引文件,平均需查找 1000 次;建立两级索引的顺序索引文件,平均只需查找 150 次。 6.2.5 直接文件和哈希文件 1.直接文件 采用前述几种文件结构对记录进行存取时,都须利用给定的记录键值,先对线性表或 链表进行检索,以找到指定记录的物理地址。然而对于直接文件,则可根据给定的记录键 值,直接获得指定记录的物理地址。换言之,记录键值本身就决定了记录的物理地址。这 种由记录键值到记录物理地址的转换被称为键值转换(Key to address transformation)。组织直 接文件的关键,在于用什么方法进行从记录值到物理地址的转换。 2.哈希(Hash)文件 这是目前应用最为广泛的一种直接文件。它利用 Hash 函数(或称散列函数),可将记录 键值转换为相应记录的地址。但为了能实现文件存储空间的动态分配,通常由 Hash 函数所 求得的并非是相应记录的地址,而是指向一目录表相应表目的指针,该表目的内容指向相 应记录所在的物理块,如图 6-6 所示。例如,若令 K 为记录键值,用 A 作为通过 Hash 函数 H 的转换所形成的该记录在目录表中对应表目的位置,则有关系 A=H(K)。通常,把 Hash 函数作为标准函数存于系统中,供存取文件时调用。 第六章 文 件 管 理 目录表 键值 Hash函数 f ·213· 图 6-6 Hash 文件的逻辑结构 6.3 外存分配方式 由于磁盘具有可直接访问的特性,故当利用磁盘来存放文件时,具有很大的灵活性。 在为文件分配外存空间时所要考虑的主要问题是:怎样才能有效地利用外存空间和如何提 高对文件的访问速度。目前,常用的外存分配方法有连续分配、链接分配和索引分配三种。 通常,在一个系统中,仅采用其中的一种方法来为文件分配外存空间。 如前所述,文件的物理结构直接与外存分配方式有关。在采用不同的分配方式时,将 形成不同的文件物理结构。例如,在采用连续分配方式时的文件物理结构,将是顺序式的 文件结构;链接分配方式将形成链接式文件结构;而索引分配方式则将形成索引式文件 结构。 6.3.1 连续分配 1.连续分配方式 连续分配(Continuous Allocation)要求为每一个文件分配一组相邻接的盘块。一组盘块的 地址定义了磁盘上的一段线性地址。例如,第一个盘块的地址为 b,则第二个盘块的地址为 b+1,第三个盘块的地址为 b+2……。通常,它们都位于一条磁道上,在进行读/写时,不必 移动磁头,仅当访问到一条磁道的最后一个盘块后,才需要移到下一条磁道,于是又去连 续地读/写多个盘块。在采用连续分配方式时,可把逻辑文件中的记录顺序地存储到邻接的 各物理盘块中,这样所形成的文件结构称为顺序文件结构,此时的物理文件称为顺序文件。 这种分配方式保证了逻辑文件中的记录顺序与存储器中文件占用盘块的顺序的一致性。为 使系统能找到文件存放的地址,应在目录项的“文件物理地址”字段中,记录该文件第一 个记录所在的盘块号和文件长度(以盘块数进行计量)。图 6-7 示出了连续分配的情况。图中 假定了记录与盘块的大小相同。Count 文件的第一个盘块号是 0,文件长度为 2,因此是在 盘块号为 0 和 1 的两盘块中存放文件 1 的数据。 ·214· 计算机操作系统 count 0 1 2 3 4 f 5 6 7 8 9 10 11 12 13 tr 14 15 mail 16 17 18 19 20 21 22 23 24 25 26 27 list 28 29 30 31 目录 file start length count 0 2 tr 15 3 mail 21 6 list 29 3 f 72 图 6-7 磁盘空间的连续分配 如同内存的动态分区分配一样,随着文件建立时空间的分配和文件删除时空间的回收, 将使磁盘空间被分割成许多小块,这些较小的连续区已难于用来存储文件,此即外存的碎 片。同样,我们也可以利用紧凑的方法,将盘上所有的文件紧靠在一起,把所有的碎片拼 接成一大片连续的存储空间。例如,可以运行一个再装配例程(repack routine),由它将磁盘 A 上的大量文件拷贝到一张软盘 B 或几张软盘(C,D,…)上,并释放原来的 A 盘,使之成 为一个空闲盘。然后再将软盘 B(C,D,…)上的文件拷回 A 盘上。这种方法能将含有多个 文件的盘上的所有空闲盘块都集中在一起,从而消除了外部碎片。但为了将外存上的空闲 空间进行一次紧凑,所花费的时间远比将内存紧凑一次所花费的时间多得多。 2.连续分配的主要优缺点 连续分配的主要优点如下: (1) 顺序访问容易。访问一个占有连续空间的文件非常容易。系统可从目录中找到该顺 序文件所在的第一个盘块号,从此开始顺序地、逐个盘块地往下读/写。连续分配也支持直 接存取。例如,要访问一个从 b 块开始存放的文件中的第 i 个盘块的内容,就可直接访问 b+i 号盘块。 (2) 顺序访问速度快。因为由连续分配所装入的文件,其所占用的盘块可能是位于一条 或几条相邻的磁道上,这时,磁头的移动距离最少,因此,这种对文件访问的速度是几种 存储空间分配方式中最高的一种。 连续分配的主要缺点如下: (1) 要求有连续的存储空间。要为每一个文件分配一段连续的存储空间,这样,便会产 生出许多外部碎片,严重地降低了外存空间的利用率。如果是定期地利用紧凑方法来消除 碎片,则又需花费大量的机器时间。 (2) 必须事先知道文件的长度。要将一个文件装入一个连续的存储区中,必须事先知道 文件的大小,然后根据其大小,在存储空间中找出一块其大小足够的存储区,将文件装入。 在有些情况下,知道文件的大小是件非常容易的事,如可拷贝一个已存文件。但有时却很 难,在此情况下,只能靠估算。如果估计的文件大小比实际文件小,就可能因存储空间不 第六章 文 件 管 理 ·215· 足而中止文件的拷贝,须再要求用户重新估算,然后再次执行。这样,显然既费时又麻烦。 这就促使用户往往将文件长度估得比实际的大,甚至使所计算的文件长度比实际长度大得 多,显然,这会严重地浪费外存空间。对于那些动态增长的文件,由于开始时文件很小, 在运行中逐渐增大,比如,这种增长要经历几天、几个月。在此情况下,即使事先知道文 件的最终大小,在采用预分配存储空间的方法时,显然也将是很低效的,即它使大量的存 储空间长期地空闲着。 6.3.2 链接分配 如同内存管理一样,连续分配所存在的问题就在于: 必须为一个文件分配连续的磁盘空 间。如果在将一个逻辑文件存储到外存上时,并不要求为整个文件分配一块连续的空间, 而是可以将文件装到多个离散的盘块中,这样也就可以消除上述缺点。在采用链接分配 (Chained Allocation)方式时,可通过在每个盘块上的链接指针,将同属于一个文件的多个离 散的盘块链接成一个链表,把这样形成的物理文件称为链接文件。 由于链接分配是采取离散分配方式,消除了外部碎片,故而显著地提高了外存空间的 利用率;又因为是根据文件的当前需要,为它分配必需的盘块,当文件动态增长时,可动 态地再为它分配盘块,故而无需事先知道文件的大小。此外,对文件的增、删、改也十分 方便。 链接方式又可分为隐式链接和显式链接两种形式。 1.隐式链接 在采用隐式链接分配方式时,在文件目录的每个目录项中,都须含有指向链接文件第 一个盘块和最后一个盘块的指针。图 6-8 中示出了一个占用 5 个盘块的链接式文件。在相 应的目录项中,指示了其第一个盘块号是 9,最后一个盘块号是 25。而在每个盘块中都含 有一个指向下一个盘块的指针,如在第一个盘块 9 中设置了第二个盘块的盘块号是 16;在 16 号盘块中又设置了第三个盘块的盘块号 1。如果指针占用 4 个字节,对于盘块大小为 512 字节的磁盘,则每个盘块中只有 508 个字节可供用户使用。 0 4 8 12 16 1 20 24 28 1 10 2 3 5 6 7 9 16 10 25 11 13 14 15 17 18 19 21 22 23 25 -1 26 27 29 30 31 目录 file start end jeep 9 25 图 6-8 磁盘空间的链接式分配 ·216· 计算机操作系统 隐式链接分配方式的主要问题在于:它只适合于顺序访问,它对随机访问是极其低效 的。如果要访问文件所在的第 i 个盘块,则必须先读出文件的第一个盘块……,就这样顺序 地查找直至第 i 块。当 i=100 时,须启动 100 次磁盘去实现读盘块的操作,平均每次都要花 费几十毫秒。可见,随机访问的速度相当低。此外,只通过链接指针来将一大批离散的盘 块链接起来,其可靠性较差,因为只要其中的任何一个指针出现问题,都会导致整个链的 断开。 为了提高检索速度和减小指针所占用的存储空间,可以将几个盘块组成一个簇(cluster)。 比如,一个簇可包含 4 个盘块,在进行盘块分配时,是以簇为单位进行的。在链接文件中 的每个元素也是以簇为单位的。这样将会成倍地减小查找指定块的时间,而且也可减小指 针所占用的存储空间,但却增大了内部碎片,而且这种改进也是非常有限的。 2.显式链接 这是指把用于链接文件各物理块的指针,显式地存放在内存的一张链接表中。该表在 整个磁盘仅设置一张,如图 6-9 所示。表的序号是物理盘块号,从 0 开始,直至 N-1;N 为 盘块总数。在每个表项中存放链接指针,即下一个盘块号。在该表中,凡是属于某一文件 的第一个盘块号,或者说是每一条链的链首指针所对应的盘块号,均作为文件地址被填入 相应文件的 FCB 的“物理地址”字段中。由于查找记录的过程是在内存中进行的,因而不 仅显著地提高了检索速度,而且大大减少了访问磁盘的次数。由于分配给文件的所有盘块 号都放在该表中,故把该表称为文件分配表 FAT(File Allocation Table)。 FCB 2 物理块号 0 1 2 3 4 5 FAT 0 4 5 1 图 6-9 显式链接结构 6.3.3 FAT 和 NTFS 技术 在微软公司的早期 MS-DOS 中,所使用的是 12 位的 FAT12 文件系统,后来为 16 位的 FAT16 文件系统;在 Windows 95 和 Windows 98 操作系统中则升级为 32 位的 FAT32;Windows NT、Windows 2000 和 Windows XP 操作系统又进一步发展为新技术文件系统 NTFS(New Technology File System)。上述的几种文件系统所采用的文件分配方式基本上都是类似于前 一节所介绍的显式链接方法。 在早期 MS-DOS 的 FAT 文件系统中,引入了“卷”的概念,可以支持将一个物理磁盘 分成四个逻辑磁盘,每个逻辑磁盘就是一个卷(也称为分区),也就是说每个卷都是一个能够 被单独格式化和使用的逻辑单元,供文件系统分配空间时使用。一个卷中包含了文件系统 信息、一组文件以及空闲空间。每个卷都专门划出一个单独区域来存放自己的目录和 FAT 表,以及自己的逻辑驱动器字母,因此,通常对于仅有一个硬盘的计算机,最多可将其硬 第六章 文 件 管 理 ·217· 盘分为“C:”、“D:”和“E:”“F:”四个卷(逻辑磁盘)。需要指出的是,在现代 OS 中,一 个物理磁盘可以划分为多个卷,一个卷也可以由多个物理磁盘组成,如 RAID 磁盘阵列等。 1.FAT12 1) 以盘块为基本分配单位 早期 MS-DOS 操作系统所使用的是 FAT12 文件系统,在每个分区中都配有两张文件分 配表 FAT1 和 FAT2,在 FAT 的每个表项中存放下一个盘块号,它实际上是用于盘块之间的 链接的指针,通过它可以将一个文件的所有的盘块链接起来,而将文件的第一个盘块号放 在自己的 FCB 中。图 6-10 示出了 MS-DOS 的文件物理结构,这里示出了两个文件,文件 A 占用三个盘块,其盘块号依次为 4、6、11;文件 B 则依次占用 9、10 及 5 号三个盘块。 每个文件的第一个盘块号放在自己的 FCB 中。整个系统有一张文件分配表 FAT。在 FAT 的 每个表项中存放下一个盘块号。对于 1.2 MB 的软盘,每个盘块的大小为 512 B,在每个 FAT 中共含有 2.4 K 个表项,由于每个 FAT 表项占 12 位,故 FAT 表占用 3.6 KB 的存储空间。 FCB A 4 FCB B 9 FAT 0 1 2 3 6 4 EOF 5 11 6 7 8 10 9 5 EOF 图 6-10 MS-DOS 的文件物理结构 现在我们来计算以盘块为分配单位时,所允许的最大磁盘容量。由于每个 FAT 表项为 12 位,因此,在 FAT 表中最多允许有 4096 个表项,如果采用以盘块作为基本分配单位, 每个盘块(也称扇区)的大小一般是 512 字节,那么,每个磁盘分区的容量为 2 MB (4096×512 B)。同时,一个物理磁盘支持 4 个逻辑磁盘分区,所以相应的磁盘最大容量仅 为 8 MB。这对最早时期的硬盘还可应付,但很快磁盘的容量就超过了 8 MB,FAT12 是否 还可继续用呢,回答虽是肯定的,但需要引入一个新的分配单位——簇。 2) 簇的基本概念 为了适应磁盘容量不断增大的需要,在进行盘块分配时,不再以盘块而是以簇(cluster) 为基本单位。簇是一组连续的扇区,在 FAT 中它是作为一个虚拟扇区,簇的大小一般是 2n (n 为整数)个盘块,在 MS-DOS 的实际运用中,簇的容量可以仅有一个扇区(512 B)、两个 扇区(1 KB)、四个扇区(2 KB)、八个扇区(4 KB)等。一个簇应包含扇区的数量与磁盘容量的 大小直接有关。例如,当一个簇仅有一个扇区时,磁盘的最大容量为 8 MB;当一个簇包含 两个扇区时,磁盘的最大容量可以达到 16 MB;当一个簇包含了八个扇区时,磁盘的最大 ·218· 计算机操作系统 容量便可达到 64 MB。 由上所述可以看出,以簇作为基本的分配单位所带来的最主要的好处是,能适应磁盘 容量不断增大的情况。值得注意的是,使用簇作为基本的分配单位虽可减少 FAT 表中的项 数(在相同的磁盘容量下,FAT 表的项数是与簇的大小成反比的)。这一方面会使 FAT 表占用 更少的存储空间,并减少访问 FAT 表的存取开销,提高文件系统的效率;但这也会造成更 大的簇内零头(它与存储器管理中的页内零头相似)。 3) FAT12 存在的问题 尽管 FAT12 曾是一个不错的文件系统,但毕竟已老化,已不能满足操作系统发展的需 要,其表现出来的主要问题是,对所允许的磁盘容量存在着严重的限制,通常只能是数十 兆字节,虽然可以用继续增加簇的大小来提高所允许的最大磁盘容量,但随着支持的硬盘 容量的增加,相应的簇内碎片也将随之成倍地增加。此外,它只能支持 8+3 格式的文件名。 2.FAT16 对 FAT12 所存在的问题进行简单的分析即可看出,其根本原因在于,FAT12 表最多只 允许 4096 个表项,亦即最多只能将一个磁盘分区分为 4096 个簇。这样,随着磁盘容量的 增加,必定会引起簇的大小和簇内碎片也随之增加。由此可以得出解决方法,那就是增加 FAT 表的表项数,亦即应增加 FAT 表的宽度,如果我们将 FAT 表的宽度增至 16 位,最大表 项数将增至 65536 个,此时便能将一个磁盘分区分为 65 536(216)个簇。我们把具有 16 位表 宽的 FAT 表称为 FAT16。在 FAT16 的每个簇中可以有的盘块数为 4、8、16、32 直到 64, 由此得出 FAT16 可以管理的最大分区空间为 216 × 64 × 512 = 2048 MB。 由上述分析不难看出,FAT16 对 FAT12 的局限性有所改善,但改善很有限。当磁盘容 量迅速增加时,如果再继续使用 FAT16,由此所形成的簇内碎片所造成的浪费也越大。例 如,当要求磁盘分区的大小为 8 GB 时,则每个簇的大小达到 128 KB,这意味着内部零头 最大可达到 128 KB。一般而言,对于 1~4 GB 的硬盘来说,大约会浪费 10%~20%的空间。 为了解决这一问题,微软推出了 FAT32。 另外,由于 FAT12 和 FAT16 都不支持长文件名,文件名受到了 8 个字符文件名和 3 个 字符文件扩展名的长度限制,为了满足用户通过文件名更好地描述文件内容的需求,在 Windows 95 以后的系统中,对 FAT16 进行了扩展,通过一个长文件名占用多个目录项的方 法,使得文件名的长度可以长达 255 个字符,这种扩展的 FAT16 也称为 VFAT。 3.FAT32 如同存储器管理中的分页管理,所选择的页面越大,可能造成的页内零头也会越大。 为减少页内零头就应该选择适当大小的页面。在这里,为了减小磁盘的簇内零头,也就应 当选择适当大小的簇。问题是 FAT16 表的长度只有 65 535 项,随着磁盘容量的增加,簇的 大小也必然会随之增加,为了减少簇内零头,也就应当增加 FAT 表的长度。为此,需要再 增加 FAT 表的宽度,这样也就由 FAT16 演变为 FAT32。 FAT32 是 FAT 系列文件系统的最后一个产品。每一簇在 FAT 表中的表项占据 4 字节(232), FAT 表可以表示 4 294 967 296 项,即 FAT32 允许管理比 FAT16 更多的簇。这样就允许在 FAT32 中采用较小的簇,FAT32 的每个簇都固定为 4 KB,即每簇用 8 个盘块代替 FAT16 的 64 个盘块,每个盘块仍为 512 字节,FAT32 分区格式可以管理的单个最大磁盘空间大到 第六章 文 件 管 理 ·219· 4 KB×232 = 2 TB。三种 FAT 类型的最大分区以及所对应的块的大小如图 6-11 所示。 块大小/KB FAT12/MB FAT16/MB FAT32/TB 0.5 2 1 4 2 8 128 4 16 256 1 8 512 2 16 1024 2 32 2048 2 图 6-11 FAT 中簇的大小与最大分区的对应关系 FAT32 比 FAT16 支持更小的簇和更大的磁盘容量,这就大大减少了磁盘空间的浪费, 使得 FAT32 分区的空间分配更有效率,例如,两个磁盘容量都为 2 GB,一个磁盘采用了 FAT16 文件系统,另一个磁盘采用了 FAT32 文件系统,采用 FAT16 磁盘的簇大小为 32 KB, 而 FAT32 磁盘簇只有 4 KB 的大小,这样,FAT32 磁盘碎片减少,比 FAT16 的存储器利用 率要高很多,通常情况下可以提高 15%。FAT32 主要应用于 Windows 98 及后续 Windows 系统,它可以增强磁盘性能,并增加可用磁盘空间,同时也支持长文件名;它不存在最小 存储空间问题,能够有效地节省硬盘空间。 FAT32 仍然有着明显的不足之处:首先,由于文件分配表的扩大,运行速度比 FAT16 格式要慢;其次,FAT32 有最小管理空间的限制,FAT32 卷必须至少有 65 537 个簇,所以 FAT32 不支持容量小于 512 MB 的分区,因此对于小分区,则仍然需要使用 FAT16 或 FAT12; 再之,FAT32 的单个文件的长度也不能大于 4 GB;最后,FAT32 最大的限制在于兼容性方 面,FAT32 不能保持向下兼容。 4.NTFS 1) NTFS 新特征 NTFS(New Technology File System)是一个专门为 Windows NT 开发的、全新的文件系 统,并适用于 Windows 2000/XP/2003。NTFS 具有许多新的特征:首先,它使用了 64 位磁 盘地址,理论上可以支持 2 的 64 次方字节的磁盘分区;其次,在 NTFS 中可以很好地支持 长文件名,单个文件名限制在 255 个字符以内,全路径名为 32 767 个字符;第三,具有系 统容错功能,即在系统出现故障或差错时,仍能保证系统正常运行,这一点我们将在 6.6 节 中介绍;第四,提供了数据的一致性,这是一个非常有用的功能,我们将在本章的最后一 节介绍;此外,NTFS 还提供了文件加密、文件压缩等功能。 2) 磁盘组织 同 FAT 文件系统一样,NTFS 也是以簇作为磁盘空间分配和回收的基本单位。一个文件 占用若干个簇,一个簇只属于一个文件。通过簇来间接管理磁盘,可以不需要知道盘块(扇 区)的大小,使 NTFS 具有了与磁盘物理扇区大小无关的独立性,很容易支持扇区大小不是 512 字节的非标准磁盘,从而可以根据不同的磁盘选择匹配的簇大小。 ·220· 计算机操作系统 在 NTFS 文件系统中,把卷上簇的大小称为“卷因子”,卷因子是在磁盘格式化时确定 的,其大小同 FAT 一样,也是物理磁盘扇区的整数倍,即一个簇包含 2n(n 为整数)个盘块, 簇的大小可由格式化命令或格式化程序按磁盘容量和应用需求来确定,可以为 512 B、1 KB、 2 KB……,最大可达 64 KB。对于小磁盘(≤512 MB),默认簇大小为 512 字节;对于 1 GB 磁盘,默认簇大小为 1 KB;对于 2 GB 磁盘,则默认簇大小为 4 KB。事实上,为了在传输 效率和簇内碎片之间进行折中,NTFS 在大多数情况下都是使用 4 KB。 对于簇的定位,NTFS 是采用逻辑簇号 LCN(Logical Cluster Number)和虚拟簇号 VCN(Virtual Cluster Number)进行的。LCN 是以卷为单位,将整个卷中所有的簇按顺序进行 简单的编号,NTFS 在进行地址映射时,可以通过卷因子与 LCN 的乘积,便可算出卷上的 物理字节偏移量,从而得到文件数据所在的物理磁盘地址。为了方便文件中数据的引用, NTFS 还可以使用 VCN,以文件为单位,将属于某个文件的簇按顺序进行编号。只要知道 了文件开始的簇地址,便可将 VCN 映射到 LCN。 3) 文件的组织 在 NTFS 中,以卷为单位,将一个卷中的所有文件信息、目录信息以及可用的未分配 空间信息,都以文件记录的方式记录在一张主控文件表 MFT(Master File Table)中。该表是 NTFS 卷结构的中心,从逻辑上讲,卷中的每个文件作为一条记录,在 MFT 表中占有一行, 其中还包括 MFT 自己的这一行。每行大小固定为 1 KB,每行称为该行所对应文件的元数 据(metadata),也称为文件控制字。 在 MFT 表中,每个元数据将其所对应文件的所有信息,包括文件的内容等,都被组织 在所对应文件的一组属性中。由于文件大小相差悬殊,其属性所需空间大小也相差很大, 因此,在 MFT 表中,对于元数据的 1 KB 空间,可能记录不下文件的全部信息。所以当文 件较小时,其属性值所占空间也较小,可以将文件的所有属性直接记录在元数据中。而当 文件较大时,元数据仅记录该文件的一部分属性,其余属性,如文件的内容等,可以记录 到卷中的其它可用簇中,并将这些簇按其所记录文件的属性进行分类,分别链接成多个队 列,将指向这些队列的指针保存在元数据中。 例如对于一个文件的真正数据,即文件 DATA 属性,如果很小,就直接存储在 MFT 表 中对应的元数据中,这样对文件数据的访问,仅需要对 MFT 表进行即可,减少了磁盘访问 次数,较大地提高了对小文件存取的效率。如果文件较大,则文件的真正数据往往保存在 其它簇中。此时通过元数据中指向文件 DATA 属性的队列指针,可以方便地查找到这些簇, 完成对文件数据的访问。 实际上,文件在存储过程中,数据往往连续存放在若干个相邻的簇中,仅用一个指针 记录这几个相邻的簇即可,而不是每个簇需要一个指针,从而可以节省指针所耗费的空间。 一般地,采用上述的方式,只需十几个字节就可以含有 FAT32 所需几百个 KB 才拥有的信 息量。 NTFS 的不足之处在于,它只能被 Windows NT 所识别。NTFS 文件系统可以存取 FAT 等文件系统的文件,但 NTFS 文件却不能被 FAT 等文件系统所存取,缺乏兼容性。Windows 的 95/98/98SE 和 Me 版本都不能识别 NTFS 文件系统。 第六章 文 件 管 理 ·221· 6.3.4 索引分配 1.单级索引分配 链接分配方式虽然解决了连续分配方式所存在的问题,但又出现了下述另外两个问题: (1) 不能支持高效的直接存取。要对一个较大的文件进行直接存取,须首先在 FAT 中顺 序地查找许多盘块号。 (2) FAT 需占用较大的内存空间。由于一个文件所占用盘块的盘块号是随机地分布在 FAT 中的,因而只有将整个 FAT 调入内存,才能保证在 FAT 中找到一个文件的所有盘块号。 当磁盘容量较大时,FAT 可能要占用数兆字节以上的内存空间,这是令人难以接受的。 事实上,在打开某个文件时,只需把该文件占用的盘块的编号调入内存即可,完全没 有必要将整个 FAT 调入内存。为此,应将每个文件所对应的盘块号集中地放在一起。索引 分配方法就是基于这种想法所形成的一种分配方法。它为每个文件分配一个索引块(表),再 把分配给该文件的所有盘块号都记录在该索引块中,因而该索引块就是一个含有许多盘块 号的数组。在建立一个文件时,只需在为之建立的目录项中填上指向该索引块的指针。图 6-12 示出了磁盘空间的索引分配图。 目录 count 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 file 块序号 jeep 19 9 16 1 19 10 25 -1 -1 -1 图 6-12 索引分配方式 索引分配方式支持直接访问。当要读文件的第 i 个盘块时,可以方便地直接从索引块中 找到第 i 个盘块的盘块号;此外,索引分配方式也不会产生外部碎片。当文件较大时,索引 分配方式无疑要优于链接分配方式。 索引分配方式的主要问题是:可能要花费较多的外存空间。每当建立一个文件时,便 须为之分配一个索引块,将分配给该文件的所有盘块号记录于其中。但在一般情况下,总 是中、小型文件居多,甚至有不少文件只需 1~2 个盘块,这时如果采用链接分配方式,只 需设置 1~2 个指针。如果采用索引分配方式,则同样仍须为之分配一索引块。通常是采用 一个专门的盘块作为索引块,其中可存放成百个、甚至上千个盘块号。可见,对于小文件 采用索引分配方式时,其索引块的利用率将是极低的。 ·222· 计算机操作系统 2.多级索引分配 当 OS 为一个大文件分配磁盘空间时,如果所分配出去的盘块的盘块号已经装满一个索 引块时,OS 便为该文件分配另一个索引块,用于将以后继续为之分配的盘块号记录于其中。 依此类推,再通过链指针将各索引块按序链接起来。显然,当文件太大,其索引块太多时, 这种方法是低效的。此时,应为这些索引块再建立一级索引,称为第一级索引,即系统再 分配一个索引块,作为第一级索引的索引块,将第一块、第二块……等索引块的盘块号填 入到此索引表中,这样便形成了两级索引分配方式。如果文件非常大时,还可用三级、四 级索引分配方式。 图 6-13 示出了两级索引分配方式下各索引块之间的链接情况。如果每个盘块的大小为 1 KB,每个盘块号占 4 个字节,则在一个索引块中可存放 256 个盘块号。这样,在两级索 引时, 最多可包含的存放文件的盘块的盘块号总数 N = 256 × 256 = 64 K 个盘块号。由此可 得出结论: 采用两级索引时,所允许的文件最大长度为 64 MB。倘若盘块的大小为 4 KB, 在采用单级索引时所允许的最大文件长度为 4 MB;而在采用两级索引时所允许的最大文件 长度可达 4 GB。 主索引 360 740 第二级索引 360 105 106 254 磁盘空间 0 1 2 … … 740 105 106 356 357 … 254 1125 … … … 1125 356 357 985 985 … … 图 6-13 两级索引分配 3.混合索引分配方式 所谓混合索引分配方式,是指将多种索引分配方式相结合而形成的一种分配方式。例 如,系统既采用了直接地址,又采用了一级索引分配方式,或两级索引分配方式,甚至还 采用了三级索引分配方式。这种混合索引分配方式已在 UNIX 系统中采用。在 UNIX System Ⅴ的索引结点中,共设置了 13 个地址项,即 iaddr(0)~iaddr(12),如图 6-14 所示。在 BSD UNIX 的索引结点中,共设置了 13 个地址项,它们都把所有的地址项分成两类,即直接地址和间 接地址。 第六章 文 件 管 理 ·223· mode owners (2) time stamps (3) size block count i.addr (0) i.addr (1) direct blocks single indirect double indirect triple indirect … … data data data data data data … … … data data data data 图 6-14 混合索引方式 1) 直接地址 为了提高对文件的检索速度,在索引结点中可设置 10 个直接地址项,即用 iaddr(0)~ iaddr(9)来存放直接地址。换言之,在这里的每项中所存放的是该文件数据所在盘块的盘块 号。假如每个盘块的大小为 4 KB,当文件不大于 40 KB 时,便可直接从索引结点中读出该 文件的全部盘块号。 2) 一次间接地址 对于大、中型文件,只采用直接地址是不现实的。为此,可再利用索引结点中的地址 项 iaddr(10)来提供一次间接地址。这种方式的实质就是一级索引分配方式。图中的一次间 址块也就是索引块,系统将分配给文件的多个盘块号记入其中。在一次间址块中可存放 1 K 个盘块号,因而允许文件长达 4 MB。 3) 多次间接地址 当文件长度大于 4 MB + 40 KB 时(一次间址与 10 个直接地址项),系统还须采用二次间 址分配方式。这时,用地址项 iaddr(11)提供二次间接地址。该方式的实质是两级索引分配 方式。系统此时是在二次间址块中记入所有一次间址块的盘号。在采用二次间址方式时, 文件最大长度可达 4 GB。同理,地址项 iaddr(12)作为三次间接地址,其所允许的文件最大 长度可达 4 TB。 6.4 目 录 管 理 通常,在现代计算机系统中,都要存储大量的文件。为了能对这些文件实施有效的管 理,必须对它们加以妥善组织,这主要是通过文件目录实现的。文件目录也是一种数据结 构,用于标识系统中的文件及其物理地址,供检索时使用。对目录管理的要求如下: ·224· 计算机操作系统 (1) 实现“按名存取”,即用户只须向系统提供所需访问文件的名字,便能快速准确地 找到指定文件在外存上的存储位置。这是目录管理中最基本的功能,也是文件系统向用户 提供的最基本的服务。 (2) 提高对目录的检索速度。通过合理地组织目录结构的方法,可加快对目录的检索速 度,从而提高对文件的存取速度。这是在设计一个大、中型文件系统时所追求的主要目标。 (3) 文件共享。在多用户系统中,应允许多个用户共享一个文件。这样就须在外存中只 保留一份该文件的副本,供不同用户使用,以节省大量的存储空间,并方便用户和提高文 件利用率。 (4) 允许文件重名。系统应允许不同用户对不同文件采用相同的名字,以便于用户按照 自己的习惯给文件命名和使用文件。 6.4.1 文件控制块和索引结点 为了能对一个文件进行正确的存取,必须为文件设置用于描述和控制文件的数据结构, 称之为“文件控制块(FCB)”。文件管理程序可借助于文件控制块中的信息,对文件施以各种 操作。文件与文件控制块一一对应,而人们把文件控制块的有序集合称为文件目录,即一个 文件控制块就是一个文件目录项。通常,一个文件目录也被看做是一个文件,称为目录文件。 1.文件控制块 为了能对系统中的大量文件施以有效的管理,在文件控制块中,通常应含有三类信息, 即基本信息、存取控制信息及使用信息。 1) 基本信息类 基本信息类包括: ① 文件名,指用于标识一个文件的符号名。在每个系统中,每一个 文件都必须有惟一的名字,用户利用该名字进行存取。② 文件物理位置,指文件在外存上 的存储位置,它包括存放文件的设备名、文件在外存上的起始盘块号、指示文件所占用的 盘块数或字节数的文件长度。③ 文件逻辑结构,指示文件是流式文件还是记录式文件、记 录数;文件是定长记录还是变长记录等。④ 文件的物理结构,指示文件是顺序文件,还是 链接式文件或索引文件。 2) 存取控制信息类 存取控制信息类包括:文件主的存取权限、核准用户的存取权限以及一般用户的存取 权限。 3) 使用信息类 使用信息类包括: 文件的建立日期和时间、文件上一次修改的日期和时间及当前使用信 息(这项信息包括当前已打开该文件的进程数、是否被其它进程锁住、文件在内存中是否已 被修改但尚未拷贝到盘上)。应该说明,对于不同 OS 的文件系统,由于功能不同,可能只 含有上述信息中的某些部分。 图 6-15 示出了 MS-DOS 中的文件控制块,其中含有文件名、文件所在的第一个盘块号、 文件属性、文件建立日期和时间及文件长度等。FCB 的长度为 32 个字节,对于 360 KB 的 软盘,总共可包含 112 个 FCB,共占 4 KB 的存储空间。 第六章 文 件 管 理 ·225· 文 扩属 件 展 名 名性 备 用 时 间 日 期 第 一 块 号 盘 块 数 图 6-15 MS-DOS 的文件控制块 2.索引结点 1) 索引结点的引入 文件目录通常是存放在磁盘上的,当文件很多时,文件目录可能要占用大量的盘块。 在查找目录的过程中,先将存放目录文件的第一个盘块中的目录调入内存,然后把用户所 给定的文件名与目录项中的文件名逐一比较。若未找到指定文件,便再将下一个盘块中的 目录项调入内存。设目录文件所占用的盘块数为 N,按此方法查找,则查找一个目录项平均 需要调入盘块(N+1)/2 次。假如一个 FCB 为 64 B,盘块大小为 1 KB,则每个盘块中只能存 放 16 个 FCB;若一个文件目录中共有 640 个 FCB,需占用 40 个盘块,故平均查找一个文 件需启动磁盘 20 次。 稍加分析可以发现,在检索目录文件的过程中,只用到了文件名,仅当找到一个目录 项(即其中的文件名与指定要查找的文件名相匹配)时,才需从该目录项中读出该文件的物理 地址。而其它一些对该文件进行描述的信息,在检索目录时一概不用。显然,这些信息在 检索目录时不需调入内存。为此,在有的系统中,如 UNIX 系统,便采用了把文件名与文 件描述信息分开的办法,亦即,使文件描述信息单独形成一个称为索引结点的数据结构, 简称为 i 结点。在文件目录中的每个目录项仅由文件名和指向该文件所对应的 i 结点的指针 所构成。在 UNIX 系统中一个目录仅占 16 个字节,其中 14 个字节是文件名,2 个字节为 i 结点指针。在 1 KB 的盘块中可做 64 个目录项,这样,为找到一个文件,可使平均启动磁 盘次数减少到原来的 1/4,大大节省了系统开销。图 6-16 示出了 UNIX 的文件目录项。 文件名 索引结点编号 文件名 1 文件名 2 … … 0 13 14 15 图 6-16 UNIX 的文件目录 2) 磁盘索引结点 这是存放在磁盘上的索引结点。每个文件有惟一的一个磁盘索引结点,它主要包括以 下内容: (1) 文件主标识符,即拥有该文件的个人或小组的标识符。 (2) 文件类型,包括正规文件、目录文件或特别文件。 (3) 文件存取权限,指各类用户对该文件的存取权限。 (4) 文件物理地址,每一个索引结点中含有 13 个地址项,即 iaddr(0)~iaddr(12),它们 以直接或间接方式给出数据文件所在盘块的编号。 ·226· 计算机操作系统 (5) 文件长度,指以字节为单位的文件长度。 (6) 文件连接计数,表明在本文件系统中所有指向该(文件的)文件名的指针计数。 (7) 文件存取时间,指本文件最近被进程存取的时间、最近被修改的时间及索引结点最 近被修改的时间。 3) 内存索引结点 这是存放在内存中的索引结点。当文件被打开时,要将磁盘索引结点拷贝到内存的索 引结点中,便于以后使用。在内存索引结点中又增加了以下内容: (1) 索引结点编号,用于标识内存索引结点。 (2) 状态,指示 i 结点是否上锁或被修改。 (3) 访问计数,每当有一进程要访问此 i 结点时,将该访问计数加 1,访问完再减 1。 (4) 文件所属文件系统的逻辑设备号。 (5) 链接指针。设置有分别指向空闲链表和散列队列的指针。 6.4.2 目录结构 目录结构的组织,关系到文件系统的存取速度,也关系到文件的共享性和安全性。因 此,组织好文件的目录,是设计好文件系统的重要环节。目前常用的目录结构形式有单级 目录、两级目录和多级目录。 1.单级目录结构 这是最简单的目录结构。在整个文件系统中只建立一张目录表,每个文件占一个目录 项,目录项中含文件名、文件扩展名、文件长度、文件类型、文件物理地址以及其它文件 属性。此外,为表明每个目录项是否空闲,又设置了一个状态位。单级目录如图 6-17 所示。 文件名 文件名 1 文件名 2 … 物理地址 文件说明 状态位 图 6-17 单级目录 每当要建立一个新文件时,必须先检索所有的目录项,以保证新文件名在目录中是惟 一的。然后再从目录表中找出一个空白目录项,填入新文件的文件名及其它说明信息,并 置状态位为 1。删除文件时,先从目录中找到该文件的目录项,回收该文件所占用的存储空 间,然后再清除该目录项。 单级目录的优点是简单且能实现目录管理的基本功能——按名存取,但却存在下述一 些缺点: (1) 查找速度慢。对于稍具规模的文件系统,会拥有数目可观的目录项,致使为找到一 个指定的目录项要花费较多的时间。对于一个具有 N 个目录项的单级目录,为检索出一个 目录项,平均需查找 N/2 个目录项。 (2) 不允许重名。在一个目录表中的所有文件,都不能与另一个文件有相同的名字。然 而,重名问题在多道程序环境下却又是难以避免的;即使在单用户环境下,当文件数超过 第六章 文 件 管 理 ·227· 数百个时,也难于记忆。 (3) 不便于实现文件共享。通常,每个用户都有自己的名字空间或命名习惯。因此,应 当允许不同用户使用不同的文件名来访问同一个文件。然而,单级目录却要求所有用户都 用同一个名字来访问同一文件。简言之,单级目录只能满足对目录管理的四点要求中的第 一点, 因而,它只能适用于单用户环境。 2.两级目录 为了克服单级目录所存在的缺点,可以为每一个用户建立一个单独的用户文件目录 UFD(User File Directory)。这些文件目录具有相似的结构,它由用户所有文件的文件控制块 组成。此外,在系统中再建立一个主文件目录 MFD(Master File Directory);在主文件目录中, 每个用户目录文件都占有一个目录项,其目录项中包括用户名和指向该用户目录文件的指 针。如图 6-18 所示,图中的主目录中示出了三个用户名,即 Wang、Zhang 和 Gao。 用户名 指向子目录指针 Wang Zhang Gao Wang用户目录 Alpha Test Alpha Test Zhang用户目录 Report Test Report Test Gao用户目录 Beta Device Misx Beta Device Misx 图 6-18 两级目录结构 在两级目录结构中,如果用户希望有自己的用户文件目录 UFD,可以请求系统为自己 建立一个用户文件目录;如果自己不再需要 UFD,也可以请求系统管理员将它撤消。在有 了 UFD 后,用户可以根据自己的需要创建新文件。每当此时,OS 只需检查该用户的 UFD, 判定在该 UFD 中是否已有同名的另一个文件。若有,用户必须为新文件重新命名;若无, 便在 UFD 中建立一个新目录项,将新文件名及其有关属性填入目录项中,并置其状态位为 “1”。当用户要删除一个文件时,OS 也只需查找该用户的 UFD,从中找出指定文件的目录 项, 在回收该文件所占用的存储空间后,将该目录项删除。 两级目录结构基本上克服了单级目录的缺点,并具有以下优点: (1) 提高了检索目录的速度。如果在主目录中有 n 个子目录,每个用户目录最多为 m 个 目录项,则为查找一指定的目录项,最多只需检索 n + m 个目录项。但如果是采用单级目录 结构,则最多需检索 n × m 个目录项。假定 n = m,可以看出,采用两级目录可使检索效率 提高 n/2 倍。 (2) 在不同的用户目录中,可以使用相同的文件名。只要在用户自己的 UFD 中,每一 个文件名都是惟一的。例如,用户 Wang 可以用 Test 来命名自己的一个测试文件;而用户 Zhang 则可用 Test 来命名自己的一个并不同于 Wang 的 Test 的测试文件。 ·228· 计算机操作系统 (3) 不同用户还可使用不同的文件名来访问系统中的同一个共享文件。采用两级目录结 构也存在一些问题。该结构虽然能有效地将多个用户隔开,在各用户之间完全无关时,这种 隔离是一个优点;但当多个用户之间要相互合作去完成一个大任务,且一用户又需去访问其 他用户的文件时,这种隔离便成为一个缺点,因为这种隔离会使诸用户之间不便于共享文件。 3.多级目录结构 1) 目录结构 对于大型文件系统,通常采用三级或三级以上的目录结构,以提高对目录的检索速度 和文件系统的性能。多级目录结构又称为树型目录结构,主目录在这里被称为根目录,把 数据文件称为树叶,其它的目录均作为树的结点。图 6-19 示出了多级目录结构。图中,用 方框代表目录文件,圆圈代表数据文件。在该树型目录结构中,主(根)目录中有三个用户的 总目录项 A、B 和 C。在 B 项所指出的 B 用户的总目录 B 中,又包括三个分目录 F、E 和 D, 其中每个分目录中又包含多个文件。如 B 目录中的 F 分目录中,包含 J 和 N 两个文件。为 了提高文件系统的灵活性,应允许在一个目录文件中的目录项既是作为目录文件的 FCB, 又是数据文件的 FCB,这一信息可用目录项中的一位来指示。例如,在图 6-19 中,用户 A 的总目录中,目录项 A 是目录文件的 FCB,而目录项 B 和 D 则是数据文件的 FCB。 1A B C 2A B D 67 5A C 3F E D 4G A 89 10 11 a 12 J N K 13 J M K 15 16 b 17 18 19 14 A H F 20 21 图 6-19 多级目录结构 2) 路径名 在树形目录结构中,从根目录到任何数据文件,都只有一条惟一的通路。在该路径上 从树的根(即主目录)开始,把全部目录文件名与数据文件名依次地用“/”连接起来,即构 成该数据文件的路径名(path name)。系统中的每一个文件都有惟一的路径名。例如,在图 6-19 中用户 B 为访问文件 J,应使用其路径名/B/F/J 来访问。 3) 当前目录(Current Directory) 当一个文件系统含有许多级时,每访问一个文件,都要使用从树根开始直到树叶(数据 文件)为止的、包括各中间节点(目录)名的全路径名。这是相当麻烦的事,同时由于一个进 程运行时所访问的文件大多仅局限于某个范围,因而非常不便。基于这一点,可为每个进 程设置一个“当前目录”,又称为“工作目录”。进程对各文件的访问都相对于“当前目录” 第六章 文 件 管 理 ·229· 而进行。此时各文件所使用的路径名,只需从当前目录开始,逐级经过中间的目录文件, 最后到达要访问的数据文件。把这一路径上的全部目录文件名与数据文件名用“/”连接形 成路径名。如用户 B 的当前目录是 F,则此时文件 J 的相对路径名仅是 J 本身。这样,把从 当前目录开始直到数据文件为止所构成的路径名,称为相对路径名(relative path name);而 把从树根开始的路径名称为绝对路径名(absolute path name)。 就多级目录较两级目录而言,其查询速度更快,同时层次结构更加清晰,能够更加有 效地进行文件的管理和保护。在多级目录中,不同性质、不同用户的文件可以构成不同的 目录子树,不同层次、不同用户的文件分别呈现在系统目录树中的不同层次或不同子树中, 可以容易地赋予不同的存取权限。 但是在多级目录中查找一个文件,需要按路径名逐级访问中间节点,这就增加了磁盘 访问次数,无疑将影响查询速度。 目前,大多数操作系统如 UNIX、Linux 和 Windows 系列都采用了多级目录结构。 4.增加和删除目录 在树型目录结构中,用户可为自己建立 UFD,并可再创建子目录。在用户要创建一个 新文件时,只需查看在自己的 UFD 及其子目录中有无与新建文件相同的文件名。若无,便 可在 UFD 或其某个子目录中增加一个新目录项。 在树型目录中,对于一个已不再需要的目录,应如何删除其目录项,须视情况而定。 这时,如果所要删除的目录是空的,即在该目录中已不再有任何文件,就可简单地将该目 录项删除,使它在其上一级目录中对应的目录项为空;如果要删除的目录不空,即其中尚 有几个文件或子目录,则可采用下述两种方法处理: (1) 不删除非空目录。当目录(文件)不空时,不能将其删除,而为了删除一个非空目录, 必须先删除目录中的所有文件,使之先成为空目录,然后再予以删除。如果目录中还包含 有子目录,还必须采取递归调用方式来将其删除,在 MS-DOS 中就是采用这种删除方式。 (2) 可删除非空目录。当要删除一个目录时,如果在该目录中还包含有文件,则目录中 的所有文件和子目录也同时被删除。 上述两种方法实现起来都比较容易,第二种方法则更为方便,但比较危险。因为整个 目录结构虽然用一条命令即能删除,但如果是一条错误命令,其后果则可能很严重。 6.4.3 目录查询技术 当用户要访问一个已存在文件时,系统首先利用用户提供的文件名对目录进行查询, 找出该文件的文件控制块或对应索引结点;然后,根据 FCB 或索引结点中所记录的文件物 理地址(盘块号),换算出文件在磁盘上的物理位置;最后,再通过磁盘驱动程序,将所需文 件读入内存。目前对目录进行查询的方式有两种: 线性检索法和 Hash 方法。 1.线性检索法 线性检索法又称为顺序检索法。在单级目录中,利用用户提供的文件名,用顺序查找 法直接从文件目录中找到指名文件的目录项。在树型目录中,用户提供的文件名是由多个 文件分量名组成的路径名,此时须对多级目录进行查找。假定用户给定的文件路径名是 /usr/ast/mbox,则查找/usr/ast/mbox 文件的过程如图 6-20 所示。 ·230· 计算机操作系统 根目录 结点6是 /usr的目录 132号盘块是 /usr的目录 结点26是 496号盘块是 /usr/ast的目录 /usr/ast的目录 1 · 1 ·· 4 bin 7 dev 14 lib 9 etc 6 · 1 ·· 19 dick 132 30 erik 51 jim 496 26 · 6 ·· 64 grants 92 books 60 mbox 6 usr 26 ast 81 minik 8 tmp 45 bal 17 src 在结点6中查找 usr字段 图 6-20 查找/usr/ast/mbox 的步骤 具体查找过程说明如下: 首先,系统应先读入第一个文件分量名 usr,用它与根目录文件(或当前目录文件)中各 目录项中的文件名顺序地进行比较,从中找出匹配者,并得到匹配项的索引结点号 6,再从 6 号索引结点中得知 usr 目录文件放在 132 号盘块中,将该盘块内容读入内存。 接着,系统再将路径名中的第二个文件分量名 ast 读入,用它与放在 132 号盘块中的第二 级目录文件中各目录项的文件名顺序进行比较,又找到匹配项,从中得到 ast 的目录文件放在 26 号索引结点中,再从 26 号索引结点中得知/usr/ast 是存放在 496 号盘块中,再读入 496 号盘块。 然后,系统又将该文件的第三个分量名 mbox 读入,用它与第三级目录文件/usr/ast 中各 目录项中的文件名进行比较,最后得到/usr/ast/mbox 的索引结点号为 60,即在 60 号索引结 点中存放了指定文件的物理地址。目录查询操作到此结束。如果在顺序查找过程中发现有 一个文件分量名未能找到,则应停止查找,并返回“文件未找到”信息。 2.Hash 方法 在 6.2.5 节中曾介绍了 Hash 文件。如果我们建立了一张 Hash 索引文件目录,便可利用 Hash 方法进行查询,即系统利用用户提供的文件名并将它变换为文件目录的索引值,再利 用该索引值到目录中去查找,这将显著地提高检索速度。 顺便指出,在现代操作系统中,通常都提供了模式匹配功能,即在文件名中使用了通 配符“*”、“?”等。对于使用了通配符的文件名,系统此时便无法利用 Hash 方法检索目 录,因此,这时系统还是需要利用线性查找法查找目录。 在进行文件名的转换时,有可能把 n 个不同的文件名转换为相同的 Hash 值,即出现了 所谓的“冲突”。一种处理此“冲突”的有效规则是: (1) 在利用 Hash 法索引查找目录时,如果目录表中相应的目录项是空的,则表示系统 中并无指定文件。 (2) 如果目录项中的文件名与指定文件名相匹配,则表示该目录项正是所要寻找的文件 所对应的目录项,故而可从中找到该文件所在的物理地址。 (3) 如果在目录表的相应目录项中的文件名与指定文件名并不匹配,则表示发生了“冲 突”,此时须将其 Hash 值再加上一个常数(该常数应与目录的长度值互质),形成新的索引值, 第六章 文 件 管 理 再返回到第一步重新开始查找。 ·231· 6.5 文件存储空间的管理 文件管理要解决的重要问题之一是如何为新创建的文件分配存储空间。其分配方法与 内存的分配有许多相似之处,即同样可采取连续分配方式或离散分配方式。前者具有较高 的文件访问速度,但可能产生较多的外存零头;后者能有效地利用外存空间,但访问速度 较慢。不论哪种分配方式,存储空间的基本分配单位都是磁盘块而非字节。 为了实现存储空间的分配,系统首先必须能记住存储空间的使用情况。为此,系统应 为分配存储空间而设置相应的数据结构;其次,系统应提供对存储空间进行分配和回收的 手段。下面介绍几种常用的文件存储空间的管理方法。 6.5.1 空闲表法和空闲链表法 1.空闲表法 1) 空闲表 空闲表法属于连续分配方式,它与内存的动态分配方式雷同,它为每个文件分配一块 连续的存储空间,即系统也为外存上的所有空闲区建立一张空闲表,每个空闲区对应于一 个空闲表项,其中包括表项序号、该空闲区的第一个盘块号、该区的空闲盘块数等信息。 再将所有空闲区按其起始盘块号递增的次序排列,如图 6-21 所示。 序号 第一空闲盘块号 空闲盘块数 1 2 4 2 9 3 3 15 5 4 — — 图 6-21 空闲盘块表 2) 存储空间的分配与回收 空闲盘区的分配与内存的动态分配类似,同样是采用首次适应算法、循环首次适应算 法等。例如,在系统为某新创建的文件分配空闲盘块时,先顺序地检索空闲表的各表项, 直至找到第一个其大小能满足要求的空闲区,再将该盘区分配给用户(进程),同时修改空闲 表。系统在对用户所释放的存储空间进行回收时,也采取类似于内存回收的方法,即要考 虑回收区是否与空闲表中插入点的前区和后区相邻接,对相邻接者应予以合并。 应该说明,在内存分配上,虽然很少采用连续分配方式,然而在外存的管理中,由于 这种分配方式具有较高的分配速度,可减少访问磁盘的 I/O 频率,故它在诸多分配方式中仍 占有一席之地。例如,在前面所介绍的对换方式中,对对换空间一般都采用连续分配方式。 对于文件系统,当文件较小(1~4 个盘块)时,仍采用连续分配方式,为文件分配相邻接的几 个盘块;当文件较大时,便采用离散分配方式。 2.空闲链表法 空闲链表法是将所有空闲盘区拉成一条空闲链。根据构成链所用基本元素的不同,可 ·232· 计算机操作系统 把链表分成两种形式:空闲盘块链和空闲盘区链。 (1) 空闲盘块链。这是将磁盘上的所有空闲空间,以盘块为单位拉成一条链。当用户因 创建文件而请求分配存储空间时,系统从链首开始,依次摘下适当数目的空闲盘块分配给 用户。当用户因删除文件而释放存储空间时,系统将回收的盘块依次插入空闲盘块链的末 尾。这种方法的优点是用于分配和回收一个盘块的过程非常简单,但在为一个文件分配盘 块时,可能要重复操作多次。 (2) 空闲盘区链。这是将磁盘上的所有空闲盘区(每个盘区可包含若干个盘块)拉成一条 链。在每个盘区上除含有用于指示下一个空闲盘区的指针外,还应有能指明本盘区大小(盘块 数)的信息。分配盘区的方法与内存的动态分区分配类似,通常采用首次适应算法。在回收 盘区时,同样也要将回收区与相邻接的空闲盘区相合并。在采用首次适应算法时,为了提 高对空闲盘区的检索速度,可以采用显式链接方法,亦即,在内存中为空闲盘区建立一张 链表。 6.5.2 位示图法 1.位示图 位示图是利用二进制的一位来表示磁盘中一个盘块的使用情况。当其值为“0”时,表 示对应的盘块空闲;为“1”时,表示已分配。有的系统把“0”作为盘块已分配的标志, 把“1”作为空闲标志。(它们在本质上是相同的,都是用一位的两种状态来标志空闲和已分 配两种情况。)磁盘上的所有盘块都有一个二进制位与之对应,这样,由所有盘块所对应的 位构成一个集合,称为位示图。通常可用 m × n 个位数来构成位示图,并使 m × n 等于磁盘 的总块数,如图 6-22 所示。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 111000111001 0 01 1 0 200011111100 0 01 1 1 311100011111 1 00 0 0 4 M 16 图 6-22 位示图 位示图也可描述为一个二维数组 map: Var map: array of bit; 2.盘块的分配 根据位示图进行盘块分配时,可分三步进行: (1) 顺序扫描位示图,从中找出一个或一组其值为“0”的二进制位(“0”表示空闲时)。 (2) 将所找到的一个或一组二进制位转换成与之相应的盘块号。假定找到的其值为“0” 的二进制位位于位示图的第 i 行、第 j 列,则其相应的盘块号应按下式计算: b = n(i - 1) + j 式中,n 代表每行的位数。 第六章 文 件 管 理 ·233· (3) 修改位示图,令 map[i,j]=1。 3.盘块的回收 盘块的回收分两步: (1) 将回收盘块的盘块号转换成位示图中的行号和列号。转换公式为: i = (b - 1)DIV n + 1 j = (b - 1)MOD n + 1 (2) 修改位示图。令 map[i,j] =0。 这种方法的主要优点是,从位示图中很容易找到一个或一组相邻接的空闲盘块。例如, 我们需要找到 6 个相邻接的空闲盘块,这只需在位示图中找出 6 个其值连续为“0”的位即 可。此外,由于位示图很小,占用空间少,因而可将它保存在内存中,进而使在每次进行 盘区分配时,无需首先把盘区分配表读入内存,从而节省了许多磁盘的启动操作。因此, 位示图常用于微型机和小型机中,如 CP/M、Apple-DOS 等 OS 中。 6.5.3 成组链接法 空闲表法和空闲链表法都不适用于大型文件系统,因为这会使空闲表或空闲链表太长。 在 UNIX 系统中采用的是成组链接法,这是将上述两种方法相结合而形成的一种空闲盘块 管理方法,它兼备了上述两种方法的优点而克服了两种方法均有的表太长的缺点。 1.空闲盘块的组织 (1) 空闲盘块号栈用来存放当前可用的一组空闲盘块的盘块号(最多含 100 个号),以 及栈中尚有的空闲盘块号数 N。顺便指出,N 还兼作栈顶指针用。例如,当 N=100 时,它 指向 S.free(99)。由于栈是临界资源,每次只允许一个进程去访问,故系统为栈设置了一 把锁。图 6-23 左部示出了空闲盘块号栈的结构。其中,S.free(0)是栈底,栈满时的栈顶为 S.free(99)。 空闲盘 块号栈 S.free 100 0 300 1 299 98 202 99 201 … … … … … 100 100 99 400 0 399 7999 … 301 7901 300 400 7900 … 299 399 7899 … 201 301 7801 图 6-23 空闲盘块的成组链接法 7999 7901 ·234· 计算机操作系统 (2) 文件区中的所有空闲盘块被分成若干个组,比如,将每 100 个盘块作为一组。假定 盘上共有 10 000 个盘块,每块大小为 1 KB,其中第 201~7999 号盘块用于存放文件,即作 为文件区,这样,该区的最末一组盘块号应为 7901~7999;次末组为 7801~7900……;第 二组的盘块号为 301~400;第一组为 201~300,如图 6-23 右部所示。 (3) 将每一组含有的盘块总数 N 和该组所有的盘块号记入其前一组的第一个盘块的 S.free(0)~S.free(99)中。这样,由各组的第一个盘块可链成一条链。 (4) 将第一组的盘块总数和所有的盘块号记入空闲盘块号栈中,作为当前可供分配的空 闲盘块号。 (5) 最末一组只有 99 个盘块,其盘块号分别记入其前一组的 S.free(1) ~S.free(99)中, 而在 S.free(0)中则存放“0”,作为空闲盘块链的结束标志。(注:最后一组的盘块数应为 99, 不应是 100,因为这是指可供使用的空闲盘块,其编号应为(1~99),0 号中放空闲盘块链的 结尾标志。) 2.空闲盘块的分配与回收 当系统要为用户分配文件所需的盘块时,须调用盘块分配过程来完成。该过程首先检 查空闲盘块号栈是否上锁,如未上锁,便从栈顶取出一空闲盘块号,将与之对应的盘块分 配给用户,然后将栈顶指针下移一格。若该盘块号已是栈底,即 S.free(0),这是当前栈中最 后一个可分配的盘块号。由于在该盘块号所对应的盘块中记有下一组可用的盘块号,因此, 须调用磁盘读过程,将栈底盘块号所对应盘块的内容读入栈中,作为新的盘块号栈的内容, 并把原栈底对应的盘块分配出去(其中的有用数据已读入栈中)。然后,再分配一相应的缓冲 区(作为该盘块的缓冲区)。最后,把栈中的空闲盘块数减 1 并返回。 在系统回收空闲盘块时,须调用盘块回收过程进行回收。它是将回收盘块的盘块号记入 空闲盘块号栈的顶部,并执行空闲盘块数加 1 操作。当栈中空闲盘块号数目已达 100 时,表 示栈已满,便将现有栈中的 100 个盘块号记入新回收的盘块中,再将其盘块号作为新栈底。 6.6 文件共享与文件保护 在现代计算机系统中,必须提供文件共享手段,即系统应允许多个用户(进程)共享同一 份文件。这样,在系统中只需保留该共享文件的一份副本。如果系统不能提供文件共享功 能,就意味着凡是需要该文件的用户,都须各自备有此文件的副本,显然这会造成对存储 空间的极大浪费。随着计算机技术的发展,文件共享的范围也在不断扩大,从单机系统中 的共享,扩展为多机系统的共享,进而又扩展为计算机网络范围的共享,甚至实现全世界 的文件共享。 早在 20 世纪的 60 和 70 年代,已经出现了不少实现文件共享的方法,如绕弯路法、连 访法,以及利用基本文件实现文件共享的方法;而现代的一些文件共享方法,也是在早期 这些方法的基础上发展起来的。下面我们仅介绍当前常用的两种文件共享方法。 6.6.1 基于索引结点的共享方式 在树型结构的目录中,当有两个(或多个)用户要共享一个子目录或文件时,必须将共享 第六章 文 件 管 理 ·235· 文件或子目录链接到两个(或多个)用户的目录中,才能方便地找到该文件,如图 6-24 所示。 此时该文件系统的目录结构已不再是树型结构,而是个有向非循环图 DAG(Directed Acyclic Graph)。 根目录 A B C A B B B C C B C C ? C CC 图 6-24 包含有共享文件的文件系统 如何建立 B 目录与共享文件之间的链接呢?如果在文件目录中包含了文件的物理地址, 即文件所在盘块的盘块号,则在链接时,必须将文件的物理地址拷贝到 B 目录中去。但如 果以后 B 或 C 还要继续向该文件中添加新内容,也必然要相应地再增加新的盘块,这须由 附加操作 Append 来完成。而这些新增加的盘块,也只会出现在执行了操作的目录中。可见, 这种变化对其他用户而言是不可见的,因而新增加的这部分内容已不能被共享。 为了解决这个问题,可以引用索引结点,即诸如文件的物理地址及其它的文件属性等 信息,不再是放在目录项中,而是放在索引结点中。`在文件目录中只设置文件名及指向相 应索引结点的指针,如图 6-25 所示。此时,由任何用户对文件进行 Append 操作或修改, 所引起的相应结点内容的改变(例如,增加了新的盘块号和文件长度等),都是其他用户可见 的,从而也就能提供给其他用户来共享。 Wan用g 户文件目录 Test r Lee用户文件目录 索引结点 count=2 文件物理地址 Test Test r 图 6-25 基于索引结点的共享方式 ·236· 计算机操作系统 在索引结点中还应有一个链接计数 count,用于表示链接到本索引结点(亦即文件)上的 用户目录项的数目。当 count=3 时,表示有三个用户目录项连接到本文件上,或者说是有三 个用户共享此文件。 当用户 C 创建一个新文件时,他便是该文件的所有者,此时将 count 置 1。当有用户 B 要共享此文件时,在用户 B 的目录中增加一目录项,并设置一指针指向该文件的索引结点, 此时,文件主仍然是 C,count=2。如果用户 C 不再需要此文件,是否能将此文件删除呢? 回答是否定的。因为,若删除了该文件,也必然删除了该文件的索引结点,这样便会使 B 的指针悬空,而 B 则可能正在此文件上执行写操作,此时将因此半途而废。但如果 C 不删 除此文件而等待 B 继续使用,这样,由于文件主是 C,如果系统要记账收费,则 C 必须为 B 使用此共享文件而付账,直至 B 不再需要。图 6-26 示出了 B 链接到文件上的前、后 情况。 C的目录 B的目录 C的目录 B的目录 owner=c count=1 owner=c count=2 owner=c count=1 链接前 建立链接后 拥有者删除文件后 图 6-26 进程 B 链接前后的情况 6.6.2 利用符号链实现文件共享 为使 B 能共享 C 的一个文件 F,可以由系统创建一个 LINK 类型的新文件,也取名为 F, 并将 F 写入 B 的目录中,以实现 B 的目录与文件 F 的链接。在新文件中只包含被链接文件 F 的路径名。这样的链接方法被称为符号链接(Symbolic Linking)。新文件中的路径名则只被 看作是符号链(Symbolic Link),当 B 要访问被链接的文件 F 且正要读 LINK 类新文件时,此 要求将被 OS 截获,OS 根据新文件中的路径名去读该文件,于是就实现了用户 B 对文件 F 的共享。 在利用符号链方式实现文件共享时,只是文件主才拥有指向其索引结点的指针;而共 享该文件的其他用户则只有该文件的路径名,并不拥有指向其索引结点的指针。这样,也 就不会发生在文件主删除一共享文件后留下一悬空指针的情况。当文件的拥有者把一个共 享文件删除后,其他用户试图通过符号链去访问一个已被删除的共享文件时,会因系统找 不到该文件而使访问失败,于是再将符号链删除,此时不会产生任何影响。 然而符号链的共享方式也存在自己的问题: 当其他用户去读共享文件时,系统是根据给 定的文件路径名,逐个分量(名)地去查找目录,直至找到该文件的索引结点。因此,在每次 访问共享文件时,都可能要多次地读盘。这使每次访问文件的开销甚大,且增加了启动磁 第六章 文 件 管 理 ·237· 盘的频率。此外,要为每个共享用户建立一条符号链,而由于该链实际上是一个文件,尽 管该文件非常简单,却仍要为它配置一个索引结点,这也要耗费一定的磁盘空间。 符号链方式有一个很大的优点,是它能够用于链接(通过计算机网络)世界上任何地方的 计算机中的文件,此时只需提供该文件所在机器的网络地址以及该机器中的文件路径即可。 上述两种链接方式都存在这样一个共同的问题,即每一个共享文件都有几个文件名。 换言之,每增加一条链接,就增加一个文件名。这在实质上就是每个用户都使用自己的路 径名去访问共享文件。当我们试图去遍历(traverse)整个文件系统时,将会多次遍历到该共享 文件。例如,当有一个程序员要将一个目录中的所有文件都转储到磁带上去时,就可能对 一个共享文件产生多个拷贝。 6.6.3 磁盘容错技术 在现代计算机系统中,通常都存放了愈来愈多的宝贵信息供用户使用,给人们带来了 极大的好处和方便,但同时也潜在着不安全性。影响文件安全性的主要因素有三: (1) 人为因素,即由于人们有意或无意的行为,而使文件系统中的数据遭到破坏或丢失。 (2) 系统因素,即由于系统的某部分出现异常情况,而造成对数据的破坏或丢失。特别 是作为数据存储介质的磁盘,在出现故障或损坏时,会对文件系统的安全性造成影响; (3) 自然因素,即存放在磁盘上的数据,随着时间的推移将可能发生溢出或逐渐消失。 为了确保文件系统的安全性,可针对上述原因而采取以下措施: (1) 通过存取控制机制来防止由人为因素所造成的文件不安全性。 (2) 通过磁盘容错技术来防止由磁盘部分的故障所造成的文件不安全性。 (3) 通过“后备系统”来防止由自然因素所造成的不安全性。 本小节主要讨论磁盘容错技术,而存取控制机制将在系统安全性一章中介绍。 容错技术是通过在系统中设置冗余部件的办法,来提高系统可靠性的一种技术。磁盘 容错技术则是通过增加冗余的磁盘驱动器、磁盘控制器等方法,来提高磁盘系统可靠性的 一种技术,即当磁盘系统的某部分出现缺陷或故障时,磁盘仍能正常工作,且不致造成数 据的丢失或错误。目前广泛采用磁盘容错技术来改善磁盘系统的可靠性。 磁盘容错技术往往也被人们称为系统容错技术 SFT。可把它分成三个级别:第一级是 低级磁盘容错技术;第二级是中级磁盘容错技术;第三级是系统容错技术,它基于集群技 术实现容错。 1.第一级容错技术 SFT-Ⅰ 第一级容错技术(SFT-Ⅰ)是最基本的一种磁盘容错技术,主要用于防止因磁盘表面缺陷 所造成的数据丢失。它包含双份目录、双份文件分配表及写后读校验等措施。 1) 双份目录和双份文件分配表 在磁盘上存放的文件目录和文件分配表 FAT,是文件管理所用的重要数据结构。为了 防止这些表格被破坏,可在不同的磁盘上或在磁盘的不同区域中,分别建立(双份)目录表和 FAT。其中一份为主目录及主 FAT;另一份为备份目录及备份 FAT。一旦由于磁盘表面缺陷 而造成主文件目录或主 FAT 的损坏时,系统便自动启用备份文件目录及备份 FAT,从而可 以保证磁盘上的数据仍是可访问的。 ·238· 计算机操作系统 2) 热修复重定向和写后读校验 由于磁盘价格昂贵,当磁盘表面有少量缺陷时,则可采取某种补救措施后继续使用磁 盘。一般主要采取以下两个补救措施: (1) 热修复重定向:系统将磁盘容量的一部分(例如 2%~3%)作为热修复重定向区,用 于存放当发现磁盘有缺陷时的待写数据,并对写入该区的所有数据进行登记,以便于以后 对数据进行访问。 (2) 写后读校验方式。为了保证所有写入磁盘的数据都能写入到完好的盘块中,应该在 每次从内存缓冲区向磁盘中写入一个数据块后,又立即从磁盘上读出该数据块,并送至另 一缓冲区中,再将该缓冲区内容与内存缓冲区中在写后仍保留的数据进行比较。若两者一 致,便认为此次写入成功,可继续写下一个盘块;否则,再重写。若重写后两者仍不一致, 则认为该盘块有缺陷,此时,便将应写入该盘块的数据,写入到热修复重定向区中。 2.第二级容错技术 SFT-Ⅱ 第二级容错技术主要用于防止由磁盘驱动器和磁盘控制器故障所导致的系统不能正常 工作,它具体又可分为磁盘镜像和磁盘双工。 1) 磁盘镜像(Disk Mirroring) 为了避免磁盘驱动器发生故障而丢失数据,便增设了磁盘镜像功能。为实现该功能, 须在同一磁盘控制器下再增设一个完全相同的磁盘驱动器,如图 6-27 所示。当采用磁盘镜 像方式时,在每次向主磁盘写入数据后,都需要 将数据再写到备份磁盘上,使两个磁盘上具有完 全相同的位像图。把备份磁盘看作是主磁盘的一 面镜子,当主磁盘驱动器发生故障时,由于有备 份磁盘的存在,在进行切换后,使主机仍能正常 工作。磁盘镜像虽然实现了容错功能,但未能使 服务器的磁盘 I/O 速度得到提高,却使磁盘的利 磁 主 机 通道 盘 控 制 器 磁盘驱动器 图 6-27 磁盘镜像示意 用率降至仅为 50%。如图 6-27 所示。 2) 磁盘双工(Disk Duplexing) 如果控制这两台磁盘驱动器的磁盘控制器发生故障,或主机到磁盘控制器之间的通道 发生了故障,磁盘镜像功能便起不到数据保护的作用。因此,在第二级容错技术中,又增 加了磁盘双工功能,即将两台磁盘驱动器分别接到两个磁盘控制器上,同样使这两台磁盘 机镜像成对,如图 6-28 所示。 在磁盘双工时,文件服务器同时将数据写到 两个处于不同控制器下的磁盘上,使两者有完全 相同的位像图。如果某个通道或控制器发生故障 时,另一通道上的磁盘仍能正常工作,不会造成 数据的丢失。在磁盘双工时,由于每一个磁盘都 磁盘 通道 控制器 主 机 通道 磁盘驱动器 磁盘 控制器 有自己的独立通道,故可同时(并行)地将数据写入 磁盘,或读出数据。 图 6-28 磁盘双工示意 第六章 文 件 管 理 ·239· 3.基于集群技术的容错功能 进入 20 世纪 90 年代后,为了进一步增强服务器的并行处理能力和可用性,采用了多 台 SMP 服务器来实现集群系统服务器。所谓集群,是指由一组互连的自主计算机组成统一 的计算机系统,给人们的感觉是,它们是一台机器。利用集群系统不仅可提高系统的并行 处理能力,还可用于提高系统的可用性,它们是当前使用最广泛的一类具有容错功能的集 群系统。其主要工作模式有三种:① 热备份模式;② 互为备份模式;③ 公用磁盘模式。 下面我们介绍如何利用集群系统来提高服务器的可用性。 1) 双机热备份模式 如图 6-29 所示,在这种模式的系统中,备有两台服务器,两者的处理能力通常是完全 相同的,一台作为主服务器,另一台作为备份服务器。平时主服务器运行,备份服务器则 时刻监视着主服务器的运行,一旦主服务器出现故障,备份服务器便立即接替主服务器的 工作而成为系统中的主服务器,修复后的服务器再作为备份服务器。 主服务器 MSL 备份服务器 传输介质 图 6-29 双机热备份模式 为使在这两台服务器间能保持镜像关系,应在这两台服务器上各装入一块网卡,并通 过一条镜像服务器链路 MSL(Mirrored Server Link)将两台服务器连接起来。两台服务器之间 保持一定的距离,其所允许的距离取决于所配置的网卡和传输介质。如果用 FDDI 单模光纤, 两台服务器间的距离可达到 20 公里。此外,还必须在系统中设置某种机制,来检测主服务 器中数据的改变。一旦该机制检测到主服务器中有数据变化,便立即通过通信系统将修改 后的数据传送到备份服务器的相应数据文件中。为了保证在两台服务器之间通信的高速性 和安全性,通常都选用高速通信信道,并有备份线路。 在这种模式下,一旦主服务器发生故障,系统能自动地将主要业务用户切换到备份服 务器上。为保证切换时间足够快(通常为数分钟),要求在系统中配置有切换硬件的开关设备, 在备份服务器上事先建立好通信配置,并能迅速处理客户机的重新登录等事宜。 该模式是早期使用的一种集群技术,它的最大优点是提高了系统的可用性,易于实现, 而且主、备份服务器完全独立,可支持远程热备份,从而能消除由于火灾、爆炸等非计算 机因素所造成的隐患。该模式的主要缺点是从服务器处于被动等待状态,整个系统的使用 效率只有 50%。 2) 双机互为备份模式 在双机互为备份的模式中,平时,两台服务器均为在线服务器,它们各自完成自己的 任务,例如,一台作为数据库服务器,另一台作为电子邮件服务器。为了实现两者互为备 份,在两台服务器之间,应通过某种专线连接起来。如果希望两台服务器之间能相距较远, 最好利用 FDDI 单模光纤来连接两台服务器,在此情况下,最好再通过路由器将两台服务器 互连起来,作为备份通信线路。图 6-30 示出了双机互为备份系统的情况。 ·240· 计算机操作系统 具有两台 硬盘 数据库 服务器 FDDI单模光纤 电子邮件 服务器 具有两台 硬盘 交 换 路由器 集 线 器 X.25 图 6-30 双机互为备份系统的示意图 在互为备份的模式中,最好在每台服务器内都配置两台硬盘,一个用于装载系统程序 和应用程序,另一个用于接收由另一台服务器发来的备份数据,作为该服务器的镜像盘。 在正常运行时,镜像盘对本地用户是锁死的,这样就较易于保证在镜像盘中数据的正确性。 如果仅有一个硬盘,则可用建立虚拟盘的方式或分区方式来分别存放系统程序和应用程序, 以及另一台服务器的备份数据。 如果通过专线链接检查到某台服务器发生了故障,此时,再通过路由器去验证这台服 务器是否真的发生了故障。如果故障被证实,则由正常服务器向故障服务器的客户机发出 广播信息,表明要进行切换。连接到故障服务器上的客户机在切换过程中会感觉到网络服 务器的短暂停顿。在切换成功后,客户机无需重新登录便可继续使用网络提供的服务和访 问服务器上的数据。而对于接在非故障服务器上的客户机,则只会感觉到网络服务稍有减 慢而已,不会有任何影响。当故障服务器修复并重新连到网上后,已被迁移到无故障服务 器上的服务功能将被返回,恢复正常工作。 这种模式的优点是两台服务器都可用于处理任务,因而系统效率较高,现在已将这种 模式从两台机器扩大到 4 台、8 台、16 台甚至更多。系统中所有的机器都可用于处理任务, 当其中一台发生故障时,系统可指定另一台机器来接替它的工作。 3) 公用磁盘模式 为了减少信息复制的开销,可以将多台计算机连接到一台公共的磁盘系统上去。该公 共磁盘被划分为若干个卷。每台计算机使用一个卷。如果某台计算机发生故障,此时系统 将重新进行配置,根据某种调度策略来选择另一台替代机器,后者对发生故障的机器的卷 拥有所有权,从而来接替故障计算机所承担的任务。这种模式的优点是:消除了信息的复 制时间,因而减少了网络和服务器的开销。 6.7 数据一致性控制 数据一致性,是数据应用中必须解决的一个重要问题。事实上,只要把一个数据分别 存储到多个文件中时,便可能使数据一致性出现问题。例如,当我们发现某种商品的进价 第六章 文 件 管 理 ·241· 有错时,除需修改流水账外,还要对付费账、分类账及总账等一系列文件进行修改,方能 保证数据的一致性。但如果在修改进行到中途时系统突然发生故障,就会造成各个账目中 该数据的不一致性,进而使多个账目不一致。 为了保证在不同文件中所存储的同一个数据相一致,在现代操作系统乃至数据库系统 中,都配置了能保证数据一致性的软件,以及相应的支持硬件。硬件支持主要是要求在系 统中能配置一个高度可靠的存储器系统,或称之为稳定存储器(Stable Storage)。实现一个稳 定存储器的措施是采用冗余技术。亦即,将一份信息同时存放在多个独立的、非易失性存 储器(Nonvolatile Storage)上。目前,广泛采用磁盘双工方式来实现稳定存储器。 6.7.1 事务 1.事务的定义 事务是用于访问和修改各种数据项的一个程序单位。事务也可以被看做是一系列相关 读和写操作。被访问的数据可以分散地存放在同一文件的不同记录中,也可放在多个文件 中。只有对分布在不同位置的同一数据所进行的读和写(含修改)操作全部完成时,才能再以 托付操作(Commit Operation)来终止事务。只要有一个读、写或修改操作失败,便须执行夭 折操作(Abort Operation)。读或写操作的失败可能是由于逻辑错误,也可能是系统故障所导 致的。 一个夭折的事务,通常已执行了一些操作,因而可能已对某些数据做了修改。为使夭 折的事务不会引起数据的不一致性,须将该事务内刚被修改的数据项恢复成原来的情况, 使系统中各数据项与该事务未执行时的数据项内容完全相同。此时,可以说该事务“已被 退回”(rolled back)。不难看出,一个事务在对一批数据执行修改操作时,要么全部完成, 并用修改后的数据去代替原来的数据,要么一个也不修改。事务操作所具有的这种特性, 就是我们在第二章中曾讲过的“原子性”。 2.事务记录(Transaction Record) 为了实现上述的原子修改,通常须借助于称为事务记录的数据结构来实现。这些数据 结构被放在稳定存储器中,用来记录在事务运行时数据项修改的全部信息,故又称为运行 记录(Log)。该记录中包括有下列字段: · 事务名:用于标识该事务的惟一名字; · 数据项名:指被修改数据项的惟一名字; · 旧值:修改前数据项的值; · 新值:修改后数据项将具有的值。 在事务记录表中的每一记录,描述了在事务运行中的重要事务操作,如修改操作、开 始事务、托付事务或夭折事务等。在一个事务 Ti 开始执行时,〈Ti 开始〉记录被写入事务记 录表中;在 Ti 执行期间,在 Ti 的任何写(修改)操作之前,便写一适当的新记录到事务记录 表中;当 Ti 进行托付时,把一个〈Ti 托付〉记录写入事务记录表中。 3.恢复算法 由于一组被事务 Ti 修改的数据以及它们被修改前和修改后的值都能在事务记录表中找 到,因此,利用事务记录表,系统能处理任何故障而不致使故障造成非易失性存储器中信 ·242· 计算机操作系统 息的丢失。恢复算法可利用以下两个过程: (1) undo〈Ti〉。该过程把所有被事务 Ti 修改过的数据恢复为修改前的值。 (2) redo〈Ti〉。该过程把所有被事务 Ti 修改过的数据设置为新值。 如果系统发生故障,系统应对以前所发生的事务进行清理。通过查找事务记录表,可 以把尚未清理的事务分成两类。一类是其所包含的各类操作都已完成的事务。确定为这一 类事务的依据是,在事务记录表中,既包含了〈Ti 开始〉记录,又包含了〈Ti 托付〉记录。 此时系统利用 redo〈Ti〉过程,把所有已被修改的数据设置成新值。另一类是其所包含的 各个操作并未全部完成的事务。对于事务 Ti,如果在 Log 表中只有〈Ti 开始〉记录而无 〈Ti 托付〉记录,则此 Ti 便属于这类事务。此时,系统便利用 undo〈Ti〉过程,将所有已 被修改的数据,恢复为修改前的值。 6.7.2 检查点 1.检查点(Check Points)的作用 如前所述,当系统发生故障时,必须去检查整个 Log 表,以确定哪些事务需要利用 redo〈Ti〉过程去设置新值,而哪些事务需要利用 undo〈Ti〉过程去恢复数据的旧值。由于 在系统中可能存在着许多并发执行的事务,因而在事务记录表中就会有许多事务执行操作 的记录。随着时间的推移,记录的数据也会愈来愈多。因此,一旦系统发生故障,在事务 记录表中的记录清理起来就非常费时。 引入检查点的主要目的,是使对事务记录表中事务记录的清理工作经常化,即每隔一 定时间便做一次下述工作:首先是将驻留在易失性存储器(内存)中的当前事务记录表中的所 有记录输出到稳定存储器中;其次是将驻留在易失性存储器中的所有已修改数据输出到稳 定存储器中;然后是将事务记录表中的〈检查点〉记录输出到稳定存储器中;最后是每当 出现一个〈检查点〉记录时,系统便执行上小节所介绍的恢复操作,利用 redo 和 undo 过程 实现恢复功能。 如果一个事务 Ti 在检查点前就做了托付,则在事务记录表中便会出现一个在检查点记 录前的〈Ti 托付〉记录。在这种情况下,所有被 Ti 修改过的数据,或者是在检查点前已写 入稳定存储器,或者是作为检查点记录自身的一部分写入稳定存储器中。因此,以后在系 统出现故障时,就不必再执行 redo 操作了。 2.新的恢复算法 在引入检查点后,可以大大减少恢复处理的开销。因为在发生故障后,并不需要对事 务记录表中的所有事务记录进行处理,而只需对最后一个检查点之后的事务记录进行处 理。因此,恢复例程首先查找事务记录表,确定在最近检查点以前开始执行的最后的事务 Ti。在找到这样的事务后,再返回去搜索事务记录表,便可找到第一个检查点记录,恢复例 程便从该检查点开始,返回搜索各个事务的记录,并利用 redo 和 undo 过程对它们进行 处理。 如果把所有在事务 Ti 以后开始执行的事务表示为事务集 T,则新的恢复操作要求是: 对 所有在 T 中的事务 TK,如果在事务记录表中出现了〈TK 托付〉记录,则执行 redo〈TK〉 操作;反之,如果在事务记录表中并未出现〈TK 托付〉记录,则执行 undo〈TK〉操作。 第六章 文 件 管 理 ·243· 6.7.3 并发控制 在多用户系统和计算机网络环境下,可能有多个用户在同时执行事务。由于事务具有 原子性,这使各个事务的执行必然是按某种次序依次执行的,只有在一个事务执行完后, 才允许另一事务执行,即各事务对数据项的修改是互斥的。人们把这种特性称为顺序性 (Serializability)。把用于实现事务顺序性的技术称为并发控制(Concurrent Control)。该技术在 应用数据库系统中已被广泛采用,现也广泛应用于 OS 中。 虽然可以利用第二章所介绍的信号量机制来保证事务处理的顺序性(例如,令所有的事 务共享一互斥信号量,每当一事务开始执行时,便执行 wait(mutex)操作,在事务正常或异 常结束时,再执行 signal(mutex)操作),但在数据库系统和文件服务器中,应用得最多的还 是较简单且较灵活的同步机制——锁。 1.利用互斥锁实现“顺序性” 实现顺序性的一种最简单的方法是,设置一种用于实现互斥的锁,简称为互斥锁 (Exclusive Lock)。在利用互斥锁实现顺序性时,应为每一个共享对象设置一把互斥锁。当一 事务 Ti 要去访问某对象时,应先获得该对象的互斥锁。若成功,便用该锁将该对象锁住, 于是事务 Ti 便可对该对象执行读或写操作;而其它事务由于未能获得该锁而不能访问该对 象。如果 Ti 需要对一批对象进行访问,则为了保证事务操作的原子性,Ti 应先获得这一批 对象的互斥锁,以将这些对象全部锁住。如果成功,便可对这一批对象执行读或写操作; 操作完成后又将所有这些锁释放。但如果在这一批对象中的某一个对象已被其它事物锁住, 则此时 Ti 应对此前已被 Ti 锁住的其它对象进行开锁,宣布此次事务运行失败,但不致引起 数据的变化。 2.利用互斥锁和共享锁实现顺序性 利用互斥锁实现顺序性的方法简单易行。目前有不少系统都是采用这种方法来保证事 务操作的顺序性,但这却存在着效率不高的问题。因为一个共享文件虽然只允许一个事务 去写,但却允许多个事务同时去读;而在利用互斥锁来锁住文件后,则只允许一个事务去 读。为了提高运行效率而又引入了另一种形式的锁——共享锁(Shared Lock)。共享锁与互斥 锁的区别在于: 互斥锁仅允许一个事务对相应对象执行读或写操作,而共享锁则允许多个事 务对相应对象执行读操作,不允许其中任何一个事务对对象执行写操作。 在为一个对象设置了互斥锁和共享锁的情况下,如果事务 Ti 要对 Q 执行读操作,则只 需去获得对象 Q 的共享锁。如果对象 Q 已被互斥锁锁住,则 Ti 必须等待;否则,便可获得 共享锁而对 Q 执行读操作。如果 Ti 要对 Q 执行写操作,则 Ti 还须去获得 Q 的互斥锁。若 失败,须等待;否则,可获得互斥锁而对 Q 执行写操作。利用共享锁和互斥锁来实现顺序 性的方法,非常类似于我们在第二章中所介绍的读者—写者问题的解法。 6.7.4 重复数据的数据一致性问题 为了保证数据的安全性,最常用的做法是把关键文件或数据结构复制多份,分别存储 在不同的地方,当主文件(数据结构)失效时,还有备份文件(数据结构)可以使用,不会造成 数据丢失,也不会影响系统工作。显然,主文件(数据结构)中的数据应与各备份文件中的对 ·244· 计算机操作系统 应数据相一致。此外,还有些数据结构(如空闲盘块表)在系统运行过程中,总是不断地对它 进行修改,因此,同样应保证不同处的同一数据结构中数据的一致性。 1.重复文件的一致性 我们以 UNIX 类型的文件系统为例,来说明如何保证重复文件的一致性问题。对于通 常的 UNIX 文件目录,其每个目录项中含有一个 ASCII 码的文件名和一个索引结点号,后 者指向一个索引结点。当有重复文件时,一个目录项可由一个文件名和若干个索引结点号 组成,每个索引结点号都是指向各自的索引结点。图 6-31 示出了 UNIX 类型的目录和具有 重复文件的目录。 文件名 i 结点 文件 1 17 文件 2 22 文件 3 12 文件 4 84 (a) 不允许有重复文件的目录 文件名 i 结点 文件 1 17 19 40 文件 2 22 72 91 文件 3 12 30 29 文件 4 84 15 66 (b) 允许有重复文件的目录 图 6-31 UNIX 类型的目录 在有重复文件时,如果一个文件拷贝被修改,则必须也同时修改其它几个文件拷贝, 以保证各相应文件中数据的一致性。这可采用两种方法来实现:第一种方法是当一个文件 被修改后,可查找文件目录,以得到其它几个拷贝的索引结点号,再从这些索引结点中找 到各拷贝的物理位置,然后对这些拷贝做同样的修改;第二种方法是为新修改的文件建立 几个拷贝,并用新拷贝去取代原来的文件拷贝。 2.盘块号一致性的检查 为了描述盘块的使用情况,通常利用空闲盘块表(链)来记录所有尚未使用的空闲盘块的 编号。文件分配表 FAT 则是用于记录已分配盘块的使用情况。由于 OS 经常访问这些数据 结构,也对它们进行修改,而如果正在修改时,机器突然发生故障,此时也会使盘块数据 结构中的数据产生不一致性现象。因此,在每次启动机器时,都应该检查相应的多个数据 结构,看它们之间是否保持了数据的一致性。 为了保证盘块数据结构(中数据)的一致性,可利用软件方法构成一个计数器表,每个盘 块号占一个表项,可有 0,…,N-1 项,N 为盘块总数。每一个表项中包含两个计数器,分 别用作空闲盘块号计数器和数据盘块号计数器。计数器表中的表项数目等于盘块数 N。在对 盘块的数据结构进行检查时,应该先将计数器表中的所有表项初始化为 0,然后,用 N 个空 闲盘块号计数器所组成的第一组计数器来对从空闲盘块表(链)中读出的盘块号进行计数;再 用 N 个数据盘块号计数器所组成的第二组计数器去对从文件分配表中读出的、已分配给文 件使用的盘块号进行计数。如果情况正常,则上述两组计数器中对应的一对(计数器中的) 数据应该互补,亦即,若某个盘块号在被第一组计数器进行计数后,使该盘块号计数器为 1, 则在第二组计数器中相应盘块号计数器中的计数必为 0;反之亦然。但如果情况并非如此, 则说明发生了某种错误。 第六章 文 件 管 理 ·245· 图 6-32(a)示出了在正常情况下,在第一组计数器和第二组计数器中的盘块号计数值是 互补的;而图 6-32(b)示出的则是一种不正常的情况,对盘块号 2 的计数值在两组计数器中 都未出现(即均为 0)。当检查出这种情况时,应向系统报告。该错误的影响并不大,只是盘 块 2 未被利用。其解决方法也较简单,只需在空闲盘块表(链)中增加一个盘块号 2。图 6-32(c) 中示出了另一种错误,即盘块号 4 在空闲盘块表(链)中出现了两次,其解决方法是从空闲盘 块表(链)中删除一个空闲盘块号 4。图 6-32(d)中所示出的情况是相同的数据盘块号 5 出现了 两次(或多次),此种情况影响较严重,必须立即报告。 计数器组 盘块号 空闲盘块号计数器组 数据盘块号计数器组 盘块号 计数器组 空闲盘块号计数器组 数据盘块号计数器组 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1101011110011100 0010100001100011 (a) 正常情况盘块号 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1101011110011100 0000100001100011 (b) 丢失了盘块盘块号 盘块号 计数器组 空闲盘块号计数器组 数据盘块号计数器组 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1101211110011100 0010000001100010 (c) 空闲盘块号重复出现盘块号 盘块号 计数器组 空闲盘块号计数器组 数据盘块号计数器组 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1101101110011100 0010020001100011 (d) 数据盘块号重复出现 图 6-32 检查盘块号一致性情况 3.链接数一致性检查 在 UNIX 类型的文件目录中,其每个目录项内都含有一个索引结点号,用于指向该文 件的索引结点。对于一个共享文件,其索引结点号会在目录中出现多次。例如,当有 5 个 用户(进程)共享某文件时,其索引结点号会在目录中出现 5 次;另一方面,在该共享文件的 索引结点中有一个链接计数 count,用来指出共享本文件的用户(进程)数。在正常情况下这 两个数据应该一致,否则就会出现数据不一致性差错。 为了检查这种数据不一致性差错,同样要配置一张计数器表,此时应是为每个文件而 ·246· 计算机操作系统 不是为每个盘块建立一个表项,其中含有该索引结点号的计数值。在进行检查时,从根目 录开始查找,每当在目录中遇到该索引结点号时,便在该计数器表中相应文件的表项上加 1。 当把所有目录都检查完后,便可将该计数器表中每个表项中的索引结点号计数值与该文件 索引结点中的链接计数 count 值加以比较,如果两者一致,表示是正确的;否则,便是产生 了链接数据不一致的错误。 如果索引结点中的链接计数 count 值大于计数器表中相应索引结点号的计数值,则即使 在所有共享此文件的用户都不再使用此文件时,其 count 值仍不为 0,因而该文件不会被删 除。这种错误的后果是使一些已无用户需要的文件仍驻留在磁盘上,浪费了存储空间。当 然这种错误的性质并不严重。解决的方法是用计数器表中的正确的计数值去为 count 重新 赋值。 反之,如果出现 count 值小于计数器表中索引结点号计数值的情况时,就有潜在的危险。 假如有两个用户共享一个文件,但是 count 值仍为 1,这样,只要其中有一个用户不再需要 此文件时,count 值就会减为 0,从而使系统将此文件删除,并释放其索引结点及文件所占 用的盘块,导致另一共享此文件的用户所对应的目录项指向了一个空索引结点,最终是使 该用户再无法访问此文件。如果该索引结点很快又被分配给其它文件,则又会带来潜在的 危险。解决的方法是将 count 值置为正确值。 习题 1. 何谓数据项、记录和文件? 2. 文件系统的模型可分为三层,试说明其每一层所包含的基本内容。 3. 试说明用户可以对文件施加的主要操作有哪些。 4. 何谓逻辑文件?何谓物理文件? 5. 如何提高对变长记录顺序文件的检索速度? 6. 试说明对索引文件和索引顺序文件的检索方法。 7. 试从检索速度和存储费用两方面来比较两级索引文件和索引顺序文件。 8. 试说明顺序文件的结构及其优点。 9. 在链接式文件中常用哪种链接方式?为什么? 10. 在 MS-DOS 中有两个文件 A 和 B,A 占用 11、12、16 和 14 四个盘块;B 占用 13、 18 和 20 三个盘块。试画出在文件 A 和 B 中各盘块间的链接情况及 FAT 的情况。 11. NTFS 文件系统对文件采用什么样的物理结构? 12. 假定一个文件系统的组织方式与 MS-DOS 相似,在 FAT 中可有 64 K 个指针,磁盘 的盘块大小为 512B,试问该文件系统能否指引一个 512MB 的磁盘? 13. 为了快速访问,又易于更新,当数据为以下形式时,应选用何种文件组织方式。 (1) 不经常更新,经常随机访问; (2) 经常更新,经常按一定顺序访问; (3) 经常更新,经常随机访问; 14.在 UNIX 中,如果一个盘块的大小为 1 KB,每个盘块号占 4 个字节,即每块可放 第六章 文 件 管 理 ·247· 256 个地址。请转换下列文件的字节偏移量为物理地址: (1) 9999 ;(2) 18000 ;(3) 420000 。 15. 什么是索引文件?为什么要引入多级索引? 16. 试说明 UNIX 系统中所采用的混合索引分配方式。 17. 对目录管理的主要要求是什么? 18. 采用单级目录能否满足对目录管理的主要要求?为什么? 19. 目前广泛采用的目录结构形式是哪种?它有什么优点? 20. Hash 检索法有何优点?又有何局限性? 21. 在 Hash 检索法中,如何解决“冲突”问题? 22. 试说明在树型目录结构中线性检索法的检索过程,并给出相应的流程图。 23. 有一计算机系统利用图 6-33 所示的位示图来管理空闲盘块。盘块的大小为 1 KB, 现要为某文件分配两个盘块,试说明盘块的具体分配过程。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 111111111111 1 1 1 1 1 211111111111 1 1 1 1 1 311011111111 1 1 1 1 1 411111101111 0 1 1 1 1 500000000000 0 0 0 0 0 图 6-33 某计算机系统的位示图 24. 某操作系统的磁盘文件空间共有 500 块,若用字长为 32 位的位示图管理盘空间, 试问: (1) 位示图需多少个字? (2) 第 i 字第 j 位对应的块号是多少? (3) 给出申请/归还一块的工作流程。 25. 对空闲磁盘空间的管理常采用哪几种分配方式?在 UNIX 系统中采用何种分配 方式? 26. 基于索引结点的文件共享方式有何优点? 27. 基于符号链的文件共享方式有何优点? 28. 在第一级系统容错技术中,包括哪些容错措施?什么是写后读校验? 29. 在第二级系统容错技术中,包括哪些容错措施?画图说明之。 30. 何谓事务?如何保证事务的原子性? 31. 引入检查点的目的是什么?引入检查点后又如何进行恢复处理? 32. 为何引入共享锁?如何用互斥锁或共享锁来实现事务的顺序性? 33. 当系统中有重复文件时,如何保证它们的一致性? 34. 如何检索盘块号的一致性?检查时可能出现哪几种情况? ·248· 计算机操作系统 第七章 操作系统接口 操作系统是用户与计算机硬件系统之间的接口,用户通过操作系统的帮助,可以快速、 有效和安全、可靠地操纵计算机系统中的各类资源,以处理自己的程序。为使用户能方便 地使用操作系统,OS 又向用户提供了如下两类接口: (1) 用户接口:操作系统专门为用户提供了“用户与操作系统的接口”,通常称为用户 接口。该接口支持用户与 OS 之间进行交互,即由用户向 OS 请求提供特定的服务,而系统 则把服务的结果返回给用户。 (2) 程序接口:操作系统向编程人员提供了“程序与操作系统的接口”,简称程序接口, 又称应用程序接口 API(Application Programming Interface)。该接口是为程序员在编程时使用 的,系统和应用程序通过这个接口,可在执行中访问系统中的资源和取得 OS 的服务,它也 是程序能取得操作系统服务的惟一途径。大多数操作系统的程序接口是由一组系统调用 (system call)组成,每一个系统调用都是一个能完成特定功能的子程序。 值得说明的是,在计算机网络中,特别是在 Internet 广为流行的今天,又出现了一种面 向网络的网络用户接口。 7.1 联机用户接口 当今,几乎所有的计算机(从大、中型机到微型机)操作系统中,都向用户提供了用户接 口,允许用户在终端上键入命令,或向操作系统提交作业书,来取得 OS 的服务,并控制自 己程序的运行。一般地,用户接口又可进一步分为如下两类: (1) 联机用户接口:终端用户利用该接口可以调用操作系统的功能,取得操作系统的服 务。用户可以使用联机控制命令来对自己的作业进行控制。联机用户接口可以实现用户与 计算机间的交互。 (2) 脱机用户接口:该接口是专为批处理作业的用户提供的,也称批处理用户接口。操 作系统提供了一个作业控制语言 JCL(Job Control Language),用户使用 JCL 语言预先写好作 业说明书,将它和作业的程序与数据一起提交给计算机,当该作业运行时,OS 将逐条按照 用户作业说明书的控制语句,自动控制作业的执行。应当指出,脱机用户接口是不能实现 用户与计算机间的交互的。 7.1.1 联机用户接口 联机用户接口,也称为联机命令接口。不同操作系统的联机命令接口有所不同,这不 仅指命令的种类、数量及功能方面,也可能体现在命令的形式、用法等方面。不同的用法 第七章 操作系统接口 ·249· 和形式构成了不同的用户界面,可分成以下两种: (1) 字符显示式用户界面; (2) 图形化用户界面。 本节主要介绍字符显示用户界面式的联机用户接口,而图形化用户界面的联机用户接 口将在本章最后一节中介绍。 所谓“字符显示式用户界面”,即用户在利用该用户界面的联机用户接口实现与机器的 交互时,先在终端的键盘上键入所需的命令,由终端处理程序接收该命令,并在用户终端 屏幕上,以字符显示方式反馈用户输入的命令信息、命令执行及执行结果信息。用户主要 通过命令语言来实现对作业的控制和取得操作系统的服务。 用户在终端键盘上键入的命令被称为命令语言,它是由一组命令动词和参数组成的, 以命令行的形式输入并提交给系统。命令语言具有规定的词法、语法、语义和表达形式。 该命令语言是以命令为基本单位指示操作系统完成特定的功能。完整的命令集反映了系统 提供给用户可使用的全部功能。不同操作系统所提供的命令语言的词法、语法、语义及表 达形式是不一样的。命令语言一般又可分成两种方式:命令行方式和批命令方式。 1.命令行方式 该方式是指以行为单位输入和显示不同的命令。每行长度一般不超过 256 个字符,命 令的结束通常以回车符为标记。命令的执行是串行、间断的,后一个命令的输入一般需等 到前一个命令执行结束,如用户键入的一条命令处理完成后,系统发出新的命令输入提示 符,用户才可以继续输入下一条命令。 也有许多操作系统提供了命令的并行执行方式,例如一条命令的执行需要耗费较长时 间,并且用户也不急需其结果时(即两条命令执行是不相关的),则可以在一个命令的结尾输 入特定的标记,将该命令作为后台命令处理,用户接着即可继续输入下一条命令,系统便 可对两条命令进行并行处理。一般而言,对新用户来说,命令行方式十分繁琐,难以记忆, 但对有经验的用户而言,命令行方式用起来快捷便当、十分灵活,所以,至今许多操作员 仍常使用这种命令方式。 简单命令的一般形式为: Command arg1 arg2 ...argn 其中,Command 是命令名,又称命令动词,其余为该命令所带的执行参数,有些命令可以 没有参数。 2.批命令方式 在操作命令的实际使用过程中,经常遇到需要对多条命令的连续使用,或若干条命令 的重复使用,或对不同命令进行选择性使用的情况。如果用户每次都采用命令行方式,将 命令一条条由键盘输入,既浪费时间,又容易出错。因此,操作系统都支持一种称为批命 令的特别命令方式,允许用户预先把一系列命令组织在一种称为批命令文件的文件中,一 次建立,多次执行。使用这种方式可减少用户输入命令的次数,既节省了时间和减少了出 错概率,又方便了用户。通常批命令文件都有特殊的文件扩展名,如 MS-DOS 系统的 .BAT 文件。 同时,操作系统还提供了一套控制子命令,增强对命令文件使用的支持。用户可以使 ·250· 计算机操作系统 用这些子命令和形式参数书写批命令文件,使得这样的批命令文件可以执行不同的命令序 列,从而增强了命令接口的处理能力。如 UNIX 和 Linux 中的 Shell 不仅是一种交互型命 令解释程序,也是一种命令级程序设计语言解释系统,它允许用户使用 Shell 简单命令、位 置参数和控制流语句编制带形式参数的批命令文件,称做 Shell 文件或 Shell 过程,Shell 可 以自动解释和执行该文件或过程中的命令。 7.1.2 联机命令的类型 为了能向用户提供多方面的服务,通常,OS 都向用户提供了几十条甚至上百条的联机 命令。根据这些命令所完成功能的不同,可把它们分成以下几类: ① 系统访问类;② 磁盘 操作类;③ 文件操作类;④ 目录操作类;⑤ 通信类;⑥ 其他命令。现分述如下。 1.系统访问类 在单用户微型机中,一般没有设置系统访问命令。然而在多用户系统中,为了保证系 统的安全性,都毫无例外地设置了系统访问命令,即注册命令 Login。用户在每次开始使用 某终端时,都须使用该命令,使系统能识别该用户。凡要在多用户系统的终端上上机的用 户,都必须先在系统管理员处获得一合法的注册名和口令。以后,每当用户在接通其所用 终端的电源后,便由系统直接调用,并在屏幕上显示出以下的注册命令: Login: /提示用户键入自己的注册名 当用户键入正确的注册名,并按下回车键后,屏幕上又会出现: Password: /提示用户键入自己的口令 用户在键入口令时,系统将关闭掉回送显示,以使口令不在屏幕上显示出来。如果键入的 口令正确而使注册成功时,屏幕上会立即出现系统提示符(所用符号随系统而异),表示用户 可以开始键入命令。如果用户多次(通常不超过三次)键入的注册名或口令都有错,系统将解 除与用户的联接。 2.磁盘操作命令 在微机操作系统中,通常都提供了若干条磁盘操作命令。 (1) 磁盘格式化命令 Format。它被用于对指定驱动器上的软盘进行格式化。每张新盘在 使用前都必须先格式化。其目的是使磁盘记录格式能为操作系统所接受。可见,不同操作 系统将磁盘初始化后的格式各异。此外,在格式化过程中,还将对有缺陷的磁道和扇区加 保留记号,以防止将它分配给数据文件。 (2) 复制整个软盘命令 Diskcopy。该命令用于复制整个磁盘,另外它还有附加的格式化 功能。如果目标盘片是尚未格式化的,则该命令在执行时,首先将未格式化的软盘格式化, 然后再进行复制。 (3) 软盘比较命令 Diskcomp。该命令用于将源盘与目标盘的各磁道及各扇区中的数据 逐一进行比较。 (4) 备份命令 Backup。该命令用于把硬盘上的文件复制到软盘上,而 RESTORE 命令则 完成相反的操作。 3.文件操作命令 每个操作系统都提供了一组文件操作命令。在微机 OS 中的文件操作命令有下述几种: 第七章 操作系统接口 ·251· (1) 显示文件命令 type:用于将指定文件内容显示在屏幕上。 (2) 拷贝文件命令 copy:用于实现文件的拷贝。 (3) 文件比较命令 comp:用于对两个指定文件进行比较。两文件可以在同一个或不同 的驱动器上。 (4) 重新命名命令 Rename:用于将以第一参数命名的文件改成用第二参数给定的名字。 (5) 删除文件命令 erase:用于删除一个或一组文件,当参数路径名为*.BAK 时,表示 删除指定目录下的所有其扩展名为 .Bak 的文件。 4.目录操作命令 目录操作命令包括下述几个命令: (1) 建立子目录命令 mkdir:用于建立指定名字的新目录。 (2) 显示目录命令 dir:用于显示指定磁盘中的目录项。 (3) 删除子目录命令 rmdir:用于删除指定的子目录文件,但不能删除普通文件,而且, 一次只能删除一个空目录(其中仅含“.”和“..”两个文件),不能删除根及当前目录。 (4) 显示目录结构命令 tree:用于显示指定盘上的所有目录路径及其层次关系。 (5) 改变当前目录命令 chdir:用于将当前目录改变为由路径名参数给定的目录。用“..” 作参数时,表示应返回到上一级目录下。 5.其它命令 (1) 输入输出重定向命令。在有的 OS 中定义了两个标准 I/O 设备。通常,命令的输入 取自标准输入设备,即键盘;而命令的输出通常是送往标准输出设备,即显示终端。如果 在命令中设置输出重定向“>”符,其后接文件名或设备名,表示将命令的输出改向,送到 指定文件或设备上。类似地,若在命令中设置输入重定向“<”符,则不再是从键盘而是从 重定向符左边参数所指定的文件或设备上,取得输入信息。 (2) 管道连接。这是指把第一条命令的输出信息作为第二条命令的输入信息;类似地, 又可把第二条命令的输出信息作为第三条命令的输入信息。这样,由两个(含两条)以上的命 令可形成一条管道。在 MS-DOS 和 UNIX 中,都用“|”作为管道符号,其一般格式为: Command1 | Command2 | … | Commandn; (3) 过滤命令。在 UNIX 及 MS-DOS 中都有过滤命令,用于读取指定文件或标准输入, 从中找出由参数指定的模式,然后把所有包含该模式的行都打印出来。例如,MS-DOS 中 用命令 find/N“erase”(路径名) 可对由路径名指定的输入文件逐行检索,把含有字符串“erase”的行输出。其中,/N 是选 择开关,表示输出含有指定字串的行;如果不用 N 而用 C,则表示只输出含有指定字串的 行数;若用 V,则表示输出不含指定字串的行。 (4) 批命令。为了能连续地使用多条键盘命令,或多次反复地执行指定的若干条命令, 而又免去每次重敲这些命令的麻烦,可以提供一特定文件。在 MS-DOS 中提供了一种特殊 文件,其后缀名用“.BAT”;在 UNIX 系统中称为命令文件。它们都是利用一些键盘命令构 成一个程序,一次建立供多次使用。在 MS-DOS 中用 batch 命令去执行由指定或默认驱动 器的工作目录上指定文件中所包含的一些命令。 ·252· 计算机操作系统 7.1.3 键盘终端处理程序 为了实现人机交互,还须在微机或终端上配置相应的键盘终端处理程序,它应具有下 述几方面的功能: (1) 接收用户从终端上打入的字符。 (2) 字符缓冲,用于暂存所接收的字符。 (3) 回送显示。 (4) 屏幕编辑。 (5) 特殊字符处理。 1.字符接收功能 为了实现人机交互,键盘终端处理程序必须能够接收从终端输入的字符,并将之传送 给用户程序。有两种方式来实现字符接收功能: (1) 面向字符方式。驱动程序只接收从终端打入的字符,并且不加修改地将它传送给用 户程序。这通常是一串未加工的 ASCII 码。但大多数的用户并不喜欢这种方式。 (2) 面向行方式。终端处理程序将所接收的字符暂存在行缓冲中,并可对行内字符进行 编辑。仅在收到行结束符后,才将一行正确的信息送命令解释程序。在有的计算机中,从 键盘硬件送出的是键的编码(简称键码),而不是 ASCII 码。例如,当打入 a 键时,是将键码 “30”放入 I/O 寄存器,此时,终端处理程序必须参照某种表格,将键码转换成 ASCII 码。 应当注意,某些 IBM 的兼容机使用的不是标准键码。此时,处理程序还须选用相应的表格 将其转换成标准键码。 2.字符缓冲功能 为了能暂存从终端键入的字符,以降低中断处理器的频率,在终端处理程序中,还必 须具有字符缓冲功能。字符缓冲可采用以下两种方式之一: (1) 专用缓冲区方式。这是指系统为每个终端设置一个缓冲区,暂存用户键入的一批字 符,缓冲区的典型长度为 200 个字符左右。这种方式较适合于单用户微机或终端很少的多 用户机。当终端数目较多时,需要的缓冲区数目可能很大,且每个缓冲区的利用率也很低。 例如,当有 100 个终端时,要求有 20 KB 的缓冲区。但专用缓冲区方式可使终端处理程序 简化。图 7-1(a)示出了专用缓冲区方式。 终端 专用缓冲区 终端 … 公用缓冲池 终端 主机 主机 (a) 专用缓冲区方式 (b) 公用缓冲池方式 图 7-1 两种缓冲方式 第七章 操作系统接口 ·253· (2) 公用缓冲池方式。系统不必为每个终端设置专用缓冲区,只须设置一个由多个缓冲 区构成的公用缓冲池。其中的每个缓冲区大小相同,如为 20 个字符,再将所有的空缓冲区 链接成一个空缓冲区链。当终端有数据输入时,可先向空缓冲区链申请一空缓冲区来接收 输入字符;当该缓冲区装满后,再申请一空缓冲区。这样,直至全部输入完毕,并利用链 接指针将这些装有输入数据的缓冲区链接成一条输入链。每当该输入链中一个缓冲区内的 字符被全部传送给用户程序后,便将该缓冲区从输入链中移出,再重新链入空缓冲区链中。 显然,利用公用缓冲池方式可有效地提高缓冲的利用率。图 7-1(b)示出了公用缓冲池方式。 3.回送显示 回送显示(回显)是指每当用户从键盘输入一个字符后,终端处理程序便将该字符送往屏 幕显示。有些终端的回显由硬件实现,其速度较快,但往往会引起麻烦。如当用户键入口 令时,为防止口令被盗用,显然不该有回显。此外,用硬件实现回显也缺乏灵活性,因而 近年来多改用软件来实现回显,这样可以做到在用户需要时才回显。用软件实现回显,还 可方便地进行字符变换,如将键盘输入的小写英文字母变成大写,或相反。驱动程序在将 输入的字符送往屏幕回显时,应打印在正确的位置上;当光标走到一行的最后一个位置后, 便应返回到下一行的开始位置。例如,当所键入的字符数目超过一行的 80 个(字符)时,应 自动地将下一个字符打印到下一行的开始位置。 4.屏幕编辑 用户经常希望能对从键盘打入的数据(字符)进行修改,如删除(插入)一个或多个字符。 为此,在终端处理程序中,还应能实现屏幕编辑功能,包括能提供若干个编辑键。常用的 编辑键有: (1) 删除字符键。它允许将用户刚键入的字符删除。在有的系统中是利用退格键即 Backspace(Ctrl+H)键。当用户敲该键时,处理程序并不将刚键入的字符送入字符队列,而是 从字符队列中移出其前的一个字符。 (2) 删除一行键。该键用于将刚输入的一行删去。 (3) 插入键。利用该键在光标处可插入一个字符或一行正文。 (4) 移动光标键。在键盘上有用于对光标进行上、下、左、右移动的键。 (5) 屏幕上卷或下移键,等等。 5.特殊字符处理 终端处理程序必须能对若干特殊字符进行及时处理,这些字符是: (1) 中断字符。当程序在运行中出现异常情况时,用户可通过键入中断字符的办法来中 止当前程序的运行。在许多系统中是利用 Break 或 Delete 或 Ctrl+C 键作为中断字符。对中 断字符的处理比较复杂。当终端处理程序收到用户键入的中断字符后,将向该终端上的所 有进程发送一个要求进程终止的软中断信号,这些进程收到该软中断信号后,便进行自我 终止。 (2) 停止上卷字符。用户键入此字符后,终端处理程序应使正在上卷的屏幕暂停上卷, 以便用户仔细观察屏幕内容。在有的系统中,是利用 Ctrl+S 键来停止屏幕上卷的。 (3) 恢复上卷字符。有的系统利用 Ctrl+Q 键使停止上卷的屏幕恢复上卷。终端处理程 序收到该字符后,便恢复屏幕的上卷功能。 ·254· 计算机操作系统 上述的 Ctrl+S 与 Ctrl+Q 两字符并不被存储,而是被用去设置终端数据结构中的某个标 志。每当终端试图输出时,都须先检查该标志。若该标志已被设置,便不再把字符送至 屏幕。 7.1.4 命令解释程序 在所有的 OS 中,都是把命令解释程序放在 OS 的最高层,以便能直接与用户交互。该 程序的主要功能是先对用户输入的命令进行解释,然后转入相应命令的处理程序去执行。 在 MS-DOS 中的命令解释程序是 COMMAND.COM,在 UNIX 中是 Shell。本小节主要介绍 MS-DOS 的命令解释程序,下一节再介绍 Shell。 1.命令解释程序的作用 在联机操作方式下,终端处理程序把用户键入的信息送键盘缓冲区中保存。一旦用户 键入回车符,便立即把控制权交给命令处理程序。显然,对于不同的命令,应有能完成特 定功能的命令处理程序与之对应。可见,命令解释程序的主要作用是在屏幕上给出提示符, 请用户键入命令,然后读入该命令,识别命令,再转到相应命令处理程序的入口地址,把 控制权交给该处理程序去执行,并将处理结果送屏幕上显示。若用户键入的命令有错,而 命令解释程序未能予以识别,或在执行中间出现问题时,则应显示出某一出错信息。 2.命令解释程序的组成 MS-DOS 是 1981 年由 Microsoft 公司开发的、配置在微机上的 OS。随着微机的发展, MS-DOS 的版本也在不断升级,由开始时的 1.0 版本升级到 1994 年的 6.X 版本。在此期间, 它已是事实上的 16 位微机 OS 的标准。 我们以 MS-DOS 操作系统中的 COMMAND.COM 处理程序为例,来说明命令解释程序的组成。它包括以下三部分: (1) 常驻部分。这部分包括一些中断服务子程序。例如:正常退出中断 INT 20,它用于 在用户程序执行完毕后,退回操作系统;驻留退出中断 INT 27,用这种方式,退出程序可 驻留在内存中;还有用于处理和显示标准错误信息的 INT 24 等。常驻部分还包括这样的程 序: 当用户程序终止后,它检查暂存部分是否已被用户程序覆盖,若已被覆盖,便重新将暂 存部分调入内存。 (2) 初始化部分。它跟随在常驻内存部分之后,在启动时获得控制权。这部分还包括对 AUTOEXEC.BAT 文件的处理程序,并决定应用程序装入的基地址。每当系统接电或重新启 动后,由处理程序找到并执行 AUTOEXEC.BAT 文件。由于该文件在用完后不再被需要, 因而它将被第一个由 COMMAND.COM 装入的文件所覆盖。 (3) 暂存部分。这部分主要是命令解释程序,并包含了所有的内部命令处理程序、批文 件处理程序,以及装入和执行外部命令的程序。它们都驻留在内存中,但用户程序可以使 用并覆盖这部分内存,在用户程序结束时,常驻程序又会将它们重新从磁盘调入内存,恢 复暂存部分。 3.命令解释程序的工作流程 系统在接通电源或复位后,初始化部分获得控制权,对整个系统完成初始化工作,并 自动执行 AUTOEXEC.BAT 文件,之后便把控制权交给暂存部分。暂存部分首先读入键盘 缓冲区中的命令,判别其文件名、扩展名及驱动器名是否正确。若发现有错,在给出出错 第七章 操作系统接口 ·255· 信息后返回;若无错,再识别该命令。一种简单的识别命令的方法是基于一张表格,其中 的每一表目都是由命令名及其处理程序的入口地址两项所组成的。如果暂存部分在该表中 能找到键入的命令,且是内部命令,便可以直接从对应表项中获得该命令处理程序的入口 地址,然后把控制权交给该处理程序去执行该命令。如果发现键入的命令不属于内部命令 而是外部命令,则暂存部分还须为之建立命令行;再通过执行系统调用 exec 来装入该命令 的处理程序,并得到其基地址;然后把控制权交给该程序去执行相应的命令。图 7-2 示出 了 MS-DOS 的 COMMAND.COM 的工作流程。 输入命令行 命令合法否? 是内部命令? 从外存调入外部命 令执行 DIR 命令 处理 程序 Type 命令 处理 程序 Copy 命令 处理 程序 Date 命令 处理 程序 显示提示符 图 7-2 COMMAND.COM 的工作流程 7.2 Shell 命令语言 UNIX 的 Shell 是作为操作系统的最外层,也称为外壳。它可以作为命令语言,为用户 提供使用操作系统的接口,用户利用该接口与机器交互。Shell 也是一种程序设计语言,用 户可利用多条 Shell 命令构成一个文件,或称为 Shell 过程。Shell 还包括了 Shell 命令解释 程序,用于对从标准输入或文件中读入的命令进行解释执行。由于篇幅所限,本节主要对 Shell 命令语言进行详细的介绍,关于 Shell 过程和 Shell 命令解释程序可参考其它书籍。 7.2.1 简单命令 所谓简单命令,实际上是一个能完成某种功能的目标程序的名字。UNIX 系统规定的命 令由小写字母构成(仅前 8 个字母有效)。命令可带有参数表,用于给出执行命令时的附加信 息。命令名与参数表之间还可使用一种称为选项的自变量,用破折号开始,后跟一个或多 个字母、数字。选项是对命令的正常操作加以修改,一条命令可有多个选项,命令的格式 ·256· 计算机操作系统 如下: $ Command-option argument list 例如: $ LS file1 file2 ↙ 这是一条不带选项的列目录命令,$是系统提示符。该命令用于列出 file1 和 file2 两个目录 文件中所包含的目录项,并隐含地指出按英文字母顺序列表。若给出-tr 选项,该命令可表 示成: $ LS-tr file1 file 2 ↙ 其中,选项 t 和 r 分别表示按最近修改次序及按反字母顺序列表。 通常,命令名与该程序的功能紧密相关,以便于记忆。命令参数可多可少,也可缺省。 例如: $ LS ↙ 表示自动以当前工作目录为缺省参数,打印出当前工作目录所包含的目录项。简单命令的 格式比较自由,包括命令名字符的个数及用于分隔命令名、选项、各参数间的空格数等。 简单命令的数量易于扩充。系统管理员与用户自行定义的命令,其执行方式与系统标准命 令的执行方式相同。除少数标准命令作为内部命令常驻内存外,其余命令均存于盘上,以 节省内存空间。下面按其功能的不同,将它们分成五大类加以简单介绍。 1.进入与退出系统 (1) 进入系统,也称为注册。事先,用户须与系统管理员商定一个唯一的用户名。管理 员用该名字在系统文件树上,为用户建立一个子目录树的根结点。当用户打开自己的终端 时,屏幕上会出现 Login:提示,这时用户便可键入自己的注册名,并用回车符结束。然后, 系统又询问用户口令,用户可用回车符或事先约定的口令键入。这两步均须正确通过检查, 才能出现系统提示符(随系统而异),以提示用户自己已通过检查,可以使用系统。若任一步 骤有错,系统均通过提示要求用户重新键入。 (2) 退出系统。每当用户用完系统后,应向系统报告自己不再往系统装入任何处理要求。 系统得知后,便马上为用户记账,清除用户的使用环境。若用户使用系统是免费的,退出 操作仅仅是一种礼貌。如果用户使用的是多终端中的一个终端,为了退出,用户只需按下 Control-D 键即可,系统会重新给出提示符即 Login,以表明该终端可供另一新用户使用。 用户的进入与退出过程,实际上是由系统直接调用 Login 及 Logout 程序完成的。 2.文件操作命令 (1) 显示文件内容命令 cat。如果用户想了解自己在当前目录中的某个或某几个指定文 件的内容时,便可使用下述格式的 cat 命令: $ cat filename1 filename2 ↙ 执行上述命令后,将按参数指定的顺序,依次把所列名字的文件内容送屏幕显示。若键入 文件名有错,或该文件不在当前目录下,则该命令执行结果将显示指定文件不能打开的 信息。 (2) 复制文件副本的命令 cp。其格式为: cp source target 第七章 操作系统接口 ·257· 该命令用于对已存在的文件 source 建立一个名为 target 的副本。 (3) 对已有文件改名的命令 mv。其格式为 mv oldname newname 用于把原来的老名字改成指定的新名字。 (4) 撤消文件的命令 rm。它给出一个参数表,是要撤消的文件名清单。 (5) 确定文件类型的命令 file。该命令带有一个参数表,用于给出想了解其(文件)类型的 文件名清单。命令执行的结果将在屏幕上显示出各个文件的类型。 3.目录操作命令 (1) 建立目录的命令 mkdir(简称 md)。当用户要创建或保存较多的文件时,应该以自己 的注册名作为根结点,建立一棵子目录树,子树中的各结点(除树叶外)都是目录文件。可用 md 命令来构建一个目录,参数是新创建目录的名字。但应注意该命令的使用,必须在其父 目录中有写许可时,才允许为其创建子目录。 (2) 撤消目录的命令 rmdir(简称 rd)。它实际上是 rm 命令的一个特例,用于删除一个或 多个指定的下级空目录。若目录下仍有文件,该命令将被认为是一个错误操作,这样可以 防止因不慎而消除了一个想保留的文件。命令的参数表用于给出要撤消的目录文件清单。 (3) 改变工作目录的命令 cd。不带参数的 cd 命令将使用户从任何其它目录回到自己的 注册目录上;若用全路径名作参数,cd 命令将使用户来到由该路径名确定的结点上;若用 当前目录的子目录名作参数,将把用户移到当前目录指定的下一级目录上(即用其下一级目 录作为新的当前目录);用“..”号或“*”号将使当前目录上移一级,即移到其父结点上。 (4) 改变对文件的存取方式的命令 chmod。其格式为 chmod op-code permission filename 其中,用于指明访问者的身份,可以是用户自己、用户组、所有其他用户及全部,分别用 u、 g、 o 和 a 表示;op-code 是操作码,分别用 +、- 及 = 表示增加、消除及赋予访问者以某种 权利;而 permission 则是分别用 r、 w 及 x 表示读、 写及执行许可。例如,命令 chmod go-w temp 表示消除用户组及所有其他用户对文件 temp 的写许可。 4.系统询问命令 (1) 访问当前日期和时间命令 date。例如,用命令 $ date ↙ 屏幕上将给出当前的日期和时间,如为 Wed Ang 14 09:27:20 PDT 1991 表示当前日期是 1991 年 9 月 14 日、 星期三,还有时间信息。若在命令名后给出参数,则 date 程序把参数作为重置系统时钟的时间。 (2) 询问系统当前用户的命令 who。who 命令可列出当前每一个处在系统中的用户的注 册名、终端名和注册进入时间,并按终端标志的字母顺序排序。例如,报告有下列三用户: Veronica bxo66 Aug 27 13:28 Rathomas dz24 Aug 28 07:42 Jlyates tty5 Aug 28 07:39 ·258· 计算机操作系统 用户可用 who 命令了解系统的当前负荷情况;也可在与其他用户通信之前,用此命令去核实 一下当前进入系统的用户及其所使用终端名和所用的正确的注册名。例如,用户在使用系统 的过程中,有时会发现在打入一个请求后,系统响应很慢,这时用户可用“who|we-L”命令, 使系统打印出当前的用户数目而不显示系统用户名等的完整清单,以得知当前用户数目。 (3) 显示当前目录路径名的命令 pwd。当前目录的路径名是从根结点开始,通过分支上 的所有结点到达当前目录结点为止的路径上的所有结点的名字拼起来构成的。用户的当前 目录可能经常在树上移动。如果用户忘记了自己在哪里,便可用 pwd 确定自己的位置。 7.2.2 重定向与管道命令 1.重定向命令 在 UNIX 系统中,由系统定义了三个文件。其中,有两个分别称为标准输入和标准输 出的文件,各对应于终端键盘输入和终端屏幕输出。它们是在用户注册时,由 Login 程序打 开的。这样,在用户程序执行时,隐含的标准输入是键盘输入,标准输出即屏幕(输出)显示。 但用户程序中可能不要求从键盘输入,而是从某个指定文件上读取信息供程序使用;同样, 用户可能希望把程序执行时所产生的结果数据,写到某个指定文件中而非屏幕上。这就使 用户必须去改变输入与输出文件,即不使用标准输入、标准输出,而是把另外的某个指定 文件或设备,作为输入或输出文件。 Shell 向用户提供了这种用于改变输入、 输出设备的手段,此即标准输入与标准输出的 重新定向。用重定向符“<”和“>”分别表示输入转向与输出转向。例如,对于命令 $ cat file1 ↙ 表示将文件 file1 的内容在标准输出上打印出来。若改变其输出,用命令 $ cat file1>file2 ↙ 时,表示把文件 file1 的内容打印输出到文件 file2 上。同理,对于命令 $ wc ↙ 表示对标准输入中的行中字和字符进行计数。若改变其输入,用命令 $ wc>”,即此时应再用命令 $ cat file4>>file2 ↙ 便可在文件 file2 中,除了上次复制的 file1 内容外,后面又附加了 file4 的内容。 当然,若想一次把两个文件 file1 和 file4 全部复制到 file2 中,则可用命令 $ cat file1 file4>>file2 ↙ 此外,也可在一个命令行中,同时改变输入与输出。例如,命令行 a.outfile0 ↙ 表示在可执行文件 a.out 执行时,将从文件 file1 中提取数据,而把 a.out 的执行结果数据输 第七章 操作系统接口 ·259· 出到文件 file0 中。 2.管道命令 在有了上述的重定向思想后,为了进一步增强功能,人们又进一步把这种思想加以扩 充,用符号“|”来连接两条命令,使其前一条命令的输出作为后一条命令的输入。即 $ command 1| command 2 ↙ 例如,对于下述输入 cat file|wc ↙ 将使命令 cat 把文件 file 中的数据作为 wc 命令的计数用输入。 从概念上说,系统执行上述输入时,将为管道建立一个作为通信通道的 pipe 文件。这 时,cat 命令的输出既不出现在终端(屏幕)上,也不存入某中间文件,而是由 UNIX 系统来 “缓冲”第一条命令的输出,并作为第二条命令的输入。在用管道线所连接的命令之间, 实现单向、同步运行。其单向性表现在: 只把管道线前面的命令的输出送入管道,而管道的 输出数据仅供管道线后面的命令去读取。管道的同步特性则表现为:当一条管道满时,其 前一条命令停止执行;而当管道空时,则其后一条命令停止运行。除此两种情况外,用管 道所连接的两条命令“同时”运行。可见,利用管道功能,可以流水线方式实现命令的流 水线化,即在单一命令行下,同时运行多条命令,以加速复杂任务的完成。 7.2.3 通信命令 为实现源进程与目标进程(或用户)之间的通信,一种办法是系统为每一进程(或用户)设 置一个信箱,源用户把信件投入到目标用户的信箱中去;目标用户则可在此后的任一时间, 从自己的信箱中读取信件。在这种通信方式中,源和目标用户之间进行的是非交互式通信, 因而也是非实时通信。但在有些办公自动化系统中,经常要求在两用户之间进行交互式会 话,即源与目标用户双方必须同时联机操作。在源用户发出信息后,要求目标用户能立即 收到信息并给予回答。 UNIX 系统为用户提供了实时和非实时两种通信方式,分别用 write 及 mail 命令。此外, 联机用户还可根据自己的当前情况,决定是否接受其他用户与他进行通信的要求。 1.信箱通信命令 mail mail 命令被作为在 UNIX 的各用户之间进行非交互式通信的工具。mail 采用信箱通信 方式。发信者把要发送的消息写成信件,“邮寄”到对方的信箱中。通常各用户的私有信箱 采用各自的注册名命名,即它是目录/usr/spool/mail 中的一个文件,而文件名又是用接收者 的注册名来命名的。信箱中的信件可以一直保留到被信箱所有者消除为止。因而,用 mail 进行通信时,不要求接收者利用终端与发送者会话。亦即,在发信者发送信息时,虽然接 收者已在系统中注册过,但允许他此时没有使用系统;也可以是虽在使用系统,但拒绝接 收任何信息。mail 命令在用于发信时,把接收者的注册名当作参数打入后,便可在新行开 始键入信件正文,最后仍在一个新行上用“.”来结束信件或用“^D”退出 mail 程序(也可 带选项,此处从略)。 接收者也用 mail 命令读取信件,可使用可选项 r、 q 或 p 等。其命令格式为 mail [-r][-q][-p][-file][-F persons] ·260· 计算机操作系统 由于信箱中可存放所接收的多个信件,这就存在一个选取信件的问题。上述几个选项分别 表示: 按先进先出顺序显示各信件的内容;在输入中断字符(Del 或 Return)后,退出 mail 程 序而不改变信箱的内容;一次性地显示信箱全部内容而不带询问;把指定文件当作信件来 显示。在不使用-p 选项时,表示在显示完一个信件后,便出现“?”,以询问用户是否继续 显示下一条消息,或选读完最后一条消息后退出 mail。此外,还可使用一些其它选项,以 指示对消息的各种处理方式,在此不予赘述。 2.对话通信命令 write 用这条命令可以使用户与当前在系统中的其他用户直接进行联机通信。由于 UNIX 系 统允许一个用户同时在几个终端上注册,故在用此命令前,要用 who 命令去查看目标用户 当前是否联机,或确定接收者所使用的终端名。命令格式为 write user[ttyname] 当接收者只有一个终端时,终端名可缺省。当接收者的终端被允许接收消息时,屏幕提示 会通知接收者源用户名及其所用终端名。 3.允许或拒绝接收消息命令 mesg mesg 命令的格式为: mesg[-n][-y] 选项 n 表示拒绝对方的写许可(即拒绝接收消息);选项 y 指示恢复对方的写许可,仅在此时, 双方才可联机通信。当用户正在联机编写一份资料而不愿被别人干扰时,常选用 n 选项来 拒绝对方的写许可。编辑完毕,再用带有 y 选项的 mesg 命令来恢复对方的写许可,不带自 变量的 mesg 命令只报告当前状态而不改变它。 7.2.4 后台命令 有些命令需要执行很长的时间,这样,当用户键入该命令后,便会发现自己已无事可 做,要一直等到该命令执行完毕,方可再键入下一条命令。这时用户自然会想到应该利用 这段时间去做些别的事。UNIX 系统提供了这种机制,用户可以在这种命令后面再加上“&” 号,以告诉 Shell 将该命令放在后台执行,以便用户在前台继续键入其它命令。 在后台运行的程序仍然把终端作为它的标准输出和标准错误文件,除非对它们进行重 新定向。其标准输入文件是自动地被从终端定向到一个被称为“/dev/null”的空文件中。若 shell 未重定向标准输入,则 shell 和后台进程将会同时从终端进行读入。这时,用户从终端 键入的字符可能被发送到一个进程或另一个进程,并不能预测哪个进程将得到该字符。 因此,对所有在后台运行的命令的标准输入,都必须加以重定向,从而使从终端键入的所 有字符都被送到 Shell 进程。用户可使用 ps、wait 及 Kill 命令去了解和控制后台进程的 运行。 7.3 系 统 调 用 程序接口是 OS 专门为用户程序设置的,也是用户程序取得 OS 服务的唯一途径。程序 接口通常是由各种类型的系统调用所组成的,因而,也可以说,系统调用提供了用户程序 第七章 操作系统接口 ·261· 和操作系统之间的接口,应用程序通过系统调用实现其与 OS 的通信,并可取得它的服务。 系统调用不仅可供所有的应用程序使用,而且也可供 OS 自身的其它部分,尤其是命令处理 程序使用。在每个系统中,通常都有几十条甚至上百条的系统调用,并可根据其功能而把 它们划分成若干类。例如,有用于进程控制(类)的系统调用和用于文件管理(类)、设备管理(类) 及进程通信等类的系统调用。 7.3.1 系统调用的基本概念 通常,在 OS 的核心中都设置了一组用于实现各种系统功能的子程序(过程),并将它们 提供给应用程序调用。由于这些程序或过程是 OS 系统本身程序模块中的一部分,为了保护 操作系统程序不被用户程序破坏,一般都不允许用户程序访问操作系统的程序和数据,所 以也不允许应用程序采用一般的过程调用方式来直接调用这些过程,而是向应用程序提供 了一系列的系统调用命令,让应用程序通过系统调用去调用所需的系统过程。 1.系统态和用户态 在计算机系统中,通常运行着两类程序:系统程序和应用程序,为了保证系统程序不 被应用程序有意或无意地破坏,为计算机设置了两种状态:系统态(也称为管态或核心态) 和用户态(也称为目态)。操作系统在系统态运行,而应用程序只能在用户态运行。在实际运 行过程中,处理机会在系统态和用户态间切换。相应地,现代多数操作系统将 CPU 的指令 集分为特权指令和非特权指令两类。 1) 特权指令 所谓特权指令,就是在系统态时运行的指令,是关系到系统全局的指令。其对内存空 间的访问范围基本不受限制,不仅能访问用户存储空间,也能访问系统存储空间,如启动 各种外部设备、设置系统时钟时间、关中断、清主存、修改存储器管理寄存器、执行停机 指令、转换执行状态等。特权指令只允许操作系统使用,不允许应用程序使用,否则会引 起系统混乱。 2) 非特权指令 非特权指令是在用户态时运行的指令。一般应用程序所使用的都是非特权指令,它只 能完成一般性的操作和任务,不能对系统中的硬件和软件直接进行访问,其对内存的访问 范围也局限于用户空间。这样,可以防止应用程序的运行异常对系统造成的破坏。 这种限制是由硬件实现的,如果在应用程序中使用了特权指令,就会发出权限出错信 号,操作系统捕获到这个信号后,将转入相应的错误处理程序,并将停止该应用程序的运 行,重新调度。 2.系统调用 如上所述,一方面由于系统提供了保护机制,防止应用程序直接调用操作系统的过程, 从而避免了系统的不安全性。但另一方面,应用程序又必须取得操作系统所提供的服务, 否则,应用程序几乎无法作任何有价值的事情,甚至无法运行。为此,在操作系统中提供 了系统调用,使应用程序可以通过系统调用的方法,间接调用操作系统的相关过程,取得 相应的服务。 当应用程序中需要操作系统提供服务时,如请求 I/O 资源或执行 I/O 操作,应用程序必 ·262· 计算机操作系统 须使用系统调用命令。由操作系统捕获到该命令后,便将 CPU 的状态从用户态转换到系统 态,然后执行操作系统中相应的子程序(例程),完成所需的功能。执行完成后,系统又将 CPU 状态从系统态转换到用户态,再继续执行应用程序。 可见,系统调用在本质上是应用程序请求 OS 内核完成某功能时的一种过程调用,但它 是一种特殊的过程调用,它与一般的过程调用有下述几方面的明显差别: (1) 运行在不同的系统状态。一般的过程调用,其调用程序和被调用程序都运行在相同 的状态——系统态或用户态;而系统调用与一般调用的最大区别就在于:调用程序是运行 在用户态,而被调用程序是运行在系统态。 (2) 状态的转换通过软中断进入。由于一般的过程调用并不涉及到系统状态的转换,可 直接由调用过程转向被调用过程。但在运行系统调用时,由于调用和被调用过程是工作在 不同的系统状态,因而不允许由调用过程直接转向被调用过程。通常都是通过软中断机制, 先由用户态转换为系统态,经核心分析后,才能转向相应的系统调用处理子程序。 (3) 返回问题。在采用了抢占式(剥夺)调度方式的系统中,在被调用过程执行完后,要 对系统中所有要求运行的进程做优先权分析。当调用进程仍具有最高优先级时,才返回到 调用进程继续执行;否则,将引起重新调度,以便让优先权最高的进程优先执行。此时, 将把调用进程放入就绪队列。 (4) 嵌套调用。像一般过程一样,系统调用也可以嵌套进行,即在一个被调用过程的执 行期间,还可以利用系统调用命令去调用另一个系统调用。当然,每个系统对嵌套调用的 深度都有一定的限制,例如最大深度为 6。但一般的过程对嵌套的深度则没有什么限制。图 7-3 示出了没有嵌套及有嵌套的两种系统调用情况。 用户程序 系统调 用命令 调用 系统功能 系统调 用命令 系统功能 系统调 用命令 调用 系统子功能 返回 返回 (a) 系统调用与返回 (b) 程序之间的嵌套调用 图 7-3 系统功能的调用 我们可以通过一个简单的例子来说明在用户程序中是如何使用系统调用的。例如,要 写一个简单的程序,用于从一个文件中读出数据,再将该数据拷贝到另一文件中。为此, 首先须输入该程序的输入文件名和输出文件名。文件名可用多种方式指定,一种方式是由 程序询问用户两个文件的名字。在交互式系统中,该方式要使用一系列的系统调用,先在 屏幕上打印出一系列的提示信息,然后从键盘终端读入定义两个文件名的字符串。 第七章 操作系统接口 ·263· 一旦获得两个文件名后,程序又必须利用系统调用 open 去打开输入文件,并用系统调 用 creat 去创建指定的输出文件;在执行 open 系统调用时,又可能发生错误。例如,程序试 图去打开一个不存在的文件;或者,该文件虽然存在,但并不允许被访问,等等错误。此 时, 程序又须利用一系列系统调用去显示出错信息,继而再利用一系统调用去实现程序的 异常终止。类似地,在执行系统调用 creat 时,同样可能出现错误。例如,系统中早已有了 与输出文件同名的另一文件,这时又须利用一系统调用来结束程序;或者利用一系统调用 来删除已存在的那个同名文件,然后,再利用 creat 来创建输出文件。 在打开输入文件和创建输出文件都获得成功后,还须利用申请内存的系统调用 alloc 根 据文件的大小申请一个缓冲区。成功后,再利用 read 系统调用从输入文件中把数据读到缓 冲区内,读完后,又用系统调用 close 去关闭输入文件。然后再利用 write 系统调用,把缓 冲区内的数据写到输出文件中。在读或写操作中,也都可能需要回送各种出错信息。比如, 在输入时可能发现已到达文件末尾(指定的字符数尚未读够);在读过程中可能发现硬件故障 (如奇、偶错);在写操作中可能遇见各种与输出设备类型有关的错误,比如,已无磁盘空间, 打印机缺纸等。在将整个文件拷贝完后,程序又须调用 close 去关闭输出文件,并向控制台 写出一消息以指示拷贝完毕。最后,再利用一系统调用 exit 使程序正常结束。由上所述可 见,一个用户程序将频繁地利用各种系统调用以取得 OS 所提供的多种服务。 3.中断机制 系统调用是通过中断机制实现的,并且一个操作系统的所有系统调用都通过同一个中 断入口来实现。如 MS-DOS 提供了 INT 21H,应用程序通过该中断获取操作系统的服务。 对于拥有保护机制的操作系统来说,中断机制本身也是受保护的,在 IBM PC 上,Intel 提供了多达 255 个中断号,但只有授权给应用程序保护等级的中断号,才是可以被应用程 序调用的。对于未被授权的中断号,如果应用程序进行调用,同样会引起保护异常,而导 致自己被操作系统停止。如 Linux 仅仅给应用程序授权了 4 个中断号:3、4、5 以及 80h, 前三个中断号是提供给应用程序调试所使用的,而 80h 正是系统调用(system call)的中断号。 7.3.2 系统调用的类型 通常,一个 OS 所具有的许多功能,可以从其所提供的系统调用上表现出来。显然,由 于各 OS 的性质不同,在不同的 OS 中所提供的系统调用之间也会有一定的差异。对于一般 通用的 OS 而言,可将其所提供的系统调用分为:进程控制、文件操纵、通信管理和系统维 护等几大类。 1.进程控制类系统调用 这类系统调用主要用于对进程的控制,如创建一个新的进程和终止一个进程的运行, 获得和设置进程属性等。 1) 创建和终止进程的系统调用 在多道程序环境下,为使多道程序能并发执行,必须先利用创建进程的系统调用来为 欲参加并发执行的各程序分别创建一个进程。当进程已经执行结束时、 或因发生异常情况 而不能继续执行时,可利用终止进程的系统调用来结束该进程的运行。 ·264· 计算机操作系统 2) 获得和设置进程属性的系统调用 当我们创建了一个(些)新进程后,为了能控制它(们)的运行,应当能了解、 确定和重新 设置它(们)的属性。这些属性包括: 进程标识符、进程优先级、最大允许执行时间等。此时, 我们可利用获得进程属性的系统调用,来了解某进程的属性,利用设置进程属性的系统调 用,来确定和重新设置进程的属性。 3) 等待某事件出现的系统调用 进程在运行过程中,有时需要等待某事件(条件)出现后方可继续执行。例如,一进程在 创建了一个(些)新进程后,需要等待它(们)运行结束后,才能继续执行,此时可利用等待子 进程结束的系统调用进行等待;又如,在客户/服务器模式中,若无任何客户向服务器发出 消息,则服务器接收进程便无事可做,此时该进程就可利用等待(事件)的系统调用,使自己 处于等待状态,一旦有客户发来消息时,接收进程便被唤醒,进行消息接收的处理。 2.文件操纵类系统调用 对文件进行操纵的系统调用数量较多,有创建文件、删除文件、打开文件、关闭文件、 读文件、写文件、建立目录、移动文件的读/写指针、改变文件的属性等。 1) 创建和删除文件 当用户需要在系统中存放程序或数据时,可利用创建文件的系统调用 creat,由系统根 据用户提供的文件名和存取方式来创建一个新文件;当用户已不再需要某文件时,可利用 删除文件的系统调用 unlink 将指名文件删除。 2) 打开和关闭文件 用户在第一次访问某个文件之前,应先利用打开文件的系统调用 open,将指名文件打 开,即系统将在用户(程序)与该文件之间建立一条快捷通路。在文件被打开后,系统将给用 户返回一个该文件的句柄或描述符;当用户不再访问某文件时,又可利用关闭文件的系统 调用 close,将此文件关闭,即断开该用户程序与该文件之间的快捷通路。 3) 读和写文件 用户可利用读系统调用 read,从已打开的文件中读出给定数目的字符,并送至指定的 缓冲区中;同样,用户也可利用写系统调用 write,从指定的缓冲区中将给定数目的字符写 入指定文件中。read 和 write 两个系统调用是文件操纵类系统调用中使用最频繁的。 3.进程通信类系统调用 在 OS 中经常采用两种进程通信方式,即消息传递方式和共享存储区方式。当系统中采 用消息传递方式时,在通信前,必须先打开一个连接。为此,应由源进程发出一条打开连 接的系统调用 open connection,而目标进程则应利用接受连接的系统调用 accept connection 表示同意进行通信;然后,在源和目标进程之间便可开始通信。可以利用发送消息的系统 调用 send message 或者用接收消息的系统调用 receive message 来交换信息。通信结束后, 还须再利用关闭连接的系统调用 close connection 结束通信。 用户在利用共享存储区进行通信之前,须先利用建立共享存储区的系统调用来建立一 个共享存储区,再利用建立连接的系统调用将该共享存储区连接到进程自身的虚地址空间 上,然后便可利用读和写共享存储区的系统调用实现相互通信。 除上述的三类外,常用的系统调用还包括设备管理类系统调用和信息维护类系统调用, 第七章 操作系统接口 ·265· 前者主要用于实现申请设备、释放设备、设备 I/O 和重定向、获得和设置设备属性、逻辑 上连接和释放设备等功能,后者主要用来获得包括有关系统和文件的时间、日期信息、操 作系统版本、当前用户以及有关空闲内存和磁盘空间大小等多方面的信息。 7.3.3 POSIX 标准 目前许多操作系统都提供了上面所介绍的各种类型的系统调用,实现的功能也相类似, 但在实现的细节和形式方面却相差很大,这种差异给实现应用程序与操作系统平台的无关 性带来了很大的困难。为解决这一问题,国际标准化组织 ISO 给出的有关系统调用的国际 标准 POSIX1003.1(Portable Operating System IX),也称为“基于 UNIX 的可移植操作系统 接口”。 POSIX 定义了标准应用程序接口(API),用于保证编制的应用程序可以在源代码一级上 在多种操作系统上移植运行。只有符合这一标准的应用程序,才有可能完全兼容多种操作 系统,即在多种操作系统下都能够运行。 POSIX 标准定义了一组过程,这组过程是构造系统调用所必须的。通过调用这些过程 所提供的服务,确定了一系列系统调用的功能。一般而言,在POSIX标准中,大多数的系 统调用是一个系统调用直接映射一个过程,但也有一个系统调用对应若干个过程的情形, 如一个系统调用所需要的过程是其它系统调用的组合或变形时, 则往往会对应多个过程。 需要明确的是,POSIX 标准所定义的一组过程虽然指定了系统调用的功能,但并没有 明确规定系统调用是以什么形式实现的,是库函数还是其它形式。如早期操作系统的系统 调用使用汇编语言编写,这时的系统调用可看成是扩展的机器指令,因而,能在汇编语言 编程中直接使用。而在一些高级语言或 C 语言中,尤其是最新推出的一些操作系统,如 UNIX 新版本、Linux、Windows 和 OS/2 等,其系统调用干脆用 C 语言编写,并以库函数形式提 供,所以在用 C 语言编制的应用程序中,可直接通过使用对应的库函数来使用系统调用, 库函数的目的是隐藏访管指令的细节,使系统调用更像过程调用。但一般地说,库函数属 于用户程序而非系统调用程序。如图 7-4 示出了 UNIX/Linux 的系统程序、库函数、系统调 用的层次关系。 用户接口 用户 标准系统程序(实用程序) 系统程序:汇编、编译、编辑、Shell 标准库函数 标准函数:打开、关闭、读、写、创建、撤销 库函数接口 系统调用接口 操作系统 系统调用:进程管理、存储管理、文件管理、设备管理 图 7-4 Unix/Linux 系统程序、库函数、系统调用的分层关系 ·266· 计算机操作系统 7.3.4 系统调用的实现 系统调用的实现与一般过程调用的实现相比,两者间有很大差异。对于系统调用,控 制是由原来的用户态转换为系统态,这是借助于中断和陷入机制来完成的,在该机制中包 括中断和陷入硬件机构及中断与陷入处理程序两部分。当应用程序使用 OS 的系统调用时, 产生一条相应的指令,CPU 在执行这条指令时发生中断,并将有关信号送给中断和陷入硬 件机构,该机构收到信号后,启动相关的中断与陷入处理程序进行处理,实现该系统调用 所需要的功能。 1.中断和陷入硬件机构 1) 中断和陷入的概念 中断是指 CPU 对系统发生某事件时的这样一种响应: CPU 暂停正在执行的程序,在 保留现场后自动地转去执行该事件的中断处理程序;执行完后,再返回到原程序的断点 处继续执行。图 7-5 表示中断时 CPU 的活动轨迹。还可进一步把中断分为外中断和内中 断。所谓外中断,是指由于外部设备事件所引起的中断,如通常的磁盘中断、打印机中 断等;而内中断则是指由于 CPU 内部事件所引起的中断,如程序出错(非法指令、地址 越界)、电源故障等。内中断(trap)也被译为“捕获”或“陷入”。通常,陷入是由于执行 了现行指令所引起的;而中断则是由于系统中某事件引起的,该事件与现行指令无关。 由于系统调用引起的中断属于内中断,因此把由于系统调用引起中断的指令称为陷入 指令。 被中断程序 中断点 中断处理开始 中断处理 程序 中断处理结束 图 7-5 中断时的 CPU 轨迹 2) 中断和陷入向量 为了处理上的方便,通常都是针对不同的设备编制不同的中断处理程序,并把该程序 的入口地址放在某特定的内存单元中。此外,不同的设备也对应着不同的处理机状态字 PSW,且把它放在与中断处理程序入口指针相邻接的特定单元中。在进行中断处理时,只 要有了这样两个字,便可转入相应设备的中断处理程序,重新装配处理机的状态字和优先 级,进行对该设备的处理。因此,我们把这两个字称为中断向量。相应地,把存放这两个 字的单元称为中断向量单元。类似地,对于陷入,也有陷入向量,不同的系统调用对应不 同的陷入向量,在进行陷入处理时,根据陷入指令中的陷入向量,转入实现相应的系统调 用功能的子程序,即陷入处理程序。由所有的中断向量和陷入向量构成了中断和陷入向量 表,如图 7-6 所示。 第七章 操作系统接口 ·267· 中断向量单元 外设种类 优先级 中断处理程序入口地址 060 电传输出 4 064 电传输入 4 070 纸带机输入 4 074 纸带机输出 4 … … … klrint klxint perint pcpint … 陷入向量单元 (a) 中断向量 陷入种类 优先级 陷入处理程序入口地址 004 总线超时 7 trap 064 非法指令 7 trap 070 电源故障 7 trap 074 trap 指令 7 trap … … … … (b) 陷入向量 图 7-6 中断向量与陷入向量 2.系统调用号和参数的设置 往往在一个系统中设置了许多条系统调用,并赋予每条系统调用一个唯一的系统调用 号。在系统调用命令(陷入指令)中把相应的系统调用号传递给中断和陷入机制的方法有很多 种,在有的系统中,直接把系统调用号放在系统调用命令(陷入指令)中;如 IBM 370 和早 期的 UNIX 系统,是把系统调用命令的低 8 位用于存放系统调用号;在另一些系统中,则 将系统调用号装入某指定寄存器或内存单元中,如 MS-DOS 是将系统调用号放在 AH 寄存 器中,Linux 则是利用 EAX 寄存器来存放应用程序传递的系统调用号。 每一条系统调用都含有若干个参数,在执行系统调用时,如何设置系统调用所需的参 数,即如何将这些参数传递给陷入处理机构和系统内部的子程序(过程),常用的实现方式有 以下几种: (1) 陷入指令自带方式。陷入指令除了携带一个系统调用号外,还要自带几个参数进入 系统内部,由于一条陷入指令的长度是有限的,因此自带的只能是少量的、有限的参数。 (2) 直接将参数送入相应的寄存器中。MS-DOS 便是采用的这种方式,即用 MOV 指令 将各个参数送入相应的寄存器中。系统程序和应用程序显然应是都可以访问这种寄存器的。 这种方式的主要问题是由于这种寄存器数量有限,因而限制了所设置参数的数目。 (3) 参数表方式。将系统调用所需的参数放入一张参数表中,再将指向该参数表的指针 放在某个指定的寄存器中。当前大多数的 OS 中,如 UNIX 系统和 Linux 系统,便是采用了 这种方式。该方式又可进一步分成直接和间接两种方式,如图 7-7 所示。在直接参数方式中, 所有的参数值和参数的个数 N,都放入一张参数表中;而在间接参数方式中,则在参数表中 仅存放参数个数和指向真正参数数据表的指针。 ·268· 计算机操作系统 变元表: N 参数1 参数2 M 参数n 变元表: N 指针 trap ×× 参数1 参数2 M 参数n (a) 直接方式 (b) 间接方式 图 7-7 系统调用的参数形式 3.系统调用的处理步骤 在设置了系统调用号和参数后,便可执行一条系统调用命令。不同的系统可采用不同的 执行方式。在 UNIX 系统中,是执行 CHMK 命令;而在 MS-DOS 中则是执行 INT 21 软中断。 系统调用的处理过程可分成以下三步:首先,将处理机状态由用户态转为系统态;之 后,由硬件和内核程序进行系统调用的一般性处理,即首先保护被中断进程的 CPU 环境, 将处理机状态字 PSW、程序计数器 PC、系统调用号、用户栈指针以及通用寄存器内容等, 压入堆栈;然后,将用户定义的参数传送到指定的地址保存起来。 其次,是分析系统调用类型,转入相应的系统调用处理子程序。为使不同的系统调用 能方便地转向相应的系统调用处理子程序,在系统中配置了一张系统调用入口表。表中的 每个表目都对应一条系统调用,其中包含该系统调用自带参数的数目、系统调用处理子程 序的入口地址等。因此,核心可利用系统调用号去查找该表,即可找到相应处理子程序的 入口地址而转去执行它。 最后,在系统调用处理子程序执行完后,应恢复被中断的或设置新进程的 CPU 现场, 然后返回被中断进程或新进程,继续往下执行。 4.系统调用处理子程序的处理过程 系统调用的功能主要是由系统调用子程序来完成的。对于不同的系统调用,其处理程 序将执行不同的功能。我们以一条在文件操纵中常用的 Creat 命令为例来说明之。 进入 Creat 的处理子程序后,核心将根据用户给定的文件路径名 Path,利用目录检索过 程去查找指定文件的目录项。查找目录的方式可以用顺序查找法,也可用 Hash 查找法。如 果在文件目录中找到了指定文件的目录项,表示用户要利用一个已有文件来建立一个新文 件。但如果在该已有(存)文件的属性中有不允许写属性,或者创建者不具有对该文件进行修 改的权限,便认为是出错而做出错处理;若不存在访问权限问题,便将已存文件的数据盘 块释放掉,准备写入新的数据文件。如未找到指名文件,则表示要创建一个新文件,核心 便从其目录文件中找出一个空目录项,并初始化该目录项,包括填写文件名、文件属性、 文件建立日期等,然后将新建文件打开。 7.4 UNIX 系统调用 在上一节中,我们对系统调用做了一般性的描述。为使读者能对系统调用有较具体的 了解,在本节中将对 UNIX 系统中的系统调用作扼要的阐述。 第七章 操作系统接口 ·269· 7.4.1 UNIX 系统调用的类型 在 UNIX 系统最早的版本中,提供了 56 条系统调用;后来,随着版本的不断翻新,其 所提供的系统调用也不断增加,其数量已增至数百条,其中较常用的系统调用大约有 30 多 条。根据其功能的不同,我们同样可将它们分为进程控制、文件操纵、进程间通信和信息 维护等几大类。 1.进程控制 该类系统调用包括:创建进程的系统调用 fork、终止进程的系统调用 exit、等待子进程 结束的系统调用 wait 等十多条。 (1) 创建进程(fork)。一个进程可以利用 fork 系统调用来创建一个新进程。新进程是作 为调用者的子进程,它继承了其父进程的环境、 已打开的所有文件、根目录和当前目录等, 即它继承了父进程几乎所有的属性,并具有与其父进程基本上相同的进程映像。 (2) 终止进程(exit)。一个进程可以利用 exit 实现自我终止。通常,在父进程创建子进程 时,便在子进程的末尾安排一条 exit 系统调用。这样,子进程在完成规定的任务后,便可 进行自我终止。子进程终止后,留下一记账信息 status,其中包含了子进程运行时记录下来 的各种统计信息。 (3) 等待子进程结束(wait)。wait 用于将调用者进程自身挂起,直至它的某一子进程终 止为止。这样,父进程可以利用 wait 使自身的执行与子进程的终止同步。 (4) 执行一个文件(exec)。 exec 可使调用者进程的进程映像(包括用户程序和数据等)被 一个可执行的文件覆盖,此即改变调用者进程的进程映像。该系统调用是 UNIX 系统中最 复杂的系统调用之一。 (5) 获得进程 ID。UNIX 系统提供了一组用于获得进程标识符的系统调用,比如,可利 用 getp-id 系统调用来获得调用进程的标识符,利用 getpgrp 系统调用来获得调用进程的进程 组 ID,以及利用 getppid 系统调用来获得调用进程的父进程 ID 等。 (6) 获得用户 ID。UNIX 系统提供了一组用于获得用户 ID 的系统调用,如 getuid 可用 于获得真正的用户 ID,geteuid 用于获得有效用户 ID,getgid 用于获得真正用户组 ID 等。 (7) 进程暂停(pause)。可用此系统调用将调用进程挂起,直至它收到一个信号为止。 2.文件操纵 用于对文件进行操纵的系统调用是数量最多的一类系统调用,其中包括创建文件、打 开文件、关闭文件、读文件及写文件等二十多条。 (1) 创建文件(creat)。系统调用 creat 的功能是根据用户提供的文件名和许可权方式,来 创建一个新文件或重写一个已存文件。如果系统中不存在指名文件,核心便以给定的文件 名和许可权方式来创建一个新文件;如果系统中已有同名文件,核心便释放其已有的数据 块。创建后的文件随即被打开,并返回其文件描述符 fd。若 creat 执行失败,便返回“-1”。 (2) 打开文件(open)。设置系统调用 open 的目的,是为了方便用户及简化系统的处理。 open 的功能是把有关的文件属性从磁盘拷贝到内存中,以及在用户和指名文件之间建立一 条快捷的通路,并给用户返回一个文件描述符 fd。文件被打开后,用户对文件的任何操作 都只须使用 fd 而非路径名。 ·270· 计算机操作系统 (3) 关闭文件(close)。当把一个文件用毕且暂不访问时,可调用 close 将文件关闭,即断 开用户程序与该文件之间已经建立的快捷通路。在 UNIX 系统中,由于允许一个文件被多个 进程所共享,故只有在无其他任何进程需要此文件时,或者说,在对其索引结点中的访问计 数 i-count 执行减 1 操作后其值为 0,表示已无进程再访问该文件时,才能真正关闭该文件。 (4) 读和写文件 read 和 write。仅当用户利用 open 打开指定文件后,方可调用 read 或 write 对文件执行读或写操作。两个系统调用都要求用户提供三个输入参数: ① 文件描述符 fd。② buf 缓冲区首址。对读而言,这是用户所要求的信息传送的目标地址;对写而言,这 则是信息传送的源地址。③ 用户要求传送的字节数 n byte。 系统调用 read 的功能是试图从 fd 所指示的文件中去读入 n byte 个字节的数据,并将它 们送至由指针 buf 所指示的缓冲区中;系统调用 write 的功能是试图把 n byte 个字节数据, 从指针 buf 所指示的缓冲区中写到由 fd 所指向的文件中。 (5) 连接和去连接(link 和 unlink)。为了实现文件共享,必须记住所有共享该文件的用 户数目。为此,在该文件的索引结点中设置了一个连接计数 i.link。每当有一用户要共享某 文件时,须利用系统调用 link 来建立该用户(进程)与此文件之间的连接,并对 i.link 做加 1 操作。当用户不再使用此文件时,应利用系统调用 unlink 去断开此连接,亦即做 i.link 的减 1 操作。当 i.link 减 1 后结果为 0 时,表示已无用户需要此文件,此时才能将该文件从文件 系统中删除。故在 UNIX 系统中并无一条删除文件的系统调用。 3.进程间的通信 为了实现进程间的通信,在 UNIX 系统中提供了一个用于进程间通信的软件包,简称 IPC。它由消息机制、共享存储器机制和信号量机制三部分组成。在每一种通信机制中,都 提供了相应的系统调用供用户程序进行进程间的同步与通信之用。 (1) 消息机制。用户(进程)在利用消息机制进行通信时,必须先利用 msgget 系统调用来 建立一个消息队列。若成功,便返回消息队列描述符 msgid,以后用户便可利用 msgid 去访 问该消息队列。用户(进程)可利用发送消息的系统调用 msgsend 向用户指定的消息队列发送 消息;利用 msgrcv 系统调用从指定的消息队列中接收指定类型的消息。 (2) 共享存储器机制。当用户(进程)要利用共享存储器机制进行通信时,必须先利用 shmget 系统调用来建立一个共享存储区,若成功,便返回该共享存储区描述符 shmid。以后, 用户便可利用 shmid 去访问该共享存储区。进程在建立了共享存储区之后,还必须再利用 shmat 将该共享存储区连接到本进程的虚地址空间上。以后,在进程之间便可利用该共享存 储区进行通信。当进程不再需要该共享存储区时,可利用 shmdt 系统调用来拆除进程与共 享存储区间的连接。 (3) 信号量机制。在 UNIX 系统中所采用的信号量机制,与第二章中所介绍的一般信号 量集机制相似,允许将一组信号量形成一个信号量集,并对这组信号量施以原子操作,详 见第十章。 4.信息维护 在 UNIX 系统中,设置了许多条用于系统维护的系统调用。 (1) 设置和获得时间。超级用户可利用设置时间的系统调用(stime),来设置系统的日期 和时间。如果调用进程并非超级用户,则 stime 失败。一般用户可利用获得时间的系统调用 第七章 操作系统接口 ·271· time 来获得当前的日期和时间。 (2) 获得进程和子进程时间(times)。利用该系统调用可获得进程及其子进程所使用的 CPU 时间,其中包括调用进程在用户空间执行指令所花费的时间,系统为调用进程所花费 的 CPU 时间、子进程在用户空间所用的 CPU 时间、系统为各子进程所花费的 CPU 时间等, 并可将这些时间填写到一个指定的缓冲区。 (3) 设置文件访问和修改时间(utime)。该系统调用用于设置指名文件被访问和修改的时 间。如果该系统调用的参数 times 为 NULL 时,文件主和对该文件具有写权限的用户,可将 对该文件的访问和修改时间设置为当前时间;如果 times 不为 NULL,则把 times 解释为指 向 utim buf 结构的指针,此时,文件主和超级用户能将访问时间和修改时间置入 utim buf 结构中。 (4) 获得当前 UNIX 系统的名称(uname)。利用该系统调用可将有关 UNIX 系统的信息 存储在 utsname 结构中。这些信息包括 UNIX 系统名称的字符串、系统在网络中的名称、 硬 件的标准名称等。 7.4.2 被中断进程的环境保护 在 UNIX 系统Ⅴ的内核程序中,有一个 trap.S 文件,它是中断和陷入总控程序。该程序 用于中断和陷入的一般性处理。为提高运行效率,该文件采用汇编语言编写。由于在 trap.S 中包含了绝大部分的中断和陷入向量的入口地址,因此,每当系统发生了中断和陷入情况 时,通常都是先进入 trap.S 程序。 1.CPU 环境保护 当用户程序处在用户态,且在执行系统调用命令(即 CHMK 命令)之前,应在用户空间 提供系统调用所需的参数表,并将该参数表的地址送入 R0 寄存器。在执行 CHMK 命令后, 处理机将由用户态转为核心态,并由硬件自动地将处理机状态长字(PSL)、程序计数器(PC) 和代码操作数(code)压入用户核心栈,继而从中断和陷入向量表中取出 trap.S 的入口地址, 然后便转入中断和陷入总控程序 trap.S 中执行。 trap.S 程序执行后,继续将陷入类型 type 和用户栈指针 usp 压入用户核心栈,接着还要 将被中断进程的 CPU 环境中的一系列寄存器如 R0~R11 的部分或全部内容压入栈中。至于 哪些寄存器的内容要压入栈中,这取决于特定寄存器中的屏蔽码,该屏蔽码的每一位都与 R0~R11 中的一个寄存器相对应。当某一位置成 1 时,表示对应寄存器的内容应压入栈中。 2.AP 和 FP 指针 为了实现系统调用的嵌套使用,在系统中还设置了两个指针,其一是系统调用参数表 指针 AP,用于指示正在执行的系统调用所需参数表的地址,通常是把该地址放在某个寄存 器中,例如放在 R12 中;再者,还须设置一个调用栈帧指针。所谓调用栈帧(或简称栈帧), 是指每个系统调用需要保存而被压入用户核心栈的所有数据项;而栈帧指针 FP,则是用于 指示本次系统调用所保存的数据项。每当出现新的系统调用时,还须将 AP 和 FP 压入栈中。 图 7-8 示出了在 trap.S 总控程序执行后,用户核心栈的情况。 当 trap.S 完成被中断进程的 CPU 环境和 AP 及 FP 指针的保存后,将会调用由 C 语言书 写的公共处理程序 trap.C,以继续处理本次的系统调用所要完成的公共处理部分。 ·272· 计算机操作系统 由中断和 陷入总控 程序压入 陷入时由 硬件压入 … AP FP R0 Rn usp type code PC PSL 用户核心栈 图 7-8 用户核心栈 7.4.3 系统调用陷入后需处理的公共问题 trap.C 程序是一个处理各种陷入情况的 C 语言文件,共有 12 种陷入的处理要调用 trap.C 程序(如因系统调用、进程调度中断、跟踪自陷非法指令、访问违章、算术自陷等)用于处理在 中断和陷入发生后需要处理的若干公共问题。如果因为系统调用而进入 trap.C 时,它所要进 行的处理将包括:确定系统调用号、实现参数传送、转入相应的系统调用处理子程序。在由 系统调用处理子程序返回到 trap.C 后,重新计算进程的优先级,对收到的信号进行处理等。 1.确定系统调用号 由上所述得知,在中断和陷入发生后,是先经硬件陷入机构予以处理,再进入 trap.S, 然后再调用 trap.C 继续处理。其调用形式为: trap(usp,type,code,PC,PSL) 其中,参数 PSL 为陷入时处理机状态字长,PC 为程序计数器,code 为代码操作数,type 为陷入类型号,usp 为用户栈指针。对陷入的处理可分为多种情况,如果陷入是由于系统调 用所引起的,则对此陷入的第一步处理,便是确定系统调用号。通常,系统调用号是包含 在代码操作数中,故可利用 code 来确定系统调用号 i。其方法是令 i=code & 0377 若 0