《操作系统之哲学原理》读书笔记

/ 2评 / 1

最近读了《操作系统之哲学原理》一书,在此做些简单的记录,方便日后回顾。

简介


简评

作者从哲学原理的角度讲述了操作系统是如何设计的,对操作系统和人类社会两个层面进行了分析与比较。借用书中的话,也就是“以使读者更加清楚地明白操作系统就是人类社会在计算机里面的反映,明白了人类社会的运转就明白了操作系统的运转。”

这本书的核心在于操作系统是由人设计出来的,而人类社会本身就充满了各种权衡,并不完美。因此我们在设计操作系统的时候也没法做到完美,只能在兼顾稳定、功能和效率的基础上最大化效益。明白了这一点就可以让我们站在一个更高的角度思考操作系统的各种设计。

当然,“本书只是从哲学原理上对操作系统予以阐述,对具体操作系统的实现细节基本忽略不计。而要完全透彻地理解操作系统,这些具体实现细节则不可或缺。事实上,只有亲手设计过商业操作系统,或者亲手阅读分析过商业操作系统源代码的人,对操作系统的掌握才可能真正到位。”,所以想要对操作系统想要有一个更加深入的了解(底层原理的实现),光靠这一本书是远远不够的,仍需要“广泛涉猎”。

对我来说,印象比较深刻的是作者的论述方式:否定,否定之否定

  1. 如果想要实现某个功能,我们自然而然就会想到某种方法
  2. 在通过这种方法实现功能后,对它的优缺点进行分析
  3. 为了克服其缺点,我们需要对其进行有针对性地改进或者设计一种新的方法
  4. 分析新方法的优缺点,并与原来的方法进行对比
  5. 不断改进或设计新的方法,并进行对比分析......
  6. 结合这些方法的优势,扬长避短,最终采取一个“尽可能优”的方法

你不能批评这其中出现的某些方法是没用的,只能说它与别的方法相比可能是不够好的。

此外,还有“操作系统提供的是一个抽象”。抽象来源于具体,又超越具体。


笔记

结构笔记

概念笔记

导论

操作系统的设计师指的是一种抽象,一种所有设计师所共有的人生哲学,因为设计师在设计操作系统时会不自觉地将自己的思维或人生追求构造在操作系统里,从而赋予操作系统以心智,而操作系统也就在这种心智的指挥下运行着。

CPU管理(进程与线程)、内存管理(虚拟存储)、外存管理(文件系统)、I/O管理(输入与输出)等操作系统的核心机制不外乎是对资源的管理,它们都遵循着一切人类资源管理的基本原则,即如何有效地发掘资源、监控资源、分配资源和回收资源

如果我们掌握了资源的根本属性,即资源管理必然涉及共享和竞争的管理,理解了操作系统必须首先保障自己的正常运转,就会理解操作系统的一切行为。前者指引着操作系统功能的设计与进化,后者则推动着操作系统可靠性的演变

基础原理

操作系统在计算机运行过程中扮演的角色:魔术师和管理者。魔术师将丑陋变得美好,将没有变为有,将少变为多;而管理者则对所有计算机资源进行管理以达到公平和效率的“双料”境界。

人造学科的四个特点:不精确、具有相对性(“没有对错”)。从对人类活动的观察导出。依赖于人的主观判断力。通常符合人的直觉。

自然存在或神造学科具有相反的四个属性:精确、绝对。从对自然存在的观察导出。不依赖于人的主观判断力。通常违反人的直觉。

操作系统是介于计算机和应用软件之间的一个软件系统,从这个定义出发,我们知道操作系统的上面和下面都有别的对象存在:下面是硬件平台,上面是应用软件。

操作系统的演变:状态机操作系统、单一操作员单一控制端操作系统、批处理操作系统、多道批处理操作系统、分时操作系统、实时操作系统、现代操作系统

魔术家的目标是把差的东西变好,把少的东西变多,把复杂变简单。同样,操作系统将计算机以一个更加容易、更加方便、更加强大的方式呈现给用户。

操作系统管理计算机上软硬件资源。例如,操作系统对CPU、内存、磁盘等的管理,使得不同用户之间或者同一用户的不同程序之间可以安全有序地共享这些硬件资源。

那怎么让用户很好地利用这些硬件资源呢?就是分块(parcel out),把硬件分块给应用程序使用。这里关键的原则是有效和公平,这是管理者的必备素质。有效指的是不能浪费资源,公平指的是每个人都有可能享有资源,即不能有不公平的现象。

当然真正的公平是没有的事,这很像人类生活的现实。不过追求公平却是我们的本能,在虚拟世界里尽可能公平一点还是非常应该的,至少应该是设计操作系统时的不懈追求:)

CPU 管理就是将要介绍的进程管理。进程管理的主要目的有 3 个:第一个是公平,即每个程序都有机会使用CPU。第二个是非阻塞,即任何程序不能无休止地阻挠其他程序的正常推进。如果一个程序在运行过程中需要输入输出或者因别的什么事情而发生阻塞,这个阻塞不能妨碍别的进程继续前进。就像人类世界,缺了谁地球照样旋转。第三个是优先级。在人类生活中人的地位不完全一样,地位高的就比地位低的优先级高。

内存管理主要是管理缓存、主存、磁盘、磁带等存储介质所形成的内存架构(虚拟内存)。

外存管理通常也称为存储管理,它就是众所周知的文件系统了。文件系统的主要目的是将磁盘变成一个很容易使用的存储媒介以提供给用户使用。

I/O 管理也称为设备管理,就是管理输入输出设备。I/O管理的目的有两个:一是屏蔽不同设备的差异性,即用户用同样的方式访问不同的设备,从而降低编程的难度;二是提供并发访问,即将那些看上去并不具备共享特性的设备(如打印机)变得可以共享。

操作系统的演变就是我们对计算机硬件进行粉饰的过程,其不断发展与改善由两个因素驱动:硬件成本不断下降计算机的功能和复杂性不断变化

从概念上讲,计算机的结构非常简单:首先布置一根总线,然后将各种硬件设备挂在总线上。所有的这些设备都有一个控制设备,外部设备都由这些控制器与CPU通信。而所有设备之间的通信均需通过总线。

所谓的抽象,就是在根本上存在但现实中不存在的东西(exist in essence but not in reality)。例如,人是具体的动物。但如果将人的具体属性,如肉体和骨架全部剥离,剩下的就是抽象,即人的灵魂。

微内核结构,即只将操作系统核心中的核心放在内核态运行,其他功能都移到用户态运行。这样就同时提高了效率和安全性。

进程原理篇

因为要并发,所以我们发明了进程。

当人们面临困境时通常的做法就是:发明新的概念、新的术语或新的机制来解脱困境。

什么是进程呢?顾名思义,进程就是进展中的程序,或者说进程是执行中的程序。就是说,一个程序加载到内存后就变为进程。即:进程=程序+执行

从物理内存的分配来看,每个进程占用一片内存空间,从这点上说,进程就是内存的某片空间。由于在任意时刻,CPU只能执行一条指令,因此任意时刻在CPU上执行的进程只有一个,而到底执行哪条指令由物理程序计数器指定。

对于操作系统来说,进程是其提供的一种抽象,目的是通过并发来提高系统利用率,同时还能缩短系统响应时间。

造成进程消亡的事件则可以分为四种情况:寿终:进程运行完成而退出。自杀:进程因错误而自行退出。他杀:进程被其他进程所终止。处决:进程因异常而被强行终结。

与一个社会管理人的过程类似,操作系统要管理进程就要维护关于进程的一些信息。当一个进程产生时,操作系统也需要为其创建记录。操作系统用于维护进程记录的结构就是进程表或进程控制块(Process Control Block,PCB)。

一般来说,创建进程的步骤如下所示:1)分配进程控制块。2)初始化机器寄存器。3)初始化页表。4)将程序代码从磁盘读进内存。5)将处理器状态设置为“用户态”。6)跳转到程序的起始地址(设置程序计数器)。

调度是操作系统实现进程模型的根本手段

CPU 调度就是要达到极小化平均响应时间、极大化系统吞吐率、保持系统各个功能部件均处于繁忙状态和提供某种貌似公平的机制。

调度算法:先来先服务算法、时间片轮转算法、短任务优先算法、优先级调度算法、混合调度算法、实时调度算法(必须提供时序可预测性,EDF、RMS)

进程之间的交互称为进程间通信(Inter-Process Communication,IPC)

从根本上说,管道是一个线性字节数组,类似文件,可以使用文件读写的方式进行访问。

如果要在两个不相关的进程(如两个不同进程里面的进程)之间进行管道通信,则需要使用记名管道

管道和记名管道虽然具有简单、无需特殊设计(指应用程序方面)就可以和另外一个进程进行通信的优点,但其缺点也很明显。首先是管道和记名管道并不是所有操作系统都支持。主要支持管道通信方式的是UNIX和类UNIX(如Linux)的操作系统。其次,管道通信需要在相关的进程间进行(无名管道),或者需要知道按名字来打开(记名管道),而这在某些时候会十分不便。

套接字的功能非常强大,可以支持不同层面、不同应用、跨网络的通信。使用套接字进行通信需要双方均创建一个套接字,其中一方作为服务器方,另外一方作为客户方。

一种不同的机制来处理如下通信需求:想迫使一方对我们的通信立即做出回应。我们不想事先建立任何连接,而是临时突然觉得需要与某个进程通信。传输的信息量微小,使用管道或套接字不划算。应付上述需求,我们使用的是信号(signal)

在计算机里,信号量实际上就是一个简单整数。一个进程在信号变为 0 或者 1 的情况下推进,并且将信号变为 1 或 0 来防止别的进程推进。当进程完成任务后,则将信号再改变为 0 或 1,从而允许其他进程执行。需要注意的是,信号量不只是一种通信机制,更是一种同步机制

管道、套接字、信号、信号量,虽然满足了多种通信需要,但还是有一种需要未能满足。这就是两个进程需要共享大量数据。可以使用共享内存解决这一问题,但其缺点是管理复杂,且两个进程必须在同一台物理机器上才能使用这种通信方式。共享内存的另外一个缺点是安全性脆弱(病毒传染)。

消息队列是一列具有头和尾的消息排列。新来的消息放在队列尾部,而读取消息则从队列头部开始。其看上去像管道,但它不是管道。首先,它无需固定的读写进程,任何进程都可以读写(当然是有权限的进程)。其次,它可以同时支持多个进程,多个进程可以读写消息队列。即所谓的多对多,而不是管道的点对点。另外,消息队列只在内存中实现。最后,它并不是只在UNIX和类UNIX操作系统中实现。几乎所有主流操作系统都支持消息队列。

线程原理篇

虽然可以使用诸多进程来相互协作实现需要并发才能完成的功能,但进程间的协作有着重要的限制:每个进程有自己的独立空间。这种限制导致进程之间的协作存在明显缺陷:如果相互协作的进程需要动态共享大量的数据,则操作起来十分麻烦

虽然进程和进程出现的动机都是并发,但它们的并发层次不同:进程属于处理器级并发,即在处理器这一层次上提供并发的抽象;线程则属于进程级并发,即在进程这个层次上再提供一层并发的抽象。如果我们下到底层,进入计算机体系结构里就会发现,流水线提供的也是一种并发,不过是指令级并发

将进程分解为线程还可以有效地利用多处理器和多核计算机。在没有线程的情况下,增加一个处理器并不能提高一个进程的执行速度。但如果分解为多个线程,则可以让不同的线程同时运转在不同的处理器上,从而提高了进程的执行速度。

到底哪些资源是(同一进程的)不同线程所共享,哪些是不共享的呢?一般的评判标准是:如果某资源不独享会导致线程运行错误,则该资源就由每个线程独享;而其他资源都由进程里面的所有线程共享。

鉴于用户态和内核态的线程模型都存在缺陷,因此现代操作系统将二者结合起来使用。用户态的执行系统负责进程内部线程在非阻塞时的切换;内核态的操作系统负责阻塞线程的切换

如果在程序运行过程中发生中断或异常,系统将自动切换到内核态来运行中断或异常处理机制。

虽然线程的优势很明显,但带来的问题也是显而易见的,那就是系统运行的不确定性。虽然在多进程时,系统运行也存在一定的不确定性,但这种不确定性基本体现在程序执行的先后顺序上,而每个程序运行的结果基本是确定的。而线程的引入却带来了程序本身运行结果的不确定性:由于多线程的存在,就每个单一线程来看,其执行效率、执行正确性均存在不确定性。当然,通过使用同步机制,可以改善这种不确定性。但如果在多线程执行过程中出现异常,则情况就相当麻烦。

线程之间的关系是合作关系。既然是合作,那就得有某种约定的规则,否则合作就会出问题。如何在保持线程这个概念的同时,消除其执行结果的不确定性呢?答案是线程的同步

一般情况下,在任何计算机体系结构中,x = 1 对应的不是一条微指令,即一条高级指令对应的是多条微指令,因此一条高级指令需要多个时钟周期(通常为6个时钟周期以上)才能完成

线程同步的目的就是不管线程之间的执行如何穿插,其运行结果都是正确的。

两个或多个线程争相执行同一段代码或访问同一资源的现象称为竞争(race)。这个可能造成竞争的共享代码段或资源称为临界区(critical section)

两个线程不可能真的在同一时刻执行(单核情况),但有可能在同一个时刻两个线程都在同一段代码上。

协调的目的就是在任何时刻都只能有一个人在临界区里,这称为互斥(mutual exclusion)。

互斥就是说一次只有一个人使用共享资源,其他人皆排除在外,并且互斥不能违反前面给出的线程模型。因此,正确互斥需要满足4个条件:不能有两个线程同时在临界区里面。线程能够在任何数量和速度的 CPU 上正确执行。在互斥区域外不能阻止另一个线程的运行。线程不能无限制地等待进入临界区。

锁有两个基本操作:闭锁和开锁。闭锁就是将锁锁上,其他人进不来;开锁就是你做的事情做完了,将锁打开,别的人可以进去了。

什么是睡觉与叫醒呢?就是如果对方持有锁,你就不需要等待锁变为打开状态,而是去睡觉,锁打开后对方再来把你叫醒。我们可以用生产者与消费者的问题来演示这个机制。

信号量(semphore)可以说是所有原语里面功能最强大的。它不仅是一个同步原语,还是一个通信原语。而且,它还能作为锁来使用。

简单来说,信号量就是一个计数器,其取值为当前累积的信号数量。它支持两个操作:加法操作 up 和减法操作 down。

为什么我们需要 3 个信号量呢?一个二元信号量用来互斥,一个信号量用来记录缓冲区里商品的数量不就可以了吗?缓冲区里空格的数量不是可以由缓冲区大小和缓冲区里商品的数量计算得出吗?为什么需要一个 full 和一个 empty 来记录满的和空的呢?这是因为生产者和消费者等待的信号不同,它们需要在不同的信号上睡觉。

锁解决了同步问题,但带来的是循环等待,我们不满意。为了消除循环等待,我们发明了睡觉与叫醒。但睡觉与叫醒又带来了死锁,因此,我们发明了信号量

如果有一组线程,每个线程都在等待一个事件的发生,而这个事件只能由该组线程里面的另一线程发出,则称这组线程发生了死锁

死锁的发生必须满足 4 个条件:资源有限持有等待(一个线程在请求新的资源时,其已经获得的资源并不释放,而是继续持有)、不能抢占循环等待

从高级境界来看,死锁的应有只有两种策略,允许死锁发生:不予理睬死锁检测与修复,不让死锁发生:死锁的动态避免、死锁的静态防止(破坏死锁发生的 4 个必要条件中的任何一个)。

实际上,闭锁操作在编程微指令后,根本不止两个步骤,而是十几个甚至更多步骤。事实上,所有同步原语在微指令级都是由多个步骤构成。那操作系统是如何保证这些同步原语的原子性呢?即在执行同步原语的中途是怎么防止别的进程或线程插入执行呢?

自然,如果没有硬件提供原子操作,只在软件层上是不可能设计出原子操作的。操作系统之所以能够构建锁之类的同步原语,原因就是硬件已经为我们提供了一些原子操作:中断禁止和启用(inter-rupt enable/disable)内存加载和存入(load/store)测试与设置(test&set)指令。

要切换进程,必须发生上下文切换,而发生上下文切换只能有两种可能:一是一个线程自愿放弃CPU而将控制权交给操作系统调度器;二是一个线程被强制放弃CPU而失去控制权。自愿放弃通过调用yield 之类的操作系统系统调用来实现;而强制放弃则需通过中断来实现,操作系统主要是通过周期性的时钟中断来获得CPU控制权的

内存原理篇

内存管理机制负责对内存架构进行管理,使程序在内存架构的任何一个层次上的存放对于用户来说都是一样的。用户无须担心自己的程序是存储在缓存、主存、磁盘还是磁带上,反正运行、计算、输出的结果都一样。而内存管理实现这种媒介透明的手段就是虚拟内存

内存管理要达到如下两个目标:地址保护:一个程序不能访问另一个程序地址空间。地址独立:程序发出的地址应与物理主存地址无关。

虚拟内存的中心思想是将物理主存扩大到便宜、大容量的磁盘上,即将磁盘空间看做主存空间的一部分

最简单的内存管理是单道程序下的内存管理。在单道编程环境下,整个内存里面只有两个程序:一个是用户程序,另一个是操作系统。操作系统所占用的内存空间是恒定的,我们可以将用户程序总是加载到同一个内存地址上。在这种管理方式下,在运行前即将物理地址计算好的方式叫做静态地址翻译

多道编程的情况下,无法将程序总是加到固定的内存地址上,也就是无法使用静态地址翻译。这样我们就必须在程序加载完毕后才能计算物理地址,也就是在程序运行时进行地址翻译,这种翻译称为动态地址翻译,物理地址=虚拟地址+程序所在区域的起始地址。

一个程序的空间增长通常有两个来源:数据和栈。如何处理这两种空间增长的关系会对整个程序的扩展性产生影响。

最简单的方式是数据和栈往一个方向增长,这样的优点是两者独立性高,缺点是空间利用率可能较低。例如,如果数据在下,栈在上,当栈长到程序所分配空间的顶时,就无法再长了。即使下面的数据部分还有闲置空间也不能利用。

一个更简单的办法是让数据和栈往相反方向增长。这样,只要本程序的自由空间还有多余,不仅可以进行函数调用,又可以增加新的数据,可以最大限度地利用这片自由空间。

当一个程序所占空间不够时,我们将其倒到磁盘上,再加载到一片更大的内存空间。这种将程序倒到磁盘上,再加载进内存的管理方式称为交换(swap)

为了解决交换系统存在的缺陷(外部碎片),出现了分页系统。分页系统的核心就是将虚拟内存空间和物理内存空间皆划分为大小相同的页面,如4KB、8KB或16KB等,并以页面作为内存空间的最小分配单位,一个程序的一个页面可以存放在任意一个物理页面里。

由于页表的特殊地位决定了它由硬件直接提供支持,即页表是一个硬件数据结构

多级页表为什么占用的内存空间少呢?因为大部分次级页表会放到磁盘上,而放在内存里面的页表较少。因此,内存占用少。多级页表有什么缺点呢?它降低了系统的速度。因此每次内存访问都变成多次内存访问

因为程序的运行呈现所谓的时空局域性,即在一段时间内,程序所要访问的地址空间有一定的空间局域性。如果一个页面被访问,则该页面的其他地址可能也将被随后访问。这样,我们可以将该页面的翻译结果存放在缓存里,而无须在访问该页面的每个地址时再翻译一次

这种存放翻译结果的缓存称为翻译快表(Translation Look-Aside Buffer,TLB)。TLB里面存放的是从虚拟页面到物理页面的映射,其记录的格式与内容和正常页表的记录格式与内容一样。

一个哲学原理:软件没有办法构建原子操作,只能把硬件请出来。在这里,我们看到,软件没有办法在以一次内存访问完成 TLB 查找,到这里卡住了。解决方案就是使用硬件。我们在 TLB 里面进行的比较不是一个个地顺序比较,而是同时比较,即将所有的 TLB 记录与目的虚拟地址同时比较,因此只需要一次查找就能确定一个虚拟页面号是否在 TLB 里。这种设计需要同时配备多套比较电路,比较电路的套数需与TLB的大小一样。这也就是为什么 TLB 非常昂贵。

分页系统的一个考虑因素就是页面应该设计为多大?如果太大,可能造成浪费。因为一个程序的最后一个页面很有可能是不满的。最好的情况是一个程序的大小正好是页面大小的整数倍;最坏的情况则是页面的整数倍多 1 条指令,多出来的这条指令就要占用一个页面,造成一个页面的绝大部分空间浪费。在平均情况下,最后一个页面有半个页面被浪费。这种浪费称为内部碎片

在最坏情况下,每次新的访问都是对一个不在内存的页面进行访问,即每次内存访问都产生一次缺页中断,这样每次内存访问皆变成一次磁盘访问,而由于磁盘访问速度比内存可以慢一百万倍,因此整个系统的效率急剧下降。这种现象就称为内存抖动

公平是人类的普遍追求,而有区别对待在某种程度上也是人类的内心期望。因此,一方面我们追求公平;另一方面又希望自己获得特殊待遇。当我们认为自己做出了比别人更多的贡献时,自然期望得到优待。对内存页面而言,也是一样的道理。

公平算法主要包括下面 4 种:随机算法、先来先出(FIFO)算法、第二次机会算法、时钟算法,非公平算法则包括如下 5 种:最优算法、NRU算法、LRU算法、工作集,算法还有一种混合算法,它既想保持公平,又含有区别对待的考虑:工作集时钟算法。

否定之否定作为辩证法的三大规律之一,时时刻刻影响着我们。体现在人生上,就是一个人总是在不断地自我否定中前进。这种推进看似在原地转圈,不停地回到同一个点,实际上是在螺旋式上升,每一次回到起点时都不是简单的重复,而是上升到了更高的层次和境界。

佛家所谓的“看山是山,看水是水”,到“看山不是山,看水不是水”,再到“看山还是山,看水还是水”的演变模式正是否定之否定的一个具体体现。入道之初,看世界的眼光原始而朴素,所以“看山是山,看水是水”;修行到一定境界时,隐隐约约发现了一些隐藏于真相背后的东西,从而体验到“看山不是山,看水不是水”;等到一切都顿彻顿悟之后,又忽然发现“山还是山,水还是水”。这种认识看似回到了原点,其实已经经过了两次否定,与先前的朴素认识“山还是山,水还是水”已经不可同日而语了。

开始研究某个事物的时候,可能会觉得简单,以为没什么可研究的;研究一些内容后忽然发现,原来认为简单的内容其实很复杂;等到完全搞清楚以后,才发现其实真的很简单。对操作系统的研究就是如此,初学操作系统时只看到操作系统是管理计算机硬件的软件系统,十分简单;研究一段时间后,又觉得操作系统从某种程度上说是人生的缩影。有人从操作系统看到了人生,有人看到了哲学,似乎每个人发现的都是冰山一角,此时觉得操作系统博大精深,充满奥秘;而当穷尽一生之力去研究操作系统,深入理解了操作系统后,很可能发现原来操作系统就是计算机软硬件资源的管理者,只不过这种看法已经是站在更高的层次上罢了。

页表太大?这个缺点用多级页表克服了。多级页表速度慢?这个问题用 TLB 解决了绝大部分。页面来回更换?这个缺点用页面更换算法解决了大部分。固定页面大小呢?这不应该算是一个缺点,因为可变页面大小的操作系统不仅难以选择最优的页面大小,而且会变得很复杂。内部碎片算是一个小小的缺憾,但总比交换系统的外部碎片强,一个进程的内部碎片所浪费的空间平均起来只有半个页面,相对于分页系统的诸多优点来说,这个缺点似乎微不足道。

那么分页系统还有其他缺陷吗?有。其中的一个是共享困难。虽然在理论上可以按页进行共享,似乎粒度很细,但实际上这根本就是不现实的。原因是一个页面的内容很可能既包括代码又包括数据,即很难使一个页面只包含需要共享的内容或不需要共享的内容。只要一个页面里面有一行地址是不能共享的,这个页面就不能共享。

有一个缺点却是无法容忍的,同时也是分页系统无法解决的。这个缺点就是一个进程只能占有一个虚拟地址空间。在此种限制下,一个程序的大小至多只能和虚拟空间一样大,其所有内容都必须从这个共同的虚拟空间内分配。

分段管理就是将一个程序按照逻辑单元分成多个程序段,每一个段使用自己单独的虚地址空间。

一般来说,一个程序的所有逻辑段是同时需要的。如果实行分段管理,则由于每个段必须全部加载到内存,这就造成了我们在基本内存管理时已经遇到过的一个问题:一个程序必须同时全部加载到内存才能执行。当然,解决这个问题可以使用重叠(overlay)

段页式管理就是将程序分为多个逻辑段,在每个段里面又进行分页,即将分段和分页组合起来使用。这样做的目的就是想同时获得分段和分页的好处,但又避免了单独分段或单独分页的缺陷。如果我们将每个段看做一个单独的程序,则逻辑分段就相当于同时加载多个程序。

文件原理篇

简单地说,文件系统将其接触的磁盘物理特性转换为用户看到的路径名和文件名。用户对磁盘进行访问只需要给出文件名和路径名即可,而无须知道磁柱、磁道、扇面、数据块等信息。

与内存管理系统类似,文件系统也需要达到两个目的:地址独立和地址保护。

文件夹也称为目录夹(folder),它保存的不是用户数据,而是关于文件及文件系统的信息。简单地说,文件夹的角色就是跟踪文件,里面存放的是从文件到文件在磁盘的地址的映射,即“文件名→文件在磁盘上的地址”。文件夹对于文件来说,就相当于动态地址翻译对于虚拟地址的作用,即从虚拟地址到实际地址的一种翻译机制。

使用相对路径的好处是没有必要寻遍整个目录夹,可以节省磁盘访问次数,从而提高文件访问效率。(一般情况)

在计算机启动时,处于主板 ROM 里面的 BIOS 程序首先运行。BIOS 在进行一些基本的系统配置扫描后对磁盘的扇面 0 进行读操作,将 MBR 里面的程序读到内存并运行。MBR 程序接下来找到系统主分区,并将主分区里面的 Boot Record 加载并运行。Boot Record 里面的内容是一个小程序,该程序将负责找到操作系统映像,并加载到内存,从而启动操作系统。所有的文件系统都必须按照这种格式存放,操作系统才能正常启动。

存放文件数据块指针的表称为文件分配表(File Allocation Table,FAT),FAT32:)。

既然单级索引不能适用于大文件,而多级索引又不适用于小文件,那么能想到的一种办法自然是单级和多级的有机组合:非对称多级索引。在非对称多级索引组织下,索引既可以是单级,也可以是多级。到底是单级还是多级取决于文件的大小。所有的文件都有一个顶级索引节点(I-NODE)。

单级也好,多级也好,文件的读取相对于内存访问来说就是慢。对于慢我们有什么解决办法吗?答案就是使用缓存

将文件里面经常要访问的内容存放在缓存里,使得文件的访问可以在缓存满足,而无须到磁盘上去读写。这种缓冲称为文件缓存。

有了链接后,文件删除的操作需要进行修改。删除文件时不是马上将文件删除(不管是逻辑上还是物理上),而是将该文件对应 I-NODE 里面的链接计数减 1。如果结果为 0,就删除该文件(逻辑删除);如果不为 0,则不删除该文件。

链接方式将文件的地址映射直接加到链接目录下,这种链接也称为硬链接。这是因为另外一条路径断开后(如删除文件或者路径上的文件夹),其他的链接不会受到影响。

软链接从严格意义上说不是链接,因为它并没有直接连到文件上,而是保存了文件的原始访问路径而已。正因为此,Windows的快捷方式不是链接。因为快捷方式保存的就是路径名。

虚拟内存(VM)和文件缓存(FC)的根本区别是什么?VM 的根本目的是提供一个速度非常高、容量非常大的并不存在的内存空间。它从物理内存出发,为了增加内存空间而扩展到磁盘上;而 FC 是为了提高文件的访问效率而出现。它从磁盘出发,为了提高访问效率而将文件置于缓存。就是说 VM 和FC 的根本目的不同,即有着本质区别。因此,其工作机制、语义和操作界面皆不相同。

从原理上看,海量文件系统与普通文件系统并无太大区别,它所应该达到的目标仍然是磁盘抽象,提供地址独立与保护,但在性能上的挑战非常明显:数据量大、分布环境广。挑战是因为数据量的增长突破了某一阈值,而这一阈值的突破导致传统文件系统的失效。这就是哲学上量变质变的规律在文件系统中的体现。

I/O 原理篇

显然,输入输出的存在才使得计算机的存在有了意义。就像一个人,如果没有输入输出,即她或他不能与外部世界打交道,则这个人通常被认为是痴呆或白痴。即使这个人实际上是一个天才情况也是如此。

直接内存访问(即Direct Memory Access,DMA)的工作原理是,如果按数据块进行I/O,即需要传输大量数据时,就无须CPU的介入。在这种情况下,我们可以让I/O设备与计算机内存进行直接数据交换。而CPU则可以去忙别的事情。

DMA 模式的优点就是将主 CPU 从 I/O 中解脱出来,而缺点自然是增加了成本和复杂性。

设备驱动程序通常由设备制造商提供,但归属于操作系统内核。正因为这一属性,设备驱动程序是操作系统安全的一个巨大隐患。

多核原理篇

在 x86 体系结构下,多处理功能芯片经过了对称多处理器结构(SMP Architecture)、超线程结构(Hyper Threading)、多核结构(Multi-coreArchitecture)和多核超线程结构(Multi-core Hy-per Threading Architecture)的4个演变阶段。

超线程技术是在一个 CPU 上同时执行多个程序而共享这个 CPU 内的资源,理论上要像两个 CPU 一样在同一时间执行两个线程,超线程技术可在同一时间里让应用程序使用芯片的不同部分。

多处理器、超线程和多核的共同点是均为了提升计算机性能而设计,均可以同时执行多个指令序列。但是区别也很明显,主要体现在同时执行的两个线程之间共享物理资源的多少。多处理器的共享物理资源最少,每个线程有自己单独的处理器;超线程共享最多,ALU、FPU、MSR、缓存等均为共享物理资源;而多核则介于两者之间,共享处理器,但不共享ALU、FPU等。

操作系统设计篇

首先,操作系统作为计算机的管理者,需要对计算机的各个组成部分进行管理,这就导致了CPU管理、内存管理、磁盘管理、I/O设备管理等操作系统功能的出现和相互关联。其次,操作系统作为魔术师,需要对计算机的各种硬件进行抽象和装扮,以使其显得更大、更快、更好、更容易使用。而这些抽象就形成了进程线程、虚拟内存、文件系统、各种I/O模式等操作系统构造的出现。而这些构造之间也因操作系统魔术师的角色而互相联系。

操作系统的设计追求与人类自身的追求相同,有如下几个目标:保证操作系统本身运行正确提供尽可能多的功能尽量提高系统的效率、在追求效率的基础上尽量顾及公平

操作系统本身并无对错之分,只有好坏之分。就像我们不能说Windows是对的,UNIX是错的。我们只能说,Windows更容易使用,而UNIX不太容易使用而已。而对于另外一些人来说则恰恰相反。


推荐


  1. CongTsang说道:

    否定之否定。形而上学,不行退学 😛 在学习操作系统的过程中自己悟到了

CongTsang进行回复 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注