OS学习_系统结构及系统调用
系统结构
根据之前提到的操作系统索要达成的目标, 可以得出操作系统是为什么要存在.
无外乎有几点.
- 对底层硬件的抽象, 对于大部分开发来说, 硬件的东西总是比较困难的, 想办法讲硬件抽象成简单的接口更有利于开发应用.
- 对各应用程序之间产生隔离. 应用直接对底层硬件操作, 难免会对一些资源有所争抢, 甚至是通过底层硬件进入其他应用.
- 应用之间的隔离又不能完全隔离. 人和人之间要沟通, 应用之间也难免要发生信息交换. 于是乎隔离和交互间又存在安全问题.
硬件抽象
对于大部分人来说, 开发应用的时候能多方便就多方便, 但是想要获取更高的性能, 只能向底层硬件贴近. 对于大佬来说, 自己动手造轮子, 与硬件交互的代码自己编写成库, 无疑能够让程序获得高性能.
事实上也是有这样的实现, 例如在嵌入式OS的开发中或是在实时系统(RTOS)中开发. 无外乎这些系统的目标都需要高性能, 并且目标单一.在这种情况下, 开发人员也许会选择让应用程序操作硬件资源.
在平时我们使用的操作系统中, 无论是Windows或是Linux系下的系统, 设计目的并不是为了单一的某个应用, 在用户进行某项应用的操作时, 背后往往会运行着其支撑服务或是其他应用. 例如我现在在写文章, 使用的编辑器, 其背后还会有Markdown的实时渲染引擎在运作, 而渲染引擎又可能依赖别的基础服务. 当所有的应用都希望获得高性能, 所有的应用都会争抢这一份硬件资源来给自身提高战力. 这时便需要有一个人来维持这么多应用之间的”秩序”, 而操作系统就是那个领导者.
除了对底层硬件资源的争夺, 更需要担心的是我们无法排除运行的应用是否是善意的. 应用直接的对硬件资源进行操作, 可能会对其他应用产生监控甚至是危害的行为.

因此, 操作系统需要作为应用程序与硬件资源之间的协调者, 一方面是抽象出硬件资源方便开发上层应用程序, 另一方面是完成对应用与硬件资源之间的隔离.

进程概述
进程, 是大多数的操作系统对应用程序间隔离的实现.
每个进程作为一个隔离单元, 单独为应用提供服务. 操作系统需要对众多进程之间完成其对硬件资源需求的调度.
从操作系统的角度来看, 操作系统需要协调多个进程之间对硬件资源的需求. 操作系统采取了在电子设计中非常常用的复用手段. 在电子设计中我们常常将信号在频率和时间上分离, 使得一个信号能够被多次使用. 对操作系统来说, 采用了对时空 (即时间和空间) 上的分离, 使得进程对硬件资源在时间和空间上, 一方面得以提高资源的利用率, 另一方面也巧妙的实现了对各进程之间的隔离.
从应用程序的角度来看, 系统将应用分配到进程. 对于每个进程来说, 进程似乎能够享用所有的硬件资源. 操作系统将进程的内存空间从物理内存上划分出来, 映射出从 0 到相应大小的内存虚拟地址空间, 当进程需要访问内存中的某个地址, 系统会将虚拟地址映射回实际的物理地址进行相应的操作, 这就是操作系统对空间上的分离, 在实现上为页表 (Page tables) 的实现.

在对于硬件资源时间分配上的分离, 也叫做分时复用. CPU讲一段时间分割为相同大小的时间片, 在一个极短的时间片上执行一个进程, 当到达了下一个时间片, 将当前进程挂起, 处理下一个进程, 看起来就像多个进程在同时执行, 也就是我们所说的并发.

通过对进程的实现, 操作系统对各应用程序之间的隔离实现便能够完成. 当然其中会有很多细节保证进程的实现能够正常进行, 后面在细说.
系统调用
想要从应用程序中对相关底层资源进行调用, 首先要从应用程序跳到内核中, 由内核来完成对底层硬件资源的调用.
因此, 进程可以通过系统调用使得应用代码跳入内核中执行调用对底层资源的调用, 例如文件操作, 内存分配等等. 当应用程序需要执行这些系统调用时, 需要内核临时将应用的权限提高, 也就是之前提到的权限访问机制.
代码转化为CPU指令后分为特权指令和非特权指令, 因此在操作系统中也区分了内核态和用户态. 在特权指令的操作下能够完成对系统资源的调用, 因此需要在内核态中执行, 普通指令在用户态中执行. 当用户想要调用系统调用, 在RISC-V架构中需要调用ecall指令1, 由内核来判定该次调用是否可以执行2,如果可以执行, 则切换成内核态即跳入内核中执行相应系统调用, 完成后退回用户态. 至此这就大致是一次系统调用的过程.
1 | /* part of xv6-riscv/user/usys.S */ |
内核架构
由于加入了各类对相关底层资源调用的模块及系统调用, 内核也随之变得庞大起来, 因此产生了两大派别, 宏内核 (monolithic kernel) 和 微内核 (micro kernel).
宏内核的设计使得整个操作系统都能拥有完全的硬件权限, 但也因此导致了内核体积不断的增大, 且必须保证内核中的服务完全正确且能够防止应用程序的恶意破坏. 否则一旦发生错误则会导致整个内核崩溃, 操作系统崩溃只能重新启动, 对于用户来说无疑是不能接受的事情.

因此有人提出了微内核的架构设计, 即内核中只保存IPC (interprocess communication进程间通信), 与底层硬件交互等的底层函数, 其余文件服务, 进程管理等服务移至用户空间内作为进程实现, 保证内核的最小实现下提供服务, 减少内核崩溃的风险, 但也会导致应用操作随着进程间通信次数的增多而效率降低.

两种内核架构的设计都各自有优缺点, 不同的操作系统也会由于各自的原因采取不同的设计 (也许只是设计者的喜好罢了). 并没有不好的设计, 只有不会用它的人罢了.
Question
- 进程内部内存空间的划分及进程调度
- 进程在内核态和用户态之间切换的凭证
- 需要一点计组知识, 补两天.
1 :不同的CPU架构对应不同的指令集
2 : 如判定是否访问的是该进程的内存空间, 是否存在其他权限指令等. 这样做不仅保证了进程间的相互隔离, 也保证了内核自身的安全.



