操作系统基础

什么是操作系统?

  1. 操作系统(Operating System,简称 OS)是管理计算机硬件与软件资源的程序,是计算机系统
    的内核与基⽯;
  2. 操作系统本质上是运⾏在计算机上的软件程序 ;
  3. 操作系统为⽤户提供⼀个与系统交互的操作界⾯ ;
  4. 操作系统分内核与外壳(我们可以把外壳理解成围绕着内核的应⽤程序,⽽内核就是能操作硬件
    的程序)。

内核负责管理系统的进程、内存、设备驱动程序、⽂件和⽹络系统等等,决定着系统的性能和稳定性。

是连接应⽤程序和硬件的桥梁。 内核就是操作系统背后⿊盒的核⼼。

image-20210519110054163

1. 什么是系统调用?

前置条件:用户态和系统态。

根据进程访问资源的特点,我们可以把进程在系统上的运⾏分为两个级别:

  1. 用户态(user mode):⽤户态运⾏的进程或可以直接读取⽤户程序的数据。
  2. 系统态(kernel mode):可以简单的理解系统态运⾏的进程或程序⼏乎可以访问计算机的任何资
    源,不受限制。

我们运⾏的程序基本都是运⾏在⽤户态,如果我们调⽤操作系统提供的系统态级别的⼦功能咋办呢?那
就需要系统调⽤了!

也就是说在我们运⾏的⽤户程序中,凡是与系统态级别的资源有关的操作(如⽂件管理进程控制
存管理等),都必须通过系统调⽤⽅式向操作系统提出服务请求,并由操作系统代为完成。

这些系统调⽤按功能⼤致可分为如下⼏类:

  • 设备管理。完成设备的请求或释放,以及设备启动等功能。
  • ⽂件管理。完成⽂件的读、写、创建及删除等功能。
  • 进程控制。完成进程的创建、撤销、阻塞及唤醒等功能。
  • 进程通信。完成进程之间的消息传递或信号传递等功能。
  • 内存管理。完成内存的分配、回收以及获取作业占⽤内存区⼤⼩及地址等功能。

2. 进程和线程

2.1 进程和线程的区别

下图是 Java 内存区域,我们从 JVM 的⻆度来说⼀下线程和进程之间的关系吧!

image-20210519110808841

从上图可以看出:⼀个进程中可以有多个线程,多个线程共享进程的堆和⽅法区 (JDK1.8 之后的元空
间)资源,但是每个线程有⾃⼰的程序计数器、 虚拟机栈 和 本地⽅法栈

总结:

线程是进程划分成的更⼩的运⾏单位,⼀个进程在其执⾏的过程中可以产⽣多个线程。

线程和进程最⼤的不同在于基本上各进程是独⽴的,⽽各线程则不⼀定,因为同⼀进程中的线程极有可能会相互
影响。

线程执⾏开销⼩,但不利于资源的管理和保护;⽽进程正相反。

2.2 进程有哪几种状态?

我们⼀般把进程⼤致分为 5 种状态,这⼀点和线程很像!

  • 创建状态(new) :进程正在被创建,尚未到就绪状态。
  • 就绪状态(ready) :进程已处于准备运⾏状态,即进程获得了除了处理器之外的⼀切所需资源,
    ⼀旦得到处理器资源(处理器分配的时间⽚)即可运⾏。
  • 运⾏状态(running) :进程正在处理器上上运⾏(单核 CPU 下任意时刻只有⼀个进程处于运⾏状
    态)。
  • 阻塞状态(waiting) :⼜称为等待状态,进程正在等待某⼀事件⽽暂停运⾏如等待某资源为可⽤
    或等待 IO 操作完成。即使处理器空闲,该进程也不能运⾏。
  • 结束状态(terminated) :进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运
    ⾏。

image-20210519113314956

2.3 进程间的通信⽅式

⼤概有 7 种常⻅的进程间的通信⽅式。

  1. 管道/匿名管道(Pipes) :⽤于具有亲缘关系的⽗⼦进程间或者兄弟进程之间的通信。

  2. 有名管道(Names Pipes) : 匿名管道由于没有名字,只能⽤于亲缘关系的进程间通信。为了克服
    这个缺点,提出了有名管道。有名管道严格遵循先进先出(first in first out)。有名管道以磁
    盘⽂件的⽅式存在,可以实现本机任意两个进程通信。

  3. 信号(Signal) :信号是⼀种⽐较复杂的通信⽅式,⽤于通知接收进程某个事件已经发⽣;

  4. 消息队列(Message Queuing) : 消息队列是消息的链表,具有特定的格式,存放在内存中并由消息
    队列标识符标识

    管道和消息队列的通信数据都是先进先出的原则。

    与管道(⽆名管道:只存在于内存中的⽂件;命名管道:存在于实际的磁盘介质或者⽂件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显示地删除⼀个消息队列时,该消息队列才会被
    真正的删除。

    消息队列可以实现消息的随机查询,消息不⼀定要以先进先出的次序读取,也可以按消息的类型读取.⽐ FIFO 更有优势。

    消息队列克服了信号承载信息量少,管道只能承载⽆格式字 节流以及缓冲区大小受限等缺

  5. 信号量(Semaphores) : 信号量是⼀个计数器,⽤于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信⽅式主要⽤于解决与同步相关的问题并避免竞争条件。

  6. 共享内存(Shared memory) : 使得多个进程可以访问同⼀块内存空间,不同进程可以及时看到对⽅进程中对共享内存中数据的更新。这种⽅式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有⽤的进程间通信⽅式。

  7. 套接字(Sockets) : 此⽅法主要⽤于在客户端和服务器之间通过⽹络进⾏通信。

    套接字是⽀持TCP/IP 的⽹络通信的基本操作单元,可以看做是不同主机之间的进程进⾏双向通信的端点,简单的说就是通信的两⽅的⼀种约定,⽤套接字中的相关函数来完成通信过程。

2.4 线程间的同步的⽅式

线程同步是两个或多个共享关键资源的线程的并发执⾏。

应该同步线程以避免关键的资源使⽤冲突。操作系统⼀般有下⾯三种线程同步的⽅式:

  • 互斥量(Mutex): 采⽤互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。 因为
    互斥对象只有⼀个,所以可以保证公共资源不会被多个线程同时访问。

    ⽐如 Java 中的synchronized 关键词和各种 Lock 都是这种机制。

  • 信号量(Semphares) : 它允许同⼀时刻多个线程访问同⼀资源,但是需要控制同⼀时刻访问此资
    源的最⼤线程数量

  • 事件(Event): 通过通知操作的⽅式来保持多线程同步,还可以⽅便的实现多线程优先级的⽐较操作

2.5 进程调度算法

现代os都将线程作为最小调度单位,进程作为资源分配的最小单位。

为了确定⾸先执⾏哪个进程以及最后执⾏哪个进程以实现最⼤ CPU 利⽤率,计算机科学家已经定义了
⼀些算法,它们是:

  • 先到先服务(FCFS)调度算法 : 从就绪队列中选择⼀个最先进⼊该队列的进程为之分配资源,使
    它⽴即执⾏并⼀直执⾏到完成发⽣某事件⽽被阻塞放弃占⽤ CPU 时再重新调度。
  • 短作业优先(SJF)的调度算法 : 从就绪队列中选出⼀个估计运⾏时间最短的进程为之分配资源,
    使它⽴即执⾏并⼀直执⾏到完成或发⽣某事件⽽被阻塞放弃占⽤ CPU 时再重新调度。
  • 时间⽚轮转调度算法 : 时间⽚轮转调度是⼀种最古⽼,最简单,最公平且使⽤最⼴的算法,⼜
    称 RR(Round robin)调度。每个进程被分配⼀个时间段,称作它的时间⽚,即该进程允许运⾏的
    时间。
  • 多级反馈队列调度算法 : 前⾯介绍的⼏种进程调度的算法都有⼀定的局限性。如短进程优先的
    调度算法,仅照顾了短进程⽽忽略了⻓进程 。多级反馈队列调度算法既能使⾼优先级的作业得
    到响应⼜能使短作业(进程)迅速完成。因⽽它是⽬前被公认的⼀种较好的进程调度算法,
    UNIX 操作系统采取的便是这种调度算法。
  • 优先级调度 : 为每个流程分配优先级,⾸先执⾏具有最⾼优先级的进程,依此类推。具有相同
    优先级的进程以 FCFS ⽅式执⾏。可以根据内存要求,时间要求或任何其他资源要求来确定优先
    级。

3. 操作系统内存管理基础

3.1 内存管理介绍

操作系统的内存管理主要是做什么?

操作系统的内存管理主要负责内存的分配与回收(malloc函数:申请内存,free函数:释放内存),另外地址转换也就是将逻辑地址转换成相应的物理地址等功能也是操作系统内存管理做的事情。

3.2 常见的几种内存管理机制

内存管理简单分为:和。

  • 连续分配管理方式:为⼀个⽤户程序分配⼀个连续的内存空间,如块式管理
  • 非连续分配管理方式:允许⼀个程序使⽤的内存分布在离散或者说不相邻的内存中,如页式管理段式管理

  • 块式管理:远古时代的计算机操系统的内存管理⽅式。将内存分为⼏个固定⼤⼩的块,每个块中只包含⼀个进程如果程序运⾏需要内存的话,操作系统就分配给它⼀块,如果程序运⾏只需要很⼩的空间的话,分配的这块内存很⼤⼀部分⼏乎被浪费了。这些在每个块中未被利⽤的空间,我们称之为碎⽚

  • 页式管理:把主存分为⼤⼩相等且固定的⼀⻚⼀⻚的形式,⻚较⼩,相对相⽐于块式管理的划分⼒度更⼤,提⾼了内存利⽤率,减少了碎⽚。⻚式管理通过⻚表对应逻辑地址和物理地址

  • 段式管理 : ⻚式管理虽然提⾼了内存利⽤率,但是⻚式管理其中的⻚实际并⽆任何实际意义段式管理把主存分为⼀段段的,每⼀段的空间⼜要⽐⼀⻚的空间⼩很多。

    但是,最重要的是段是有实际意义的,每个段定义了⼀组逻辑信息, 例如,有主程序段 MAIN、⼦程序段 X、数据段 D及栈段 S 等。 段式管理通过段表对应逻辑地址和物理地址

  • 段⻚式管理机制: 把主存先分成若⼲段,每个段⼜分成若⼲⻚,也就是说 段⻚式管理机制 中段与段之间以及段的内部的都是离散的。

3.3 快表和多级页表

⻚表管理机制中有两个很重要的概念:快表和多级⻚表,这两个东⻄分别解决了⻚表管理中很重要的两个问题。

在分页内存管理中,很重要的两点是:

  1. 虚拟地址到物理地址的转换要快
  2. 解决虚拟地址空间大,页表也会很大的问题

快表

为了解决虚拟地址到物理地址的转换速度,操作系统在页表基础之上引⼊了快表来加速虚拟地址到物理地址的转换。 我们可以把快表理解为⼀种特殊的⾼速缓冲存储器(Cache),其中的内容是⻚表的⼀部分或者全部内容。

作为⻚表的 Cache,它的作⽤与⻚表相似,但是提⾼了访问速率。由于采⽤⻚表做地址转换,读写内存数据时 CPU 要访问两次主存。

有了快表,有时只要访问⼀次⾼速缓冲存储器,⼀次主存,这样可加速查找并提⾼指令执⾏速度。

使⽤快表之后的地址转换流程是这样的:

  1. 根据虚拟地址中的⻚号查快表;
  2. 如果该⻚在快表中,直接从快表中读取相应的物理地址;
  3. 如果该⻚不在快表中,就访问内存中的⻚表,再从⻚表中得到物理地址,同时将⻚表中的该映射
    表项添加到快表中;
  4. 当快表填满后,⼜要登记新⻚时,就按照⼀定的淘汰策略淘汰掉快表中的⼀个⻚。

多级页表

引⼊多级⻚表的主要⽬的是为了避免把全部⻚表⼀直放在内存中占⽤过多空间,特别是那些根本就不需
要的⻚表就不需要保留在内存中。多级⻚表属于时间换空间的典型场景,具体可以查看下⾯这篇⽂章

https://www.polarxiong.com/archives, 多级页表如何节约内存

总结:

为了提⾼内存的空间性能,提出了多级⻚表的概念;

但是提高空间性能是以浪费时间性能为基础的,因此为了补充损失的时间性能,提出了快表(即 TLB)的概念。

不论是快表还是多级⻚表实际上都利⽤到了程序的局部性原理,局部性原理在后⾯的虚拟内存这部分会介绍到。

3.4 分页机制和分段机制的共同点和区别

共同点:

  • 分⻚机制和分段机制都是为了提⾼内存利⽤率,减少内存碎⽚。
  • ⻚和段都是离散存储的,所以两者都是离散分配内存的⽅式。但是,每个⻚和段中的内
    存是连续的。

区别:

  • ⻚的⼤⼩是固定的,由操作系统决定;⽽段的⼤⼩不固定,取决于我们当前运⾏的程序。
  • 分⻚仅仅是为了满⾜操作系统内存管理的需求,⽽段是逻辑信息的单位,在程序中可以体现为代码段,数据段,能够更好满⾜⽤户的需要。

3.5 逻辑(虚拟)地址和物理地址

编程⼀般只有可能和逻辑地址打交道,⽐如在 C 语⾔中,指针⾥⾯存储的数值就可以理解成为内存⾥的⼀个地址,这个地址也就是我们说的逻辑地址,逻辑地址由操作系统决定。

物理地址指的是真实物理内存中地址,更具体⼀点来说就是内存地址寄存器中的地址。

物理地址是内存单元真正的地址。

3.6 什么是CPU寻址?为什么需要虚拟地址空间?

现代处理器使⽤的是⼀种称为 虚拟寻址(Virtual Addressing) 的寻址⽅式。

使⽤虚拟寻址, CPU 需要将虚拟地址翻译成物理地址,这样才能访问到真实的物理内存。

实际上完成虚拟地址转换为物理地址转换的硬件是 CPU 中含有⼀个被称为 内存管理单元(Memory Management Unit, MMU) 的硬件。如下图所示:

image-20210519155159927

为什么要有虚拟地址空间呢?

没有虚拟地址空间的时候, 程序都是直接访问和操作的都是物理内存 。但是这样有什么问题呢?

  1. ⽤户程序可以访问任意内存,寻址内存的每个字节,这样就很容易(有意或者⽆意)破坏操作系
    统,造成操作系统崩溃。
  2. 想要同时运⾏多个程序特别困难,⽐如你想同时运⾏⼀个微信和⼀个 QQ ⾳乐都不⾏。为什么
    呢?举个简单的例⼦:微信在运⾏的时候给内存地址 1xxx 赋值后, QQ ⾳乐也同样给内存地址
    1xxx 赋值,那么 QQ ⾳乐对内存的赋值就会覆盖微信之前所赋的值,这就造成了微信这个程序
    就会崩溃。

总的来说:如果直接把物理地址暴露出来的话会带来严重问题,⽐如可能对操作系统造成伤害以及给同
时运⾏多个程序造成困难

通过虚拟地址访问内存有以下优势:

  • 程序可以使⽤⼀系列相邻的虚拟地址来访问物理内存中不相邻的⼤内存缓冲区。
  • 程序可以使⽤⼀系列虚拟地址来访问⼤于可⽤物理内存的内存缓冲区。当物理内存的供应量变⼩
    时,内存管理器会将物理内存⻚(通常⼤⼩为 4 KB)保存到磁盘⽂件。数据或代码⻚会根据需
    要在物理内存与磁盘之间移动。
  • 不同进程使⽤的虚拟地址彼此隔离。⼀个进程中的代码⽆法更改正在由另⼀进程或操作系统使⽤
    的物理内存。

4. 虚拟内存

4.1 什么是虚拟内存?

虚拟内存可以让程序可以拥有超过系统物理内存大小的可⽤内存空间。

另外, 虚拟内存为每个进程提供了⼀个⼀致的、私有的地址空间,它让每个进程产⽣了⼀种⾃⼰在独享主存的错觉(每个进程拥有⼀⽚连续完整的内存空间) 。这样会更加有效地管理内存并减少出错。

虚拟内存是计算机系统内存管理的⼀种技术,我们可以⼿动设置⾃⼰电脑的虚拟内存。

不要单纯认为虚拟内存只是“使⽤硬盘空间来扩展内存“的技术。 虚拟内存的重要意义是它定义了⼀个连续的虚拟地址空间,并且 把内存扩展到硬盘空间

4.2 局部性原理

局部性原理是虚拟内存技术的基础,正是因为程序运⾏具有局部性原理,才可以只装⼊部分程序到内存就开始运⾏。

早在 1968 年的时候,就有⼈指出我们的程序在执⾏的时候往往呈现局部性规律,也就是说在某个较短
的时间段内,程序执⾏局限于某⼀⼩部分,程序访问的存储空间也局限于某个区域。

局部性原理表现在以下两个⽅⾯:

  • 时间局部性:如果程序中的某条指令⼀旦执⾏,不久以后该指令可能再次执⾏;如果某数据被
    访问过,不久以后该数据可能再次被访问。产⽣时间局部性的典型原因,是由于在程序中存在着
    ⼤量的循环操作
  • 空间局部性:⼀旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即
    程序在⼀段时间内所访问的地址,可能集中在⼀定的范围之内,这是因为指令通常是顺序存放、
    顺序执⾏的,数据也⼀般是以向量、数组、表等形式簇聚存储的

时间局部性是通过将近来使⽤的指令和数据保存到⾼速缓存存储器中,并使⽤高速缓存的层次结构实

空间局部性通常是使⽤较⼤的⾼速缓存,并将预取机制集成到⾼速缓存控制逻辑中实现。

虚拟内存技术实际上就是建⽴了 “内存⼀外存”的两级存储器的结构,利⽤局部性原理实现髙速缓存

4.3 虚拟存储器

基于局部性原理,在程序装⼊时,可以将程序的⼀部分装⼊内存,⽽将其他部分留在外存就可以启动
程序执⾏

由于外存往往⽐内存⼤很多,所以我们运⾏的软件的内存⼤⼩实际上是可以⽐计算机系统实际的内存⼤⼩⼤的。

在程序执⾏过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调⼊内存,然后继续执⾏程序。

另⼀⽅⾯,操作系统将内存中暂时不使⽤的内容换到外存上,从⽽腾出空间存放将要调⼊内存的信息。

这样,计算机好像为⽤户提供了⼀个⽐实际内存⼤的多的存储器——虚拟存储器。

实际上,我觉得虚拟内存同样是⼀种时间换空间的策略,你⽤ CPU 的计算时间,⻚的调⼊调出花费的
时间,换来了⼀个虚拟的更⼤的空间来⽀持程序的运⾏。不得不感叹,程序世界⼏乎不是时间换空间就
是空间换时间。

4.4 虚拟内存的技术实现

虚拟内存的实现需要建⽴在离散分配的内存管理⽅式的基础上。 虚拟内存的实现有以下三种⽅式:

  • 请求分⻚存储管理(最常用): 建⽴在分⻚管理之上,为了⽀持虚拟存储器功能⽽增加了请求调⻚功能和
    ⻚⾯置换功能。 请求分⻚存储管理系统中,在作业开始运⾏之前,仅装⼊当前要执⾏的部分段即可运⾏。

假如在作业运⾏的过程中发现要访问的⻚⾯不在内存,则由处理器通知操作系统按照对应的⻚⾯置换算法将相应的⻚⾯调⼊到主存,同时操作系统也可以将暂时不⽤的⻚⾯置换到外存中。

  • 请求分段存储管理: 建⽴在分段存储管理之上,增加了请求调段功能、分段置换功能。 请求分段储存管理⽅式就如同请求分⻚储存管理⽅式⼀样,在作业开始运⾏之前,仅装⼊当前要执⾏的部分段即可运⾏;在执⾏过程中,可使⽤请求调⼊中断动态装⼊要访问但⼜不在内存的程序段;当内存空间已满,⽽⼜需要装⼊新的段时,根据置换功能适当调出某个段,以便腾出空间⽽装⼊新的段。

  • 请求段⻚式存储管理

这⾥多说⼀下?很多⼈容易搞混请求分⻚与分⻚存储管理,两者有何不同呢?

请求分⻚存储管理建⽴在分⻚管理之上。他们的根本区别是是否将程序全部所需的全部地址空间都装⼊
主存,这也是请求分⻚存储管理可以提供虚拟内存的原因,我们在上⾯已经分析过了。

它们之间的根本区别在于是否将⼀作业的全部地址空间同时装⼊主存。请求分⻚存储管理不要求将作业
全部地址空间同时装⼊主存。基于这⼀点,请求分⻚存储管理可以提供虚存,⽽分⻚存储管理却不能提
供虚存。

不管是上⾯那种实现方式,我们⼀般都需要:

  1. ⼀定容量的内存和外存:在载⼊程序的时候,只需要将程序的⼀部分装⼊内存,⽽将其他部分留
    在外存,然后程序就可以执⾏了;
  2. 缺⻚中断: 如果需执⾏的指令或访问的数据尚未在内存(称为缺⻚或缺段),则由处理器通知操
    作系统将相应的⻚⾯或段调⼊到内存,然后继续执⾏程序;
  3. 虚拟地址空间: 逻辑地址到物理地址的变换。

4.5 页面置换算法

虚拟内存管理很重要的⼀个概念就是⻚⾯置换算法。

地址映射过程中,若在⻚⾯中发现所要访问的⻚⾯不在内存中,则发⽣缺⻚中断

缺页中断:就是要访问的⻚不在主存,需要操作系统将其调⼊主存后再进⾏访问。 在这个时候,被
内存映射的⽂件实际上成了⼀个分⻚交换⽂件。

当发⽣缺⻚中断时,如果当前内存中并没有空闲的⻚⾯,操作系统就必须在内存选择⼀个⻚⾯将其移出
内存,以便为即将调⼊的⻚⾯让出空间。

⽤来选择淘汰哪⼀⻚的规则叫做⻚⾯置换算法,我们可以把页面置换算法看成是淘汰页面的规则。

OPT页面置换算法(最佳页面置换算法)

最佳(Optimal, OPT)置换算法所选择的被淘汰页面将是以后永不使⽤的,或者是在最⻓时间内不再被访问的页面,这样可以保证获得最低的缺页率。

但由于⼈们⽬前⽆法预知进程在内存下的若千⻚⾯中哪个是未来最⻓时间内不再被访问的,因而该算法⽆法实现。⼀般作为衡量其他置换算法的方法。

FIFO(First In First Out) ⻚⾯置换算法(先进先出⻚⾯置换算法)

总是淘汰最先进⼊内存的页面,即选择在内存中驻留时间最久的页面进⾏淘汰。

LRU (Least Currently Used)⻚⾯置换算法(最近最久未使⽤⻚⾯置换算法)

LRU算法赋予每个⻚⾯⼀个访问字段,⽤来记录⼀个⻚⾯⾃上次被访问以来所经历的时间 T,当须淘汰⼀个⻚
⾯时,选择现有⻚⾯中其 T 值最⼤的,即最近最久未使⽤的⻚⾯予以淘汰。

如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。

LFU (Least Frequently Used)⻚⾯置换算法(最少使⽤⻚⾯置换算法)

该置换算法选择在之前时期使⽤最少的页面作为淘汰页。

作者

bd160jbgm

发布于

2021-05-19

更新于

2021-05-20

许可协议