了解操作系统的那些事儿,从这篇文章开始
不懂计算机系统也能写程序,这是不争的事实。但是我们「学习操作系统并非是为了去创造一个操作系统,而是为了理解操作系统之后写出更好的程序」。
全文脉络思维导图如下:
1. 什么是操作系统
了解什么是操作系统,操作系统是干什么用的,我们还需要从操作系统的需求起源开始说起。
1946 年,「世界上第一台通用计算机埃尼阿克(ENIAC)「在美国宾夕法尼亚大学诞生,用了 18000 个电子管,占地 170 平方米,重达 30 吨。最恐怖的是,ENIAC 或者说」早期的计算机是没有操作系统的」,运转这个 170 平方米的庞然大物全靠工程师们的手动操作。大概是种什么感觉呢?请看下图:
图中遍布整个房间的大机器就是 ENIAC,从照片中我们可以看到一名操作人员正在手动调整一个按钮,而像这样的按钮,整个房间内有成千上万个!可想而知,没有操作系统的时代,工程师们都需要直接面对这种反人类的硬件面板进行操作,运转并维护一个计算机付出的人力成本简直无法想象。
由此,引出了操作系统最主要的功能之一:「对计算机硬件资源进行管理、分配和调度。」
有了操作系统之后,某个程序需要占据多少内存,具体分配哪个内存空间;只有一个 CPU,如何同时运行多个程序等,用户都无需关心,更不需要去学习那些晦涩难懂的底层专业知识比如数字电路逻辑、计算机组成原理等,这一切都交给操作系统就好!
也就是说,「操作系统将所有的底层硬件封装成一个黑盒子」,用户直接向操作系统发送指令,即可完成与底层硬件的交互。
那么,操作系统到底是何方神圣?我们先来看看「早期的操作系统 Disk Operating System,DOS」(磁盘操作系统) 长什么样:
有同学就说这不就是我们使用的 cmd 命令行,没有错,但是,它是整个屏幕都是这样的界面,全屏都是!而不是某一个对话框里面是这样。
显然,DOS 这种通过键盘输入命令的操作方式已经比我们手动调整按钮、连接线路要简单得多。但是,这样的命令行界面(CLI)必定无法符合大部分用户的口味,于是后来拥有良好图形界面(GUI)的操作系统比如 Windows 逐渐成为主流,毕竟只需通过鼠标点击就能进行操作,门槛已经足够的低。
由此可见,一个操作系统的操作方式和界面的美观程度对于用户来说是非常重要的。
这就引出了操作系统的第二个主要功能:「为用户提供一个友好、清晰且简单的操作界面」(专业的叫法是 「壳 Shell」),用户通过 Shell 执行该操作系统提供的所有的功能。对于命令行来说,就需要提供足够多的命令;对于图形界面来说,就需要提供足够多数量的按钮。
⭐ 总结一下:操作系统本质上是运行在计算机上的软件/程序,作为硬件基础上的第一层软件,操作系统是硬件和各种软件沟通的桥梁。其功能大致可分为两个部分:
「管理计算机硬件与软件资源」 「向用户提供一个与系统交互的操作界面」
2. 计算机硬件简单介绍
为了能够工作,操作系统必须了解大量的硬件,至少需要了解硬件如何面对程序员。出于这个原因,这里我们先简单介绍现代个人计算机中的硬件,然后再讨论操作系统中的细节。
一台简单的个人计算机可以抽象为类似于下图中的模型:
CPU、内存以及 I/O 设备都由一条系统总线连接起来并通过总线与其他设备通信。
处理器
所谓处理器也就是我们常说的 CPU,它是计算机的「大脑」。CPU 从内存中提取指令并执行它。一个 CPU 的执行周期是从内存中提取一条指令、解码并决定它的类型和操作数,接着执行之,然后再提取、解码并执行下一条指令。重复该循环直到程序运行完毕。
简单来说,大家记住这句话就行:「一个程序需要放入内存并给它分配 CPU 才能执行」。
存储器
计算机中第二个主要的组件就是存储器。「理想情况下」,存储器应该「非常快速」,比执行一条指令要快,从而使得 CPU 的执行效率不会收到存储器的影响,而且「足够大且非常便宜」。
但是目前的技术手段无法同时满足这三个目标,于是出现了不同的处理方式。存储器系统采用一种分层次的结构,如下图所示,「顶层的存储器速度最快、容量最小、成本最高,越往下层存储器的速度越慢、容量越大、成本也越低」。
为什么访问「寄存器」的速度这么快呢?那是因为寄存器的材料和 CPU 是相同的,所以和访问 CPU 比起来几乎是没有时延的。
寄存器的下一个层次是「缓存」,想必大家并不陌生,通常缓存的使用会带来性能上的改善。操作系统一直都在使用缓存,比如说在内存中保留频繁使用的文件,以避免从磁盘上重复地调取这些文件。
再往下一层就是「内存」,也称「主存」,通常被称为「随机访问存储器」(Random Access Memory,「RAM」)。所有不能在缓存中得到满足的访问请求都会转往内存。
除了内存 RAM 之外,许多计算机还具有少量的「非易失性随机存取存储器」(Read Only Memory,「ROM」)。它们与 RAM 不同,在电源断电后,ROM 并不会丢失内容,其中的内容一旦存储后就不会再被修改。而且 ROM 非常快而且便宜。
下一个层次是「磁盘」,其容量更大,磁盘唯一的问题就是随机访问数据的时间比内存大约慢了三个数量级,其低速的原因是因为「磁盘是一种机械装置并且拥有一种特殊的构造」,如下图所示:
在一个磁盘中有一个或多个金属「盘片」,它们以 5400、7200、10800 rpm 或更高的速度旋转。从边缘开始有一个「机械臂」悬横在盘面上,这种情形大伙可以回想一下老式的播放塑料唱片机。信息会写在磁盘一系列的同心圆上。在任意一个给定臂的位置,每个「磁头」可以读取一段环形区域,称为「磁道」(track)。把一个给定臂的位置上的所有磁道合并起来,就组成了一个「柱面」(cylinder)。
每个磁道划分若干「扇区」,扇区的值是 512 字节。在现代磁盘中,较外部的柱面比较内部的柱面有更多的扇区。机械臂从一个柱面移动到相邻的柱面大约需要 1ms。而随机移到一个柱面的典型时间为 5ms 至 10ms,其具体时间取决于驱动器。磁臂到达正确的磁道上后,驱动器必须等待所需的扇区旋转到磁头之下(这大概需要 5ms 至 10ms 的时延),再开始读写,低端硬盘的速率是 50MB/s
,而高速磁盘的速率是 160MB/s
。
I/O 设备
CPU 和存储器并不是操作系统唯一需要管理的资源,I/O 设备与操作系统同样密不可分。如下图所示,I/O 设备一般包括两个部分:设备控制器和设备本身,比如键盘控制器和键盘。
「设备控制器」其实就是一块芯片或者一组芯片,它能够接收操作系统的指令并控制物理设备。例如,从设备中读取数据并完成数据的处理。
在许多情况下,实际控制设备的过程是非常复杂而且存在诸多细节。因此控制器的工作就是为操作系统提供一个更简单(但仍然非常复杂)的接口。
I/O 设备另一部分是「设备本身」,设备本身有一个相对简单的接口,这是因为接口既不能做很多工作,而且也已经被「标准化」了,标准化后任何一个磁盘控制器就可以适配任意一种磁盘,所以标准化是非常必要的。
3. 操作系统的四个特征
操作系统拥有 4 个鲜明的特征:并发、共享、虚拟和异步。其中,「并发和共享是操作系统的最基本特征」,没有并发和共享,就谈不上虚拟和异步。
下面我们来简单的了解一下这 4 个特征,其中涉及的很多详细概念不会在本文做出解释,后续会陆续开更。
① 并发
并发和并行这两个孪生兄弟,经常会让初学者摸不着头脑。
「并发」:并发是指宏观上在「一段时间内」能同时运行多个程序。当然,这些程序宏观上是同时发生的,但微观上是交替发生的。操作系统通过引入进程和线程,使得程序能够并发运行。
「并行」:并行则指「同一时刻」能运行多个指令,指两个或多个事件在「同一时刻同时发生」。并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统。
配合下图形象的理解下:
说到并发与并行,不得不提 CPU,作为计算机的大脑, CPU 主要和内存进行交互,从内存中提取指令并执行。而一个程序需要放入内存并给它分配 CPU 才能执行,所以并发并行的能力与 CPU 的性能息息相关:
「单核 CPU」同一时刻只能执行一个程序,各个程序只能「并发」地执行 ; 「多核 CPU」同一时刻可以同时执行多个程序,多个程序可以「并行」地执行。
比如 Intel 的第八代 i3 处理器就是 4 核 CPU,意味着可以并行地执行 4 个程序。当然,即使是对于 4 核 CPU 来说,只要有 4 个以上的程序需要 “同时” 运行,那么并发性依然是必不可少的,因此「并发性是操作系统一个最基本的特征」。
② 共享
共享即「资源共享」,是指系统中的资源可供内存中多个「并发」执行的进程共同使用。
主要有两种共享方式:
1)「互斥共享」
所谓互斥共享,就是说虽然这个资源是共享的,所有进程都能够使用,但是「同一个资源在某一时刻只允许一个进程访问」,也称为互斥访问,需要用同步机制来实现互斥访问。互斥共享/访问的资源称为「临界资源」。
举个互斥共享的例子:
如果我们同时使用 QQ 和微信视频,同一时间段内摄像头资源只能分配给其中的一个进程。
2)「同时共享」
同时共享与互斥共享相反,「允许一个时间段内多个进程 “同时” 对系统中的某些资源进行访问」。当然,所谓的 “同时” 往往是宏观上的,而在微观上,这些进程可能是交替地对该资源进行访问(即分时共享)
举个同时共享的例子:
使用 QQ 发送硬盘上的文件 A,同时使用微信发送硬盘上的文件 B。宏观上看,两边在同时读取并发送文件, 都在访问硬盘资源,并从中读取数据。微观上看,QQ 和微信这两个进程是交替访问硬盘资源的。
「并发和共享作为操作系统的两大最基本特征,其实是互为存在条件的」。为什么这么说呢?这样,我们继续通过上述发送文件的例子来看并发与共享的关系(使用 QQ 发送硬盘上的文件 A,同时使用微信发送硬盘上的文件 B):
并发性的体现:两个进程正在并发执行。
如果失去并发性,系统中只有一个程序正在运行,则共享性失去存在的意义
共享性的体现:两个进程需要共享地访问硬盘资源。
如果失去共享性,则 QQ 和微信不能同时访问硬盘资源,就无法实现同时发送文件,并发也就无从谈起
③ 虚拟
先上定义:虚拟是指「把一个物理上的实体变为若干个逻辑上的对应物」。物理实体(前者)是实际存在的,而逻辑上对应物(后者)是用户感受到的。
这么说是有点虚无缥缈了,用一个例子来理解:
上文说过,一个程序需要放入内存并给它分配 CPU 才能执行。那比如说 GTA5 需要 4GB 的运行内存,QQ 需要256MB 的内存,Chrome 需要 512MB 的内存,网易云音乐需要 256MB 的内存…… 假设我们的电脑只有 4GB 内存且 CPU 是单核的。那么这里就存在如下两个问题:
「问题 1」:这些程序同时运行需要的内存远大于 4 GB,那么为什么它们还可以在我的电脑上同时运行呢?
答:这得益于虚拟内存技术的「空分复用技术」。虽然我们的电脑实际上只有 4GB 的内存,但是却可以完美的同时运行这些远大于 4G 内存的程序,「在用户看来似乎我们的电脑内存远远大于 4GB」。
「问题 2」:既然一个程序需要被分配 CPU 才能正常执行,那为什么单核 CPU 的电脑中也能同时运行这么多个程序呢?
答:这得益于虚拟内存技术的「时分复用技术」。虽然实际上只有一个单核 CPU,无法同时并行执行这么多个程序,但是微观上 CPU 在各个微小的时间段内交替着为各个进程服务,「在用户看来似乎我们的 CPU 是多核的」。
下面我们来解释一下上述两种虚拟技术:
1)「空分复用技术」
空分复用技术的原理就是把内存作为高速缓存来使用,只用来保存最频繁使用的部分程序,而把程序的大部分放在磁盘上。这种机制需要快速的映像内存地址,以便把程序生成的地址转换为有关字节在内存中的物理地址。这种映像由 CPU 中的一个部件,称为「存储器管理单元」(Memory Management Unit,「MMU」)来完成,这张图上面我们也看过了,回顾一遍:
2)「时分复用技术」
多个进程能在同一个 CPU 上并发执行就是因为使用了时分复用技术,让每个进程轮流占用处理器,每次只执行一小个时间片并快速切换。
显然,如果失去了并发性,一个时间段内系统中只能运行一道程序,那也就失去了实现虚拟性的意义了。因此,「没有并发性,就谈不上虚拟性」
④ 异步
异步是指:在多道程序环境下,允许多个程序并发执行,但「由于资源有限,进程的执行不是一贯到底的, 而是走走停停,以不可预知的速度向前推进」,这就是进程的异步性。
举个例子:老渣在 8 点 和 12 点要和两个女孩并发约会,每个女孩的约会时间是 2 小时,与一、二号的约会就是两道并发执行的程序,而老渣的心则是有限的系统资源。
由于并发运行的程序会争抢使用系统资源,而系统中的资源有限,那么老渣同一号或二号的 2 小时约会时间就可能无法一次性结束,而是断断续续的。可能 8 点到 9 点和一号约会,9 点到10 点和二号约会,然后 10 点到 11 点再和一号约会,最后 11 点到 12 点和二号约会。
如果失去了并发性,即系统只能串行地运行各个程序,那么每个程序的执行会一贯到底。因此,「只有系统拥有并发性,才有可能导致异步性」。
4. 学习操作系统的核心内容
本系列的核心内容主要有以下:
进程管理 存储管理 文件系统管理 I/O 设备管理
① 进程管理
进程(Process)这个概念在上文中已经多次出现了,为了能更好地实现操作系统的并发性和共享性,遂引入了进程。
「进程就是程序的一次执行过程,它是暂时的。不仅包含正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说 CPU、内存、网络资源等」。很多小伙伴在回答进程的概念的时候,往往只会说它是一个运行的实体,而会忽略掉进程所占据的资源。比如说,同样一个程序,同一时刻被两次运行了,那么他们就是两个独立的进程。
讲到进程不得不说一嘴线程,「一个进程中可以有多个线程,它们共享进程资源。」
举个例子,QQ 和 Chrome 浏览器是两个进程,Chrome 进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。
我们所说的进程管理其实主要包含两个内容:
进程通信 进程调度
「进程通信」顾名思义,就是进程之间的相互交流。CPU 作为计算机最宝贵的资源,一个进程需要放入内存并给它分配 CPU 才能执行,而一个程序可能包含多个进程,这就需要进程之间进行相互交流,彼此同步,共同完成这个程序。举个例子,如果进程 A 产生数据而进程 B 打印数据,那么 B 在 A 产生数据之前就必须等待,那么 A 是不是就得给 B 发消息,告诉 B 我生产了数据,你可以打印了。
「进程调度」就是说,通常情况下,会有多个进程或线程同时竞争 CPU,如果恰好只有一个 CPU 可用,这就导致 CPU 必须对下一个运行的进程或线程做出选择,这个选择的过程就是进程调度。
② 内存管理
高速缓存作为除寄存器外最底层的存储器,其管理是由硬件完成的,所以我们学习的内容集中在对内存的管理。
其实这个话题在上文操作系统的四个特征中虚拟这个特征时我们就说过了,内存一般用来保存正在执行的程序,在非常简单的操作系统中,内存中每次只能运行一个程序,如果要运行第二个程序,第一个程序就必须被移除内存,再把第二个程序装入内存。
显然这样非常低效的,为此引入了虚拟内存技术,把内存作为高速缓存来使用,只用来保存最频繁使用的部分程序,而把程序的大部分放在磁盘上。
那么内存管理做的事情大概就是:
把使用频繁的部分程序放入内存 当内存满的时候,替换掉内存中的某些部分
③ 文件系统管理
文件其实是进程创建的信息逻辑单元,一个磁盘可能含有几千甚至几百万个文件,每个文件都是独立于其他文件的,事实上,把每个文件看成一种地址空间更容易理解文件的本质。
文件同样是受操作系统管理的,有关文件的构造、命名、存取、使用、保护、实现和管理方法都是操作系统设计的内容,这就是文件系统的主题。
④ I/O 设备管理
操作系统必须高效的管理 I/O 设备,它需要向 I/O 设备发送命令,捕捉中断,并处理设备的各种错误,它还应该在设备和系统的其他部分之间提供简单且易用的接口。
5. 内核态和用户态
内核这个专有名词可能会让大家有点懵逼。这里解释一下。现代操作系统都采用进程的概念,为了更好的处理系统的并发性、共享性等,并使进程能够协调地工作,仅依靠计算机硬件提供的功能是远远不够的。例如,进程的调度就不能用硬件来实现,必须使用一组基本软件对硬件资源进行改造,以便为进程的执行提供良好的运行环境,这个软件就是内核(kernel)。
简单来说,「内核就是操作系统中的一组程序模块」,作为可信软件来提供支持进程并发执行的基本功能和基本操作,「具有访问硬件设备和所有内存空间的权限」。不夸张的说,内核是操作系统的核心。
当然,操作系统除了内核程序外,还有包括其他一些基本组件,如文本编辑器、编译器、用来与用户进行交互的程序比如桌面系统等。
① 什么是内核态和用户态
那么既然内核是程序,它需要运行,就必须被分配 CPU。因此,CPU 上会运行两种程序,一种是操作系统的内核程序(也称为系统程序),一种是应用程序。前者完成系统任务,后者实现应用任务。两者之间有控制和被控制的关系,前者有权管理和分配资源,而后者只能向系统申请使用资源。
显然,我们应该把在 CPU 上运行的这两类程序加以区分,这就是内核态和用户态出现的原因。
「内核态」(kernel mode):当 CPU 处于内核态时,这是操作系统管理程序(也就是内核)运行时所处的状态。运行在内核态的程序可以访问计算机的任何资源,不受限制,为所欲为,例如协调 CPU 资源,分配内存资源,提供稳定的环境供应用程序运行等。 「用户态」(user mode):应用程序基本都是运行在用户态的,或者说用户态就是提供应用程序运行的空间。运行在用户态的程序只能访问当前 CPU 上执行程序所在的地址空间,这样有效地防止了操作系统程序受到应用程序的侵害。
对操作系统来说,「什么样的程序应该放在内核态呢」?这取决于对资源的需求、时间的紧迫和效率高低等因素。比如 CPU、内存、设备等资源管理器程序应该在内核态运行,否则安全性没有保证。对于文件系统和数据来说,文件系统数据和管理必须放在内核态,但是用户的数据和管理可以放在用户态。
② 中断机制
在合适的情况下,操作系统的内核会把 CPU 的使用权主动让给应用程序,也就是使 CPU 从内核态转换到用户态。而 CPU 要想从用户态回到内核态,只能通过中断机制完成,如果没有中断机制,那么一旦应用程序上 CPU 运行(用户态),CPU 就会一直运行这个应用程序。也就是说,「中断是让操作系统内核夺回 CPU 使用权的唯一途径」。可以说,「操作系统是由中断驱动的」。
当然,这里的中断机制非常广义,包含了三种手段,也就是说从用户态转换到内核态有三种手段:
1)程序请求操作系统服务,执行系统调用 2)程序运行时产生外中断事件(比如 I/O 操作完成),运行程序被中断,转向中断程序处理 3)在程序运行时发生内中断(异常)事件,运行程序被打断,转向异常处理程序工作
以上这三种手段都是通过中断机制来发生的,那么接下来我们就来看看中断到底有哪些类型。
按照中断信号来源于CPU 的外部还是内部,将中断类型分为外中断和内中断:
「外中断」 (也称中断,狭义上的中断)
外中断与当前执行的指令无关, 中断信号来源于 CPU 外部。如 I/O 完成中断,表示设备输入/输出处理已经完成,CPU 能够发送下一个输入/输出请求。此外还有时钟中断、控制台中断等。
「内中断」(也称 异常、例外)
内中断与当前执行的指令有关, 中断信号来源于 CPU 内部。如非法操作码、地址越界、算术溢出,除数为 0 等。
这里简单解释一下「中断机制的基本原理」:不同的中断信号,肯定是需要用不同的中断处理程序来处理的。那么当 CPU 检测到中断信号后,就会根据中断信号的类型去查询「中断向量表」,以此来找到相应的中断处理程序在内存中的存放位置。
③ 系统调用
如上文所说,程序通过执行系统调用,也可以使得 CPU 从用户态转向内核态。那么系统调用和中断有啥关系呢?为什么说这种方式也属于中断机制的一种呢?那是因为「系统调用是通过陷入指令完成的」,该指令会引发内中断。
说的更详细点,操作系统作为计算机硬件之上的第一层软件,需要向上层提供一些简单易用的服务,这个上层包括用户和应用程序:
给用户提供的接口有图形界面 GUI 和命令接口,给应用程序提供的是「程序接口」,这个程序接口就是由一组系统调用组成的,是操作系统提供给开发人员使用的。可以理解为一种可供应用程序调用的特殊函数,「应用程序可以通过系统调用来请求获得操作系统内核的服务」。
「系统调用的过程」简略版大致如下:
1)在用户态,应用程序传递系统调用参数
2)执行陷入指令,引发一个内中断,使 CPU 进入内核态
3)在内核态,执行相应的请求,内核程序处理系统调用
4)返回应用程序
思考一下「为什么系统调用是必须的」?
举个例子:我们去学校打印店打印论文,你按下了 WPS 的 “打印” 选项,于是打印机开始工作。 你的论文打印到一半时,另一位同学按下了 Word 的 “打印” 按钮,开始打印他自己的论文。想象一下如果两个进程可以随意的、并发的共享打印机资源,会发生什么情况?
显然,两个进程并发运行,导致打印机设备交替的收到 WPS 和 Word 两个进程发来的打印请求,结果两篇论文的内容混杂在一起了
如何解决这个问题?这就需要操作系统内核对共享资源进行统一的管理,并向上层提供 “系统调用” ,运行在用户态的应用程序或者进程想要使用打印机这种共享资源,只能通过系统调用向操作系统内核发出请求,然后内核会对各个请求进行协调处理(进程调度)。
通过上面这个例子,我们就可以总结出什么功能会用到系统调用:「凡是与共享资源有关的操作(比如内存分配、I/O 操作、文件管理等),都必须通过系统调用的方式向操作系统内核提出请求,由操作系统内核代为完成」。这样可以保证系统的稳定性和安全性,防止用户进行非法操作。这些系统调用按功能大致可分为如下几类:
设备管理。完成设备的请求或释放,以及设备启动等功能。 文件管理。完成文件的读、写、创建及删除等功能。 进程控制。完成进程的创建、撤销、阻塞及唤醒等功能。 进程通信。完成进程之间的消息传递或信号传递等功能。 内存管理。完成内存的分配、回收以及获取作业占用内存区大小及地址等功能。
6. 总结
缝缝补补终于写完了,说实话,这种系列综述类的文章真的难写,既要照顾知识体系完整度,保证每个模块都能涉及,又不能讲的太深太细,还需要循序渐进,防止陡然出现一个并未介绍的专有名词。
OK,抓住春节的尾巴,祝大家新年快乐,牛年大吉 🎉,操作系统系列就此开篇了~
参考资料
《现代操作系统 — 第 3 版》 《王道考研 — 计算机网络》 百度百科 — 进程管理 知乎 — 什么是操作系统?https://www.zhihu.com/question/61861692/answer/575287870
😁 下方点击卡片关注公众号「飞天小牛肉」(专注于分享计算机基础、Java 基础和面试指南的相关原创技术好文,帮助读者快速掌握高频重点知识,有的放矢),与小牛肉一起成长、共同进步
🎉 并向大家强烈推荐我维护的 Gitee 仓库 「CS-Wiki」(Gitee 推荐项目,目前已 0.9k star。面向全栈,致力于构建完善的知识体系:数据结构、计算机网络、操作系统、算法、数据库、设计模式、Java 技术栈、机器学习、深度学习、强化学习等),相比公众号,该仓库拥有更健全的知识体系,欢迎前来 star,仓库地址 https://gitee.com/veal98/CS-Wiki。也可直接下方扫码访问
原创不易,读完有收获不妨点赞|分享|在看支持哦