读《莱昂氏UNIX源代码分析》

在unix早期的代码中,schedule和swap两个核心任务都是由0号进程来负责的,这个朴实的设计就是unix系统最最原始的设计,因为unix在开始设计的时候十分清楚进程应该做什么不应该做什么,应该做它本职的工作,而诸如调度和置换之类的任务不应该由用户进程负责,但是linux后来颠覆了这个想法,毕竟频繁的切换带来的开销已经基本抵消了分工设计带来的优雅,于是就将调度工作分担给了各个进程本身,而置换工作仍旧由 内核进程来完成但是却不是0号进程,而0号进程最终退化成了一个idle进程。

我们从原始的unix 6的代码中可以看到schedule的实现,该实现中一共进行了两次切换而不是像linux中的那样仅仅一次,起初切换到0号进程,实际上就是切换了一下堆栈,核心堆栈切换到了0号进程就可以说当前的进程是0号进程了,代码执行流依旧往下走,切换堆栈并没有修改pc寄存器的值,因此进行了第一次切换以后的代码就成了0号进程执行的了,0号进程接下来做什么呢?其实很简单,就是在所有被换进内存的进程中找一个最值得运行的,这就涉及到了具体的调度策略。待0号进程选择好了下一个进程之后,最后进行第二次切换,也就是切换到这个被选中的进程,切换的过程是简单的,就是核心栈的切换,然后就是切换页表,实际上那个时候的页表被叫做区表,系统还没有实现完全的虚拟内存管理,PDP-11机器使用寄存器来实现类似X86页表的功能,在切换的过程中,将负责“虚拟页面”和物理页面映射的寄存器修改一下就可以了,因为一个时刻只有一个进程是运行态,所以进程的核心栈所在的位置是一个确定的位置,unix的这个设计非常好,系统变得简单,只需要将这个确定位置的“虚拟内存”地址指向新进程的物理内存地址就可以了,物理内存由malloc分配,注意和标准c库的malloc的区别和联系,区别是功能不同,联系是实现思想相同。这里不深谈其它的地址映射,实际上内核空间的代码是共享的,而用户空间的不同,PDP-11机器分别用两组寄存器来进行内核空间和用户空间的地址映射,由此可见内核空间的映射是相同的,并且和后来的linux一样,也用了一一的线性映射。这里可以看出代码和数据的区别,虽然在调度函数里面执行的是一个函数的代码,但是却是不同的3个进程在执行,所以正是数据将进程区别了开来,而不是代码,数据又被分为堆栈等等,因此切换了堆栈就是切换了进程,当然这里的堆栈是核心堆栈。这个设计被后来的windows所采用,一直到现在的Windows NT都直接体现了古老的unix的这一思想,将核心堆栈等等一些核心的数据结构放置于一个固定的内存地址,而linux却不是这样,这得益于linux中task_struct这一精巧结构的实现,在windows中和unix一样也是将进程的控制块分为了类似U区和proc结构,并且unix自从设计之初,优先级的概念就很重要,后来windows的IRQL也是借鉴了这一思想,说说看,linux是类unix系统吗?除了API一样之外几乎完全颠覆了unix的一切,倒不如说windows的某些设计和unix很相似,当然这里不谈微内核和大内核。

通观unix 6代码的fork和newproc函数的实现,简直叫一个妙啊,很简单,很明了,在父进程中直接返回子进程的pid,而子进程直接挂入进程队列,待到系统进行调度的时候也就是switch的时候,子进程会返回switch的返回值,也就是0,switch的返回值简直就是一石三鸟啊!unix在当时其实还没有完全实现虚拟存储,仅仅是很朴素的虚拟内存的概念,因此并没有后来复杂的请求调页机制,而是整个进程的换入换出,负责这件事的就是0号进程最后的那个无限大循环。在linux的请求调页实现中,专门有一个内核线程负责页面的换入和换出,当然必须处理的就是缺页异常,linux靠缺页处理以及kswapd实现了请求调页。

总之,如果你想深入理解操作系统原理,那么务必看一下《莱昂氏UNIX源代码分析》这一本书,这本书的分量可以任何讲操作系统的书都要重,从中你可以得到操作系统最精髓的部分,比现如今动则五六百页的书要简单得多,简单就是美。只求理解精髓!10000行左右的代码竟然诠释了如此一个庞然大物,并且作为后面几乎所有操作系统的蓝本一直发展壮大着,直到现在。

目 录 献辞 致谢 序(一) 序(二) 历史注记 上篇 UNIX操作系统版本6源代码 UNIX操作系统过程分类索引 3 UNIX操作系统文件及过程 5 UNIX操作系统定义的符列表 7 UNIX操作系统源代码交叉引用列表 9 第一部分 初始化、进程初始化 25 第二部分 陷入、中断、系统调用和 进程管理 75 第三部分 程序交换、基本输入/输出、 块设备 109 第四部分 文件和目录、文件系统、管道 133 第五部分 面向字符的特殊文件 181 下篇 莱昂UNIX源代码分析 前言 207 第1章 绪论 209 1.1 UNIX操作系统 209 1.2 公用程序 209 1.3 其他文档 210 1.4 UNIX程序员手册 210 1.5 UNIX文档 211 1.6 UNIX操作系统源代码 211 1.7 源代码中各部分 212 1.8 源代码文件 212 1.9 分析的使用 212 1.10 对程序设计水平的一条注释 212 第2章 基础知识 214 2.1 处理机 214 2.2 处理机状态字 214 2.3 通用寄存器 214 2.4 指令集 215 2.5 寻址方式 216 2.5.1 寄存器方式 217 2.5.2 寄存器延迟方式 217 2.5.3 自动增1方式 217 2.5.4 自动减1方式 217 2.5.5 变址方式 217 2.5.6 立即方式 218 2.5.7 相对方式 218 2.6 UNIX汇编程序 219 2.7 存储管理 219 2.8 段寄存器 220 2.9 页说明寄存器 220 2.10 存储分配 220 2.11 状态寄存器 221 2.12 “i”和“d”空间 221 2.13 启动条件 221 2.14 专用设备寄存器 221 第3章 阅“C”程序 222 3.1 某些选出的例子 222 3.2 例1 222 3.3 例2 223 3.4 例3 223 3.5 例4 225 3.6 例5 225 3.7 例6 227 3.8 例7 227 3.9 例8 228 3.10 例9 228 3.11 例10 229 3.12 例11 229 3.13 例12 230 3.14 例13 230 3.15 例14 231 3.16 例15 231 3.17 例16 232 3.18 例17 233 第4章 概述 235 4.1 变量分配 235 4.2 全局变量 235 4.3 “C”预处理程序 235 4.4 第一部分 236 4.4.1 第1组“.h”文件 236 4.4.2 汇编语言文件 237 4.4.3 在第一部分中的其他文件 237 4.5 第二部分 237 4.6 第三部分 238 4.7 第四部分 238 4.8 第五部分 239 第一部分 初始化、进程初始化 第5章 两个文件 241 5.1 文件malloc.c 241 5.1.1 列表维护规则 241 5.1.2 malloc(2528) 242 5.1.3 mfree(2556) 243 5.1.4 结论 244 5.2 文件prf.c 244 5.2.1 printf(2340) 244 5.2.2 printn(2369) 245 5.2.3 putchar(2386) 246 5.2.4 panic(2419) 247 5.2.5 prdev(2433)、deverror(2447) 247 5.3 包含的文件 247 第6章 系统初启 249 6.1 操作员的动作 249 6.2 start(0612) 249 6.3 main(1550) 251 6.4 进程 252 6.5 proc〔0〕的初始化 252 6.6 sched(1940) 253 6.7 sleep(2066) 253 6.8 swtch(2178) 253 6.9 再回到main 254 第7章 进程 256 7.1 进程映像 256 7.2 proc结构(0358) 257 7.3 user结构(0413) 257 7.4 每个进程数据区 258 7.5 段 258 7.6 映像的执行 258 7.7 核心态执行 259 7.8 用户态执行
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值