操作系统

246 阅读9分钟

前言

操作系统是计算机专业中非常核心也是很基础的课程,但是很多人在学习编程的过程中却忽略了对它的学习,我当时因为疫情在家上网课,所以没有认真学习这门课程。近日有些空闲时间,于是重温了哈工大李治军老师的《操作系统》课程,以此留下学习记录。

一.概述

1.1 什么是操作系统?

操作系统就是一个管理计算机硬件资源的一种软件。它的位置在硬件之上,用户软件之下,可以帮助我们的应用软件更好的进行调度系统硬件资源。

image.png

1.2 操作系统的发展过程

  • 上古神机IBM7094,造价在250万美元以上。

一台计算机只用做一件事情,批处理操作系统,专注于计算,没有多进程的概念。

  • OS/360(1965-1980)

需要让一台计算机做多件事情,有多个程序运行。作业之间的调度和切换称为核心,多进程结构和进程管理开始萌芽。

  • MULTICS

每个人启动一个作业,作业之间快速切换。出现了分时系统的概念,核心仍然是线程之间的切换。

  • UNIX(1980-1990)

个人计算机的普及,让操作系统的出现更加必要。Ken Thompson,Dennis Ritchi两人开发。

  • Linux(1990-2000)

Linus Torvalds编写了Linux.

1.3 操作系统能够解决的问题?

  1. 屏蔽掉硬件调用的接口,为应用程序员提供了调用硬件资源更好,更简单,更清晰的模型(系统调用接口)。
  2. 管理计算机的CPU,让CPU能够更加高效的进行工作(多进程切换运行)。
  3. 管理计算机的内存,让内存的使用更加充分(虚拟内存)。

二.操作系统管理CPU

管理CPU的核心就是要CPU运行的更加的高效。那cpu是如何工作的呢?

2.1 cpu的工作方式

基本知识

程序在我们的计算机中是如何进行存储的呢?就是一段一段的指令!而计算机运行程序也就是cpu把这个程序的指令从磁盘中加载到内存中,然后读取程序的指令并运行的过程!

CPU执行过程image.png

CPU的执行就是不断的取指令运行的过程,如果在取指令的过程中存在一条读文件的操作,那么就需要访问磁盘,进行I/O操作。由于磁盘IO的速度和CPU执行的速度相比非常慢,所以CPU在这种情况下就会空闲,直到磁盘IO完成,从而导致CPU的利用率低下。

image.png

这种情况下指令是一直保持顺序执行的

所以操作系统就引入了一个多进程执行的概念,这样来执行提高CPU的效率。

image.png

image.png

等到程序1的磁盘io结束以后,会发出一个中断请求,这时cpu又从程序2切换回程序1继续执行,因为cpu没有处于等待的状态,一直在运行,这样就大大减少了cpu空闲的时间,提高了cpu的效率!

问题1. pc切换进程的时候,如何保存当前进行的执行信息呢?

使用PCB进程控制块,该进程运行的所有信息都会保存在这个结构当中,当切换到这个进程的时候,就会通过保存的PCB恢复进程的运行信息。

问题2. 多进程有哪些调度的方式

1. 先来先服务算法(FCFS)
用一个队列来维护任务列表,按照顺序依次运行。

2. 时间片轮转算法
每隔一段固定的时间进行线程的切换。

3. 短任务优先算法

核心思想还是不通的进程有不同的优先级,而短任务的优先级要比长任务的优先级更高,而我们总是安排高优先级的任务运行。

4. 优先级调度算法
给每一个进程分配一个优先级,每次需要进程切换时,找一个优先级最高的进程进行切换。

问题3. 进程和线程的关系

  1. 一个进程往往有多个线程,一个线程只属于一个进程。
  2. 同一进程的多个线程可以共享进程的资源。
  3. 进程是操作系统分配资源的最小单位,线程是CPU执行的最小单位。

问题4. 内核级线程和用户级线程的关系

每一个线程都有自己的执行栈,这样当执行线程跳转的时候不会出现跳转到其他线程中的错误。当用户级线程经过调用而陷入内核的时候,就会调用核心级线程。
核心级线程也对应着一个栈,但是核心级线程还必须关联着它在用户级下的线程。当核心线程切换的时候,用户线程也必须跟着进行切换。

用户级线程:不需要内核的支持而在用户程序中实现的线程,它的内核的切换时用户态程序自己控制内核的切换,不需要内核的干涉,但是它不能像内核级线程一样更好的运用多核CPU,内核对用户级线程不能感知
内核级线程:切换由内核控制,当线程进行切换的时候,会从用户态转换为内核态,切换完毕又要从内核态转变为用户态。可以很好的运用多核CPU。

三. 操作系统管理内存

如何让内存使用起来?

我们都知道,程序都是存储在磁盘中的。当我们要运行一个程序的时候,就会把程序加载到内存中,然后cpu取指运行程序。磁盘的容量是很大的,可以完整的存储程序,但是我们内存的容量是非常有限的,并且当我们运行一个程序的时候往往只需要其中的一些功能,而不是全部功能,所以需要把程序的全部代码都加载到内存中吗?

3.1 分段

在实际中,对程序员来说一个完整的程序往往被分为多个不同的段(如数据段,变量集,函数等等)。在程序载入内存的时候,程序以段为单位载入内存,每个段都有一个pc执行单元向下执行。
那这些段是载入到程序的哪些区域呢?如何找到指令所在的地址呢?
这就涉及到了相应的内存分配算法:1. 首先适配 2. 最佳适配 3.最差适配。但是这种分配算法存在缺陷,就是会产生大量的内存碎片,从而当需要进行大数据存放的时候没有完整的内存空间进行分配。 找到指令的地址也简单呀,给每一个段加一个段地址,每条指令有一个偏移地址,此时该条指令的物理地址=段地址+偏移地址

但是现在分区又存在一个问题,当空闲内存区域的长度不够分配怎么办?比如现在有一个进程需要100k内存,但是现在空闲内存只有50k和80k,两个合起来就够用,但是分开就不够用,造成空间的浪费,所以引入了一个分页的概念。

3.2分页

为了解决分段所造成的内存碎片问题,我们引入了分页概念。就是将内存作为固定的大小如4k再分为一页一页的形式,在分配程序内存的时候,计算它需要多少页内存,直接分配给对应大小的页即可,使用一张分页表来记录每个页分配的情况,并提供了从虚拟内存到物理内存的映射。

这样分页也会存在问题,如果每一页的内存设置的小的话那么确实减少了内存的浪费,但是极大的增加了页表所占的空间。

解决方案:多级页表。 这样的话空间利用率就高了,但是时间利用率也不是很高,所以又使用快表(TLB)。作用就是页表的cache中存储了当前最有可能被访问到的页表项。只有在TLB无法完成地址翻译任务时,才会到内存中去查询页表,这样就减少了页表查询导致的处理器性能下降。

3.3段页结合(虚拟内存)

在实际使用中我们都是使用的段页结合的方式,在段和内存之间引入虚拟内存的概念。实际内存的分配对用户是不可见的,段的分配对用户来说是可见的。

3.4页面换入换出

当用户访问一个页时,MMU会去查询一个操作系统维护的页表,看看用户访问的页是否存在于内存当中,如果存在,就直接调用;如果没有,就需要开启中断,执行页错误处理程序,在磁盘中找到相应的数据页,并在内存中找到一块空闲,将其载入进来,然后中断返回,执行用户程序,在去访问内存中用户需要的页。
当我们不断地将页面换入内存当中,内存肯定会不足的,我们需要选择一些页面进行换出,为新的数据腾出空间,但是我们应该选择那些页面换出呢?

3.5页面置换算法

FIFO算法: 将最先进入的页面置换出去,让新的页面加载进来。

LRU(Least Recently Used)算法 : 将最近最少使用的页面置换出去,让新的页面加载进入。