0%

操作系统-概论

什么是操作系统

概述

现代计算机系统是一个复杂的系统,为了管理这些设备,为用户程序提供一个更清晰,更简便的计算机模型,计算机安装了一层软件:操作系统

用户与操作系统交互的程序有两种(用户接口程序并不属于操作系统的一部分):

  • Shell:基于文本;
  • GUI(Graphical User Interface):基于图形。

在计算机系统中操作系统所处的位置如下图:

操作系统位置

  • 内核态的操作系统拥有对所有硬件的完全访问权,可以执行机器能够运行的任何指令;
  • 用户态只使用了机器指令的一个子集。

用户接口程序(Shell,GUI)允许用户运行其他程序。

扩展机器

抽象是管理复杂性的一个关键。以SATA硬盘为例,在硬件层面其接口及其复杂,而且经过不停地修改,因此提供了一个叫做硬盘驱动的软件来与硬盘交互,这类软件提供了读写硬盘的接口,而不需要深入细节。即使在这个层面,对大多数应用来说还是太底层了,因此,所有的操作系统都提供了使用硬盘的另一层抽象:文件

资源管理

从这个角度看,操作系统的任务是在相互竞争的程序之间有序地控制对硬件设备资源的分配。它记录着哪个程序在使用什么资源,对资源请求进行分配,评估使用代价,并且为不同的程序和用户调解互相冲突的资源请求。

计算机硬件

CPU

CPU是“Center Process Unit”缩写,CPU与电路进行电信号交换,而且对于电信号,只能识别ON和OFF两种状态。

如果把电信号的开(ON)/关(OFF)与数字0和1对应起来,就能将二进制数转换为电信号,同时电信号也可以转换回二进制数。因为我们可以把十进制数转换成二进制数,也能把二进制数还原成十进制数,所以人们发明了普通的计算机。

当给每个文字编码,将之与二进制数字对应起来,就可以把文字也转换成电信号,让CPU来处理文字。依此类推,人们接着又找到了将图像、音乐等等转换成电信号的方法,使CPU的应用范围越来越广。不过CPU还是一如既往,只能处理电信号。

CPU能处理的并不仅仅只有数据,还可以用电信号向CPU发出指令。我们所编写的程序最终都要转换成所谓的机器语言,这些机器语言就是以电信号的形式发送给CPU的。这些机器语言不过就是一连串的指令代码,实际上也就是一串0和1的组合而已。

CPU从内存中取出指令并执行,在每个CPU的周期中,计算机首先从内存中取出指令,解码以确定其类型和操作数,然后执行,然后取指,解码并执行下一条指令。

每种CPU都有一套专用的可执行的指令集。由于用来访问内存以得到指令的或数据的时间要比执行指令花费的时间长得多,因此CPU都有用于保存关键变量的临时数据的通用寄存器。

此外还有一些专用寄存器:

  • 程序计数器:指向下一条指令的地址;
  • 堆栈指针:指向内存中当前栈的顶端;
  • 程序状态字(Program Status Word,PSW):包含条件码位,CPU优先级,模式(用户态/内核态),以及其他的控制位。

存储器

分类 典型访问时间 典型容量
寄存器 1ns <1KB
高速缓存 2ns 4MB
主存 10ns 1-16GB
磁盘 10ms 1-nT

磁盘

I/O设备

总线

计算机启动

主引导扇区

  • 主引导扇区是硬盘0号柱面,0号磁道的第一个扇区,大小为512字节。
  • 主引导记录(MBR),占用主引导扇区的前446字节,紧随其后的64字节是磁盘分区表DPT,最后还剩两个字节则恒为55AA,表示结束符号(Magic Number)。

MBR,全称为Master Boot Record,即硬盘的主引导记录。MBR,共446字节,一般在操作系统安装时写入,但它并不属于操作系统。MBR就是一段引导程序,用于检测磁盘的分区合法性和加载操作系统,它的重要作用就是识别活动分区,并引导操作系统。它不依赖任何操作系统,而且启动代码也是可以改变的,从而能够实现多系统引导。

分区表DPT,共64字节,记录了硬盘有多少分区以及分区的各种属性。由于一个分区的信息要占用16字节,所以分区表只能定义4个分区,这就是为什么我们说硬盘一般最多只能分为4个主分区。

主分区、扩展分区、逻辑分区

主分区是由主引导扇区中64字节的分区表所定义的,最多只能有4个。但为了满足更多分区的需求,便产生了扩展分区。形式上,如果拥有扩展分区,就必须牺牲一个主分区,而且最多有一个扩展分区,也就是说:主分区+扩展分区<=4 && 扩展分区<=1。因此扩展分区也可以看成一种特殊的主分区。

但扩展分区并不可以直接使用,扩展分区又必须以逻辑分区的形式出现,可以这样认为:扩展分区包含着若干逻辑分区,而且至少包含一个。

扩展分区中的逻辑分区是以链式存在的。即每一个逻辑分区都记录着下一个逻辑分区的位置信息,依次串联。事实上每一个逻辑分区都有一个和主引导扇区类似的引导扇区,引导扇区里有类似的分区表。该分区表记录了该分区的信息和一个指针,指向下一个逻辑分区的引导扇区。

因此,逻辑分区是借鉴了主分区的方法,相当于在一个主分区下面建立了若干级“主分区”。一个可以预测的现象是:一旦某一个逻辑分区损害,跟在它后面的所有逻辑分区都将丢失,而前面的逻辑分区去可以保留。这也是链式结果的特点。

固件接口标准

BIOS

IBM推出的业界标准的固件接口,存储于主板的ROM/EEPROM/flash中,提供的功能包括:

  • 开机自检
  • 加载引导程序(MBR中的,通常是bootloader的第一级)
  • 向OS提供抽象的硬件接口

PS:CMOS是PC上的另一个重要的存储器,用于保存BIOS的设置结果,CMOS是RAM。

在每台计算机上有一块“双亲板”,其上有一个称为BIOS(Basic Input Output System)的程序。

在计算机启动时,BIOS开始运行。它首先检查所安装的RAM数量,键盘和其他设备是否已安装且正常响应。接着它开始扫描PCIe和PCI总线并找出连在上面的所有设备。

然后BIOS通过存储在CMOS存储器中的设备清单决定启动设备。用户可以在系统刚刚启动的时候进入一个BIOS配置程序,对设备清单进行修改。

然后操作系统询问BIOS,以获取配置信息。对于每种设备,系统检查对应的设备驱动程序是否存在。如果没有,系统要求用户插入含有该设备驱动程序的CD-ROM(由设备供应商提供)或者从网络上下载驱动程序。一旦拥有全部的设备驱动程序,操作系统将它们调入内核,然后初始化有关表格,创建需要的任何背景进程,并在每个终端上启动登录程序或者GUI。

EFI/UEFI

Unified Extensible Firmware Interface,架设在系统固件之上的软件接口,用于替代BIOS接口,EFI是UEFI的前称。

一般认为,UEFI由以下几个部分组成:

  • Pre-EFI初始化模块
  • EFI驱动程序执行环境(DXE)
  • EFI驱动程序
  • 兼容性支持模块(CSM)
  • EFI高层应用
  • GUID磁盘分区表(GPT)

通常初始化模块和DXE被集成在一个ROM中;EFI驱动程序一般在设备的ROM中,或者ESP中;EFI高层应用一般在ESP中。CSM用于给不具备UEFI引导能力的操作系统提供类似于传统BIOS的系统服务。

启动方式

Legacy mode

即通过MBR/BIOS进行引导的传统模式,流程如下:

  1. BIOS加电自检(Power On Self Test – POST)。
  2. 读取主引导记录(MBR)。BIOS根据CMOS中的设置依次检查启动设备:将相应启动设备的第一个扇区(也就是MBR扇区)读入内存。
  3. 检查MBR的结束标志位是否等于55AA,若不等于则转去尝试其他启动设备,如果没有启动设备满足要求则显示”NO ROM BASIC”然后死机。
  4. 当检测到有启动设备满足要求后,BIOS将控制权交给相应启动设备的MBR。
  5. 根据MBR中的引导代码启动引导程序。

UEFI mode

UEFI启动不依赖于Boot Sector(比如MBR),大致流程如下:

  1. Pre-EFI初始化模块运行,自检
  2. 加载DXE(EFI驱动程序执行环境),枚举并加载EFI驱动程序(设备ROM或ESP中)
  3. 找到ESP中的引导程序,通过其引导操作系统。

CSM mode

UEFI中的兼容性支持模块(Compatible Support Module)提供了引导UEFI固件的PC上的传统磁盘(MBR格式)的方法。

分区表

MBR分区表

指的是512字节的Master Boot Record(主引导记录)中的分区表,由于大小限制,其中只能存有最多四个分区的描述(亦即4个主分区)。

EBR分区表

位于Extended Boot Record(扩展引导纪录)中的分区表,该分区表所描述的分区即扩展分区。每个EBR仅表示了一个扩展分区,该扩展分区紧接在它的EBR后。EBR中的四个分区描述符中的第一个表示其描述的分你去,第二个描述符则表示下一个扩展分区(如果是最后一个则为空),也就是说,各个EBR串接成了一个EBR链表。

GPT分区表

GUID Partition Table,是EFI标准的一部分,用于替代MBR分区表,相较起来有分区更大,数量更多(没有4个主分区的限制)等优势,GPT格式的硬盘结构如下,可以看到首部MBR的位置有个保护MBR(用于防止不识别GPT的硬盘工具错误识别并破坏硬盘中的数据),这个MBR中只有一个类型为0xEE的分区。

Bootloader

Bootloader即引导程序,用于启动操作系统或者其它引导程序(比如Grub启动Windows Bootmgr)

Grub

GNU的开源引导程序,可以用于引导Linux等操作系统,或者用于链式引导其它引导程序(比如Windows Boot Manager),分为三个部分,分别称为步骤1、1.5、2,看名字就可以知道,步骤1.5是可有可没有的,这三个步骤对应的文件分别是:

  • Boot.img:步骤1对应的文件,446个字节大小,步骤1可以引导步骤1.5也可以引导步骤2。MBR分区格式的磁盘中,放在MBR里(446也是为了符合MBR的启动代码区大小);GPT分区格式的磁盘中,放在Protective MBR中。
  • Core.img:步骤1.5对应的文件,32256字节大小。MBR分区格式的磁盘中,放在紧邻MBR的若干扇区中;GPT分区格式的磁盘中,则放在34号扇区开始的位置(第一个分区所处的位置),而对应的GPT分区表中的第一个分区的entry被置空。通常其中包含文件系统驱动以便load步骤2的文件。
  • /boot/grub:步骤2对应的文件目录,放在系统分区或者单独的Boot分区中

Windows Boot Manager

是从Windows Vista开始引进的新一代开机管理程序,用以取代NTLDR。

当电脑运行完开机自检后,传统型BIOS会根据引导扇区查找开机硬盘中标记”引导”分区下的BOOTMGR文件;若是UEFI则是Bootmgfw.efi文件和Bootmgr.efi文件,接着管理程序会读取开机配置数据库(BCD, Boot Configuration Database)下的引导数据,接着根据其中的数据加载与默认或用户所选择的操作系统。

操作系统大观

  • 大型机操作系统
  • 服务器操作系统
  • 多处理器操作系统
  • 个人计算机操作系统
  • 掌上计算机操作系统
  • 嵌入式操作系统
  • 传感器节点操作系统
  • 实时操作系统
  • 智能卡操作系统

操作系统结构

单体系统

整个操作系统在内核态以单一程序的方式运行,整个操作系统以过程集合的方式编写,首先编译单个过程,然后链接成一个大型可执行二进制程序。使用这种技术,系统中的每个过程可以自由调用其他过程,但无数个不受限制地彼此调用的过程显得过于笨拙,而且任何一个过程的崩溃都会连累整个接口。

许多操作系统支持可装载的扩展,在UNIX中称为共享库(Shared Library,so),在Windows中称为动态链接库(Dynamic Link Library,dll)

在单体系统中也可能有一些结构存在。

层次系统

将单体系统中的结构进一步通用化,就变成一个层次式的系统。THE操作系统结构如下:

THE操作系统结构

微内核

微内核的设计思想:为了实现高可靠性,将操作系统划分为小的,定义良好的模块,只有其中一个模块——微内核,运行在内核态,其余的模块作为普通用户进程运行。

由于把每个设备驱动和文件系统分别作为普通用户进程,当这些模块发生错误时不会造成整个系统的崩溃。

当然,微内核由于增加了许多系统调用,因此性能上可能会有所下降。

客户端-服务器模式

这个模式可以看成是微内核的变体。

虚拟机

分层系统可延伸为虚拟机概念,虚拟机的基本思想是单个计算机的硬件抽象为几个不同的执行部件从而使得仿佛每个独立的执行环境都在自己的计算机运行一样。通过cpu调度和虚拟内存技术,操作系统能带来一种幻觉,即进程认为有自己的处理器和自己的(虚拟)内存。

  • 非虚拟机:进程→内核-硬件
  • 虚拟机:进程→内核→虚拟机实现→硬件

创建虚拟机的原因:最根本的是,在并行运行几个不同的执行环境(即不同的操作系统)能共享相同的硬件。

虚拟机方法的主要困难在于磁盘系统。解决方法是提供虚拟磁盘。

底层机器有两种模式:用户模式和内核模式。

虚拟机软件可以运行在内核模式,因为它就是操作系统,虚拟机本身只能运行在用户模式,但他必须有虚拟用户模式和虚拟内核模式。这两种模式都运行在物理用户模式。

虚拟模式的转换可按下述方法实现。例如,当以一个虚拟用户模式而在虚拟机上运行的程序执行系统调用时,他会在真正机器上引起一个到虚拟机监控器的转换。当虚拟机监控器获得控制,他能改变虚拟机的寄存器内容和程序计数器以模拟系统调用的效果。接着他能重新启动虚拟机,注意他现在是在虚拟机内核模式下执行。

实例:VMWare,Java VM

操作系统和内核

区别

  • 操作系统:管理计算机硬件与软件资源的系统软件,同时也是计算机系统的内核与基石。操作系统需要处理如管理与配置内存、决定系统资源供需的优先次序、控制输入与输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互的操作界面。对我们来说,操作系统最直观的感受就是桌面系统,以及上层的应用程序,而后面的资源处理等等就是操作系统背后的黑盒。
  • 内核:一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。内核是操作系统最基本的部分。它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间。直接对硬件操作是非常复杂的,所以内核通常提供一种硬件抽象的方法来完成这些操作。硬件抽象隐藏了复杂性,为应用软件和硬件提供了一套简洁,统一的接口,使程序设计更为简单。简单来说,内核是一个操作系统的核心。它负责管理系统的进程、内存、设备驱动程序、文件和网络系统等等,决定着系统的性能和稳定性。是连接应用程序和硬件的桥梁。内核就是操作系统背后黑盒的核心。

它们的关系如下图:

操作系统和内核

内核的分类

宏内核(单内核)

内核管理着操作系统的内存,文件,IO,网络等等,每个功能可以看做一个模块,在宏内核中,这些模块都是集成在一起的,运行在内核进程中,模块之间的交互直接通过方法调用。如下图:

宏内核

微内核

在微内核中,内核只提供最核心的功能,比如任务调度,内存管理等等,其他模块被移出内核,运行在不同的进程中,这样即使某一个模块出现问题,只要重启这个模块的进程即可,不会影响到其他模块,稳定性大大增加。甚至可以在系统运行过程中替换现有模块的实现。而且由于模块独立的性质,可以做到模块的按需加载。但是模块间的相互调用需要通过进程间通信,通信效率相对较低。如下图:

微内核

混合内核

宏内核和微内核各有千秋,也各有缺点,所以混合内核就是集中了两者的特点,让微内核中的一些核心模块运行在内核中,从而使内核效率更高一些。

外内核

外内核是把硬件暴露给应用程序,应用程序可以直接访问硬件,外内核对系统提供保护。目前还在研究阶段。

宏内核/微内核对比

- 宏内核 微内核
通信效率 高(函数调用) 低(进程间通信)
稳定性 低(模块集成在一起) 高(模块间互不影响)
扩展性 低(模块集成在一起) 高(模块间互不影响)
代码量 多(需要实现所有模块) 少(只需要实现核心功能)

主流操作系统内核

宏内核:

  • Linux
  • Windows 9X 系列
  • MacOS 8.6 版本之前

微内核:

  • Fuchsia
  • 鸿蒙
  • Minix

混合内核:

  • Windows XP
  • Windows 7
  • Mac OS X
  • XNU

外内核:

  • Nemesis

用户态和内核态

特权级

熟悉Unix/Linux系统的人都知道,fork的工作实际上是以系统调用的方式完成相应功能的,具体的工作是由sys_fork负责实施。其实无论是不是Unix或者Linux,对于任何操作系统来说,创建一个新的进程都是属于内核功能,因为它要做很多底层的工作,消耗系统的物理资源,比如分配物理内存,从父进程拷贝相关信息,拷贝设置页目录页表等等,这些显然不能随便让哪个程序就能去做,于是就自然引出特权级别的概念,显然,最关键性的权力必须由高特权级的程序来执行,这样才可以做到集中管理,减少有限资源的访问和使用冲突。

特权级显然是非常有效的管理和控制程序执行的手段,因此在硬件上对特权级做了很多支持,就Intel x86架构的CPU来说一共有0~3四个特权级,0级最高,3级最低,硬件上在执行每条指令时都会对指令所具有的特权级做相应的检查。硬件已经提供了一套特权级使用的相关机制,软件自然就是好好利用的问题,这属于操作系统要做的事情,对于 Unix/Linux来说,只使用了0级特权级和3级特权级。也就是说在Unix/Linux系统中,一条工作在0级特权级的指令具有了CPU能提供的最高权力,而一条工作在3级特权级的指令具有CPU提供的最低或者说最基本权力。

用户态和内核态

现在我们从特权级的调度来理解用户态和内核态就比较好理解了,当程序运行在3级特权级上时,就可以称之为运行在用户态,因为这是最低特权级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态;反之,当程序运行在0级特权级上时,就可以称之为运行在内核态。

虽然用户态下和内核态下工作的程序有很多差别,但最重要的差别就在于特权级的不同,即权力的不同。运行在用户态下的程序不能直接访问操作系统内核数据结构和程序,比如上面例子中的testfork()就不能直接调用 sys_fork(),因为前者是工作在用户态,属于用户态程序,而sys_fork()是工作在内核态,属于内核态程序。

当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态,比如testfork()最初运行在用户态进程下,当它调用fork()最终触发 sys_fork()的执行时,就切换到了内核态。

同步/异步和阻塞/非阻塞

  • 同步、异步:涉及到IO通知机制。所谓同步,就是发起调用后,被调用者处理消息,必须等处理完才直接返回结果,没处理完之前是不返回的,调用者主动等待结果;所谓异步,就是发起调用后,被调用者直接返回,但是并没有返回结果,等处理完消息后,通过状态、通知或者回调函数来通知调用者,调用者被动接收结果。
  • 阻塞、非阻塞:涉及到CPU线程调度。所谓阻塞,就是调用结果返回之前,该执行线程会被挂起,不释放CPU执行权,线程不能做其它事情,只能等待,只有等到调用结果返回了,才能接着往下执行;所谓非阻塞,就是在没有获取调用结果时,不是一直等待,线程可以往下执行,如果是同步的,通过轮询的方式检查有没有调用结果返回,如果是异步的,会通知回调。
  • 同步阻塞:老张在厨房用普通水壶烧水,一直在厨房等着(阻塞),盯到水烧开(同步);
  • 异步阻塞:老张在厨房用响水壶烧水,一直在厨房中等着(阻塞),直到水壶发出响声(异步),老张知道水烧开了;
  • 同步非阻塞:老张在厨房用普通水壶烧水,在烧水过程中,就到客厅去看电视(非阻塞),然后时不时去厨房看看水烧开了没(轮询检查同步结果);
  • 异步非阻塞:老张在厨房用响水壶烧水,在烧水过程中,就到客厅去看电视(非阻塞),当水壶发出响声(异步),老张就知道水烧开了。

POSIX

POSIX,Portable Operating System Interface(可移植操作系统接口)。是UNIX系统的一个设计标准,很多类UNIX系统也在支持兼容这个标准,如Linux,遵循这个标准的好处是软件可以跨平台。

从Unix之后出现了许多独立开发的与Unix类似但又不完全兼容的OS,通称Unix-like OS。局面非常混乱,为了提高兼容性和应用程序的可移植性,标准化Unix-like OS,提出了大家都应该遵守的POSIX标准。后来,Unix这个名字成为了商标,只有花钱进行POSIX标准兼容性测试并通过了的OS,才能称为Unix,其余的OS,最多称为Unix-like OS。

Windows从WinNT开始就有兼容POSIX的考虑,因为当年在要求严格的领域,Unix地位比Windows高。

一般情况下,应用程序通过应用编程接口(API)而不是直接通过系统调用来编程,因为应用程序使用的这种编程接口实际上并不需要和内核提供的系统调用对应。一个API定义了一组应用程序使用的编程接口,它们可以实现成一个系统调用,也可以通过调用多个系统调用来实现,也可以完全不使用任何系统调用。实际上,API可以在各种不同的操作系统上实现,给应用程序提供完全相同的接口,而它们本身在这些系统上的实现却可能迥异。

完成同一功能,不同内核提供的系统调用(也就是一个函数)是不同的,例如创建进程,linux下是fork函数,windows下是creatprocess函数。为了解决跨平台的问题,linux和windows等都要实现基本的posix标准,linux把fork函数以及windows把creatprocess函数封装成同一个函数,都声明在unistd.h里。这样,程序员编写普通应用时候,只用包含unistd.h,然后调用这个封装后的函数,程序就在源代码级别可移植了。