零基础学习操作系统:从进程、线程到内存和文件系统,一篇文章讲明白

很多人第一次学习操作系统时,会觉得里面全是抽象概念:

  • 进程和线程有什么区别?

  • CPU 为什么可以同时运行这么多程序?

  • 内存明明只有 16GB,为什么程序使用的是虚拟地址?

  • 文件删除之后,数据真的消失了吗?

  • 用户态和内核态到底是什么意思?

这些问题看起来零散,其实都围绕着同一个核心:

操作系统负责管理计算机中的硬件资源,并为程序提供一个安全、稳定、方便的运行环境。

只要先抓住这条主线,再去理解进程、内存、文件系统等知识,操作系统就不会那么难。


一、操作系统到底是什么

一台计算机中,最重要的硬件主要包括:

  • CPU:负责执行指令

  • 内存:临时保存正在使用的数据

  • 硬盘:长期保存文件和程序

  • 键盘、鼠标、显示器:负责输入和输出

  • 网卡:负责网络通信

应用程序如果想运行,就必须使用这些硬件资源。

例如,浏览器打开一个网页时,需要:

  1. 使用 CPU 执行代码;

  2. 使用内存保存网页数据;

  3. 使用网卡接收服务器返回的内容;

  4. 使用显卡和显示器展示页面;

  5. 使用硬盘保存缓存文件。

问题在于,应用程序不能直接随意控制硬件。

如果每个程序都可以直接操作 CPU、内存和硬盘,那么很容易出现混乱。

例如:

  • 一个程序占用全部 CPU,其他程序无法运行;

  • 一个程序修改了另一个程序的内存;

  • 两个程序同时修改同一个文件,导致文件损坏;

  • 普通程序直接控制硬件,造成系统崩溃。

因此,计算机需要一个统一的管理者,这个管理者就是操作系统。

操作系统主要负责四件事:

  1. 管理 CPU;

  2. 管理内存;

  3. 管理文件和磁盘;

  4. 管理各种输入输出设备。

Windows、Linux、macOS、Android 和 iOS 都属于操作系统。


二、程序是怎么运行起来的

我们平时写好的代码,最终都会以文件的形式保存在硬盘中。

例如:

hello.py
main.exe
app.jar

这些文件没有运行时,只是一段静态数据。

当我们双击程序,或者在命令行中执行程序时,操作系统会完成下面几个步骤:

  1. 从硬盘中读取程序文件;

  2. 将程序需要的代码和数据加载到内存;

  3. 为程序分配运行所需的资源;

  4. 创建一个进程;

  5. 让 CPU 开始执行程序中的指令。

因此,程序和进程并不是同一个概念。

程序

程序是保存在硬盘中的静态文件。

它包含:

  • 程序代码;

  • 初始数据;

  • 程序运行所需的信息。

进程

进程是正在运行的程序。

进程不仅包含代码,还包含:

  • 当前执行到了哪条指令;

  • 使用了多少内存;

  • 打开了哪些文件;

  • 创建了哪些线程;

  • 使用了哪些系统资源。

可以把程序理解成一份菜谱,把进程理解成正在按照菜谱做菜的过程。

同一份菜谱可以被使用多次,因此同一个程序也可以同时创建多个进程。

例如,同时打开两个记事本窗口,可能会创建两个记事本进程。


三、什么是进程

进程是操作系统管理程序运行的重要单位。

每个进程通常都有自己相对独立的运行空间。

例如,浏览器进程和音乐播放器进程各自拥有自己的内存。

正常情况下,浏览器不能随便读取音乐播放器的内存,音乐播放器也不能直接修改浏览器中的数据。

这样做有两个好处:

1. 提高安全性

一个程序不能随便查看另一个程序中的数据。

例如,普通程序不能直接读取密码管理软件中的内存内容。

2. 提高稳定性

一个程序崩溃时,通常不会直接导致其他程序一起崩溃。

例如,记事本突然关闭,一般不会导致浏览器也跟着关闭。


进程的几种状态

一个进程从创建到结束,不会一直占用 CPU。

它通常会在不同状态之间切换。

创建状态

操作系统正在为进程分配资源,还没有正式开始运行。

就绪状态

进程已经准备好运行,但是还没有获得 CPU。

可以理解为已经排好队,等待 CPU 处理。

运行状态

进程正在使用 CPU 执行指令。

阻塞状态

进程暂时无法继续执行,需要等待某个事件完成。

例如:

  • 等待用户输入;

  • 等待文件读取完成;

  • 等待网络返回数据;

  • 等待某个锁被释放。

结束状态

进程已经执行完成,操作系统开始回收它使用的资源。

进程状态之间的变化大致如下:

创建 → 就绪 → 运行 → 结束
              ↓
             阻塞
              ↓
             就绪

假设一个程序需要读取硬盘文件。

程序运行到读取文件的代码时,硬盘读取需要一定时间。CPU 没有必要一直等待,于是操作系统会先让这个进程进入阻塞状态,再把 CPU 分配给其他进程。

等文件读取完成后,这个进程会重新进入就绪状态,等待再次获得 CPU。


四、CPU 为什么可以同时运行很多程序

我们平时可能同时打开:

  • 浏览器;

  • 微信;

  • 音乐播放器;

  • 代码编辑器;

  • 文件管理器。

看起来这些程序都在同时运行。

但一个 CPU 核心在某一个极短的时刻,通常只能执行一个线程中的指令。

之所以看起来像同时运行,是因为操作系统会快速切换 CPU 当前执行的任务。

例如:

浏览器运行 5 毫秒
微信运行 5 毫秒
音乐播放器运行 5 毫秒
代码编辑器运行 5 毫秒
然后再回到浏览器

由于切换速度非常快,人感觉不到中间的停顿,于是看起来像多个程序同时运行。

这种在多个任务之间快速切换的方式,叫作并发。


并发和并行的区别

这两个概念很容易混淆。

并发

多个任务在一段时间内交替执行。

例如,一个 CPU 核心在浏览器和音乐播放器之间快速切换。

并行

多个任务在同一时刻真正同时执行。

例如,一台电脑有多个 CPU 核心,一个核心运行浏览器,另一个核心运行音乐播放器。

可以这样理解:

  • 并发是一个人同时处理多件事情,来回切换;

  • 并行是多个人分别处理不同事情。

现代计算机通常既存在并发,也存在并行。


五、什么是 CPU 调度

多个进程都想使用 CPU,但 CPU 数量有限。

操作系统必须决定:

  • 下一个运行哪个进程;

  • 每个进程运行多长时间;

  • 什么时候暂停当前进程;

  • 是否应该优先运行某些重要任务。

这个过程叫作 CPU 调度。

负责选择下一个运行任务的程序,叫作调度器。


1. 先来先服务

谁先进入队列,谁先运行。

例如:

任务 A 需要运行 20 秒
任务 B 需要运行 2 秒
任务 C 需要运行 1 秒

如果顺序是 A、B、C,那么 B 和 C 即使很快就能完成,也必须等待 A 运行结束。

这种方法简单,但短任务可能等待很长时间。


2. 短任务优先

优先运行执行时间较短的任务。

上面的三个任务会按照:

C → B → A

的顺序运行。

这样可以减少平均等待时间,但也存在问题。

如果系统不断出现短任务,长任务可能一直得不到运行机会。


3. 时间片轮转

操作系统给每个进程分配一个很短的运行时间,这段时间叫作时间片。

例如:

A 运行 10 毫秒
B 运行 10 毫秒
C 运行 10 毫秒
然后再次轮到 A

如果某个进程在时间片内没有执行完成,就先暂停,等待下一轮。

这种方式比较公平,也是现代操作系统中非常重要的调度思想。


六、什么是上下文切换

当 CPU 从进程 A 切换到进程 B 时,不能直接把 A 丢在一边。

操作系统需要保存进程 A 当前的运行信息,例如:

  • 执行到了哪条指令;

  • CPU 寄存器中的数据;

  • 当前使用的栈位置;

  • 进程的状态信息。

之后,当进程 A 再次获得 CPU 时,操作系统会恢复这些信息,让它从之前暂停的位置继续运行。

这个保存旧任务状态、加载新任务状态的过程,叫作上下文切换。

上下文切换本身需要时间。

因此,并不是线程和进程越多越好。

如果任务数量过多,CPU 可能会频繁切换,大量时间都消耗在保存和恢复状态上,真正执行任务的时间反而减少。


七、什么是线程

进程负责提供资源,线程负责执行任务。

一个进程中至少有一个线程,也可以有多个线程。

例如,一个浏览器进程中可能同时存在:

  • 一个线程负责显示页面;

  • 一个线程负责下载文件;

  • 一个线程负责处理鼠标点击;

  • 一个线程负责播放视频;

  • 一个线程负责执行 JavaScript。

这些线程共同使用浏览器进程中的部分资源。


进程和线程的区别

可以把进程理解成一家公司,把线程理解成公司里的员工。

公司拥有:

  • 办公室;

  • 文件;

  • 设备;

  • 资金。

员工在公司内部执行具体工作。

对应到计算机中:

  • 进程拥有内存、文件等资源;

  • 线程负责真正执行代码;

  • 同一进程中的线程可以共享进程资源。

对比内容 进程 线程
作用 管理资源 执行任务
内存 通常相对独立 共享进程内存
创建成本 较高 较低
切换成本 较高 相对较低
相互影响 相对较小 一个线程出错可能影响整个进程

线程的优点是轻量、切换快、共享数据方便。

但是共享数据也会带来新的问题。


八、多线程为什么会出现数据错误

假设有一个变量:

count = 0

现在有两个线程,都需要执行:

count = count + 1

很多人会认为,两个线程执行之后,结果一定是 2。

但这条代码在 CPU 中可能会被分成多个步骤:

  1. 读取 count;

  2. 计算 count + 1;

  3. 把结果写回 count。

假设线程 A 和线程 B 同时读取了 count。

此时两个线程读取到的值都是 0。

接下来:

线程 A:0 + 1 = 1
线程 B:0 + 1 = 1

最后两个线程都把 1 写回 count。

最终结果就可能是 1,而不是 2。

这类问题叫作竞态条件。

它出现的原因是:

多个线程同时操作共享数据,最终结果受到执行顺序影响。


什么是锁

为了避免多个线程同时修改共享数据,可以使用锁。

线程在修改数据之前,先获取锁。

获取锁成功的线程可以继续执行,其他线程必须等待。

大致过程如下:

线程 A 获取锁
线程 A 修改 count
线程 A 释放锁

线程 B 获取锁
线程 B 修改 count
线程 B 释放锁

这样可以保证同一时刻只有一个线程修改 count。

不过,锁也不能随便使用。

如果锁的范围太大,会导致很多线程长时间等待,降低程序运行效率。


九、什么是死锁

死锁是多个线程或进程互相等待,导致所有任务都无法继续执行。

假设有两把锁:

  • 锁 A;

  • 锁 B。

现在有两个线程:

线程 1 已经拿到了锁 A,正在等待锁 B
线程 2 已经拿到了锁 B,正在等待锁 A

线程 1 不释放锁 A,因为它还在等锁 B。

线程 2 不释放锁 B,因为它还在等锁 A。

两个线程会一直等待下去,这就是死锁。

可以类比成:

  • 小明拿着笔,但需要尺子才能完成作业;

  • 小红拿着尺子,但需要笔才能完成作业;

  • 两个人都不愿意先把自己手里的工具交出去;

  • 最终谁都无法完成作业。

解决死锁的常见思路包括:

  • 按照固定顺序获取锁;

  • 获取不到锁时主动释放已经持有的锁;

  • 为等待设置超时时间;

  • 尽量减少同时持有多把锁的情况。

初学阶段不需要研究复杂的死锁算法,先理解“互相等待资源”这个核心即可。


十、什么是用户态和内核态

操作系统中有很多非常重要的操作,例如:

  • 修改内存映射;

  • 控制硬盘;

  • 创建进程;

  • 管理网络;

  • 修改文件权限;

  • 关闭系统。

这些操作不能让普通程序随便执行。

因此,CPU 会把程序运行状态分为不同权限等级。

最常见的是:

  • 用户态;

  • 内核态。


用户态

普通应用程序通常运行在用户态。

例如:

  • 浏览器;

  • 微信;

  • Python 程序;

  • 代码编辑器。

用户态权限受到限制,不能直接控制硬件,也不能随便访问操作系统核心数据。


内核态

操作系统内核运行在内核态。

内核态拥有更高权限,可以:

  • 操作硬件;

  • 管理内存;

  • 创建进程;

  • 控制文件系统;

  • 执行设备驱动程序。

这样设计的主要目的是保护系统。

如果普通程序拥有全部权限,一个小错误就可能直接导致整个操作系统崩溃。


十一、什么是系统调用

普通程序运行在用户态,无法直接控制硬件。

但是程序又需要读取文件、访问网络、创建进程。

这时,程序可以向操作系统提出请求。

这种由应用程序向操作系统内核请求服务的方式,叫作系统调用。

例如,一个程序想读取文件,大致会经历:

  1. 程序调用读取文件的函数;

  2. 程序通过系统调用进入内核;

  3. 操作系统检查文件是否存在;

  4. 操作系统检查程序是否有读取权限;

  5. 操作系统控制硬盘读取数据;

  6. 数据返回给程序;

  7. 程序继续在用户态运行。

常见的系统调用功能包括:

  • 创建进程;

  • 结束进程;

  • 打开文件;

  • 读取文件;

  • 写入文件;

  • 申请内存;

  • 发送网络数据。

可以把系统调用理解成应用程序向操作系统提交的办事申请。

普通程序不能直接进入仓库拿东西,只能向管理员提出请求,由管理员检查后完成操作。


十二、什么是中断

CPU 正在运行程序时,外部设备可能突然发生事件。

例如:

  • 用户按下键盘;

  • 鼠标发生移动;

  • 网卡收到数据;

  • 硬盘完成读取;

  • 定时器到达指定时间。

CPU 不可能一直主动询问每个设备:

键盘有输入吗?
鼠标动了吗?
网卡收到数据了吗?
硬盘读完了吗?

这样会浪费大量时间。

更合理的方式是,设备在事件发生时主动通知 CPU。

这种通知机制叫作中断。

当中断发生时,CPU 会暂时保存当前程序的状态,转去执行对应的处理程序。

处理完成后,再回来继续执行原来的任务。

例如,用户按下键盘后:

  1. 键盘产生中断;

  2. CPU 暂停当前任务;

  3. 操作系统读取按键信息;

  4. 操作系统把按键信息交给应用程序;

  5. CPU 恢复原来的任务。

中断让 CPU 不需要一直等待设备,提高了系统效率。


十三、内存为什么重要

CPU 的运行速度很快,而硬盘的读取速度相对较慢。

如果 CPU 每执行一条指令,都要从硬盘读取数据,计算机会非常慢。

因此,程序运行时,需要先把代码和数据加载到内存中。

内存的特点是:

  • 读取速度快;

  • 容量比硬盘小;

  • 断电后数据通常会消失;

  • 用来保存正在运行的程序和临时数据。

例如,当你打开浏览器时,浏览器程序从硬盘加载到内存。

当你关闭浏览器后,操作系统会回收浏览器占用的内存。


十四、操作系统怎样管理内存

假设电脑有 16GB 内存,同时运行了多个程序:

浏览器占用 3GB
代码编辑器占用 1GB
微信占用 500MB
操作系统占用 4GB
其他程序占用若干空间

操作系统需要记录:

  • 哪些内存已经被使用;

  • 哪些内存仍然空闲;

  • 每一块内存属于哪个进程;

  • 进程结束后应该回收哪些内存。

如果没有操作系统统一管理,多个程序可能会把数据写到同一个位置,导致数据互相覆盖。

因此,每个进程通常不能直接使用真实物理内存地址,而是使用虚拟地址。


十五、什么是虚拟内存

虚拟内存并不是简单地“拿硬盘当内存”。

更准确地说:

虚拟内存是操作系统为每个进程提供的一套独立、连续的虚拟地址空间。

程序看到的是虚拟地址,真正访问内存时,操作系统和硬件会把虚拟地址转换成物理地址。

假设程序认为自己使用了下面这些地址:

地址 0
地址 1
地址 2
地址 3

但这些地址在真实内存中可能并不连续:

虚拟地址 0 → 物理内存 1000
虚拟地址 1 → 物理内存 5000
虚拟地址 2 → 物理内存 8000
虚拟地址 3 → 物理内存 9000

程序不需要知道真实数据放在哪里。

它只需要使用虚拟地址,地址转换工作由操作系统和 CPU 完成。


虚拟内存有什么好处

1. 隔离不同进程

不同进程可以使用相同的虚拟地址,但最终映射到不同的物理内存位置。

这样可以避免程序互相破坏数据。

2. 简化程序开发

程序不需要关心自己被加载到物理内存的哪个位置。

在程序看来,自己拥有一块连续、完整的地址空间。

3. 提高内存利用率

程序中暂时不用的数据,不一定要一直放在物理内存中。

操作系统可以只加载当前真正需要的部分。

4. 支持比物理内存更大的地址空间

当物理内存不足时,操作系统可以把暂时不用的数据放到硬盘中,腾出内存空间。

但硬盘速度比内存慢得多,因此频繁交换数据会导致电脑明显卡顿。


十六、什么是分页

操作系统通常不会把整个程序一次性放进一块连续的物理内存中。

它会把虚拟内存划分成大小固定的小块,这些小块叫作页。

物理内存也会被划分成相同大小的小块,通常叫作页框。

例如:

虚拟页 0 → 物理页框 5
虚拟页 1 → 物理页框 8
虚拟页 2 → 物理页框 2

这些映射关系保存在页表中。

当程序访问某个虚拟地址时,CPU 会通过页表找到对应的物理地址。

分页的好处是,程序不需要占用一整块连续的物理内存。

只要系统中存在足够数量的空闲页框,程序就可以运行。


十七、什么是缺页

程序运行时,并不是所有页面都已经加载到物理内存中。

当程序访问一个尚未加载到内存的页面时,会发生缺页。

缺页并不一定是程序错误,它可能是正常现象。

操作系统会进行下面的处理:

  1. 暂停当前程序;

  2. 查找需要的页面;

  3. 从硬盘中读取页面;

  4. 把页面加载到物理内存;

  5. 修改页表;

  6. 让程序继续执行。

如果物理内存已经没有空闲空间,操作系统还需要选择一个暂时不用的页面,把它移出内存。

这就是页面置换。

如果程序频繁发生缺页,系统就需要不断在内存和硬盘之间交换数据,运行速度会明显下降。


十八、栈和堆是什么

程序运行时,经常会听到栈内存和堆内存。

它们都是进程内存空间中的一部分。


栈主要用于保存:

  • 函数参数;

  • 局部变量;

  • 函数返回位置;

  • 函数调用过程中的临时数据。

例如:

def add(a, b):
    result = a + b
    return result

参数 ab 和局部变量 result 通常与栈有关。

栈的特点是:

  • 分配速度快;

  • 自动管理;

  • 空间通常较小;

  • 函数结束后,相关空间会自动释放。


堆主要用于保存程序动态申请的数据。

例如,程序运行过程中创建一个很大的对象或数组,通常会使用堆空间。

堆的特点是:

  • 空间通常更大;

  • 使用方式更灵活;

  • 管理成本更高;

  • 某些语言需要程序员手动释放;

  • 某些语言由垃圾回收机制自动管理。

如果程序不断申请堆内存,却不释放不再使用的数据,就可能出现内存泄漏。


十九、什么是内存泄漏

内存泄漏是指:

程序已经不再需要某块内存,但这块内存没有被正确释放。

假设一个程序每处理一次请求,就申请 10MB 内存,但处理完成后没有释放。

随着运行时间增加:

第一次占用 10MB
第二次占用 20MB
第三次占用 30MB
……

最终程序占用的内存越来越大,可能导致:

  • 程序变慢;

  • 系统开始频繁使用虚拟内存;

  • 其他程序无法申请内存;

  • 程序被操作系统强制终止。

内存泄漏不代表数据从内存中“漏出去”,而是指内存被占用后无法重新使用。


二十、文件系统是什么

硬盘本身只负责保存大量二进制数据。

它并不知道什么是:

  • 文件名;

  • 文件夹;

  • 图片;

  • 视频;

  • 文档;

  • 文件权限。

文件系统负责把硬盘中的数据组织成我们熟悉的文件和目录。

常见文件系统包括:

  • Windows 中的 NTFS;

  • Linux 中的 ext4;

  • macOS 中的 APFS;

  • U 盘常见的 FAT32 和 exFAT。

文件系统主要负责:

  1. 记录文件名;

  2. 记录文件大小;

  3. 记录文件保存位置;

  4. 管理目录结构;

  5. 管理文件权限;

  6. 管理磁盘空闲空间。


二十一、文件名和文件内容不是一回事

很多人认为文件名就是文件本身。

实际上,一个文件通常可以分成两部分:

  1. 文件的元数据;

  2. 文件的实际内容。

元数据包括:

  • 文件名;

  • 文件大小;

  • 创建时间;

  • 修改时间;

  • 所属用户;

  • 访问权限;

  • 数据在磁盘中的位置。

实际内容则是文件保存的数据。

例如,一个文本文件的内容可能是:

Hello World

文件系统会记录这些文字被保存在哪些磁盘区域中。

当程序打开文件时,操作系统先根据文件名找到元数据,再根据元数据找到实际内容。


二十二、文件夹到底是什么

文件夹也叫目录。

目录本质上是一种特殊的数据结构,用来记录文件名与文件信息之间的关系。

例如:

学习资料
├── 操作系统.md
├── 计算机网络.md
└── 数据结构.md

目录中保存的是:

  • 文件名;

  • 文件对应的标识;

  • 子目录信息。

因此,移动一个文件到另一个文件夹时,有时并不需要真正移动文件内容,只需要修改目录中的记录。

这也是为什么在同一个磁盘分区中移动大文件通常很快。


二十三、绝对路径和相对路径

路径用于描述文件的位置。

绝对路径

从文件系统的起点开始描述完整位置。

Windows 示例:

C:\Users\DengKai\Documents\os.md

Linux 示例:

/home/dengkai/documents/os.md

相对路径

相对于当前所在目录描述位置。

例如,当前目录是:

/home/dengkai

那么文件可以写成:

documents/os.md

程序报“找不到文件”时,很多情况不是文件不存在,而是当前工作目录与预期不同,导致相对路径解析错误。


二十四、删除文件后发生了什么

删除文件时,操作系统通常不会立刻把文件的每一个字节都清空。

更常见的做法是:

  1. 删除目录中的文件记录;

  2. 把文件占用的磁盘空间标记为空闲;

  3. 以后允许其他数据覆盖这些空间。

因此,在数据还没有被覆盖之前,某些恢复工具可能还能找回文件内容。

这也说明:

普通删除并不等于数据已经被彻底清除。

如果需要彻底删除重要数据,仅仅清空回收站可能不够,还需要使用安全擦除等方式。


二十五、什么是文件权限

操作系统不能让所有用户随便读取和修改所有文件。

因此,文件系统会记录文件权限。

在 Linux 中,常见权限包括:

  • r:读取;

  • w:写入;

  • x:执行。

使用下面的命令:

ls -l

可能看到:

-rw-r--r-- 1 user user 1200 Jul 31 notes.txt

其中:

rw-

表示文件所有者可以读取和写入。

后面的:

r--

表示其他用户只能读取,不能修改。

权限机制可以保护:

  • 系统文件;

  • 用户隐私;

  • 程序安全;

  • 多用户环境中的数据。


二十六、什么是输入输出

输入输出通常写作 I/O,也就是 Input/Output。

输入包括:

  • 键盘输入;

  • 鼠标操作;

  • 从硬盘读取文件;

  • 从网络接收数据。

输出包括:

  • 在屏幕上显示内容;

  • 向文件写入数据;

  • 向网络发送数据;

  • 向打印机发送内容。

CPU 速度很快,而硬盘、网络和键盘等设备速度相对较慢。

如果 CPU 每次进行 I/O 时都原地等待,会浪费大量计算能力。

因此,操作系统通常会让等待 I/O 的进程进入阻塞状态,再让其他进程使用 CPU。

等 I/O 完成后,设备通过中断通知操作系统,原来的进程再继续运行。


二十七、为什么需要缓冲区

假设程序每产生一个字符,就立刻向硬盘写入一次。

如果要写入一万个字符,就可能进行一万次磁盘操作。

磁盘操作的成本比内存操作高得多。

更好的方法是:

  1. 先把数据保存到内存缓冲区;

  2. 等缓冲区积累一定数据;

  3. 再一次性写入硬盘。

这种临时保存数据的内存区域叫作缓冲区。

缓冲区可以减少设备操作次数,提高程序运行效率。

但缓冲区也会带来一个现象:

程序执行了写入操作,数据不一定已经真正写入硬盘。

有时数据还停留在内存缓冲区中。

如果电脑突然断电,这部分数据可能丢失。


二十八、缓存和缓冲有什么区别

缓存和缓冲经常一起出现,但作用并不完全相同。

缓冲

主要用于解决生产速度和消费速度不一致的问题。

例如,程序生成数据很快,硬盘写入较慢,可以先把数据放进缓冲区。

缓存

主要用于保存之前使用过的数据,避免下次重复读取或计算。

例如,浏览器把图片保存在缓存中,下次打开网页时就不需要重新下载。

可以简单理解为:

  • 缓冲是先临时放一下;

  • 缓存是保存下来以后再用。


二十九、设备驱动程序是什么

不同硬件的控制方式不同。

例如:

  • 不同品牌显卡;

  • 不同型号打印机;

  • 不同网卡;

  • 不同声卡。

操作系统不可能在核心代码中直接写死所有设备的控制方法。

因此,硬件厂商通常会提供设备驱动程序。

驱动程序负责把操作系统的通用请求转换成硬件能够理解的操作。

例如,操作系统提出:

请打印这份文档

打印机驱动程序会把这个请求转换成具体打印机型号能够识别的命令。

因此,驱动程序可以理解为操作系统与硬件之间的翻译器。


三十、操作系统启动时发生了什么

电脑刚开机时,内存中还没有操作系统。

操作系统一般保存在硬盘中,因此需要一个启动过程把它加载到内存。

大致过程如下:

  1. 按下开机键;

  2. CPU 从固定位置开始执行固件程序;

  3. BIOS 或 UEFI 检查硬件;

  4. 找到可以启动的磁盘;

  5. 加载引导程序;

  6. 引导程序加载操作系统内核;

  7. 内核初始化内存、CPU 和设备;

  8. 启动系统服务;

  9. 显示登录界面或桌面。

操作系统启动完成后,才开始管理各种应用程序。


三十一、为什么程序会崩溃

程序崩溃可能有很多原因,例如:

  • 访问了不允许访问的内存;

  • 除以零;

  • 使用了已经释放的内存;

  • 栈空间耗尽;

  • 文件或资源不存在;

  • 多线程数据冲突;

  • 程序本身存在逻辑错误。

当程序执行危险操作时,操作系统会进行保护。

例如,一个程序试图访问不属于自己的内存,CPU 和操作系统会阻止它,并结束这个程序。

这样可以避免一个普通程序破坏整个系统。

所以,程序崩溃有时正说明操作系统的隔离和保护机制正在正常工作。


三十二、为什么电脑会越来越卡

电脑卡顿可能与多个操作系统资源有关。

1. CPU 使用率过高

某个程序进行了大量计算,或者程序陷入死循环。

2. 内存不足

运行的程序太多,物理内存不够,系统开始频繁使用硬盘作为虚拟内存。

3. 磁盘读写过多

程序不断读取或写入文件,导致磁盘长时间繁忙。

4. 进程和线程过多

系统频繁进行上下文切换,真正执行任务的时间减少。

5. 后台程序过多

很多程序开机自动启动,在后台持续占用资源。

遇到电脑卡顿时,可以打开任务管理器观察:

  • CPU 占用率;

  • 内存占用率;

  • 磁盘占用率;

  • 网络占用率;

  • 哪个进程占用资源最多。

这样比直接重启电脑更容易找到问题原因。


三十三、用任务管理器观察操作系统

在 Windows 中,可以按下:

Ctrl + Shift + Esc

打开任务管理器。

可以重点观察以下内容。

进程

查看当前有哪些程序正在运行。

CPU

查看哪些进程正在大量使用 CPU。

内存

查看每个进程占用了多少内存。

磁盘

查看哪些进程正在大量读取或写入磁盘。

网络

查看哪些进程正在发送或接收网络数据。

线程数

在详细信息中可以观察某些进程包含多少线程。

通过任务管理器,可以把很多抽象概念和真实程序对应起来。

例如:

  • 打开浏览器后,观察新增了哪些进程;

  • 播放视频时,观察 CPU 和网络变化;

  • 打开大型软件时,观察内存变化;

  • 下载文件时,观察网络和磁盘变化;

  • 关闭程序后,观察资源是否被释放。


三十四、用 Linux 命令观察操作系统

Linux 提供了很多查看系统状态的命令。

查看当前进程

ps

查看全部进程

ps aux

动态查看进程状态

top

查看内存使用情况

free -h

查看磁盘空间

df -h

查看当前目录

pwd

查看目录内容

ls

查看文件权限

ls -l

查看进程编号

ps aux | grep 程序名

结束进程

kill 进程号

学习这些命令时,不要只记命令格式,要把它们和操作系统知识联系起来。

例如:

  • ps 对应进程管理;

  • top 对应 CPU 调度和资源占用;

  • free 对应内存管理;

  • df 对应文件系统和磁盘管理;

  • kill 对应进程控制;

  • ls -l 对应文件权限。


三十五、操作系统最核心的知识关系

操作系统中的知识并不是互相独立的。

一个程序运行时,会同时涉及多个模块。

以浏览器打开网页为例:

  1. 用户双击浏览器图标;

  2. 操作系统创建浏览器进程;

  3. 进程中创建多个线程;

  4. 调度器为线程分配 CPU 时间;

  5. 内存管理系统为浏览器分配虚拟内存;

  6. 浏览器通过系统调用访问网络;

  7. 网卡接收数据并产生中断;

  8. 操作系统把网络数据交给浏览器;

  9. 浏览器把缓存文件写入文件系统;

  10. 显卡驱动把网页内容显示在屏幕上。

在这个过程中,进程、线程、CPU 调度、内存、文件系统、系统调用、中断和设备驱动全部参与了工作。

这也是学习操作系统时最重要的理解:

操作系统不是一堆互不相关的概念,而是一套共同管理程序运行的机制。


总结

操作系统的核心任务,可以归纳为四个方面。

1. 管理 CPU

决定哪个进程或线程可以运行,以及运行多长时间。

2. 管理内存

为程序分配内存,隔离不同进程,并通过虚拟内存提高内存利用率。

3. 管理文件

组织磁盘数据,提供文件名、目录、权限和读写操作。

4. 管理设备

通过驱动程序、中断和输入输出机制控制键盘、鼠标、硬盘、网卡等设备。

理解操作系统时,可以始终围绕三个问题思考:

  1. 这个资源由谁管理?

  2. 多个程序同时使用时,怎样避免冲突?

  3. 一个程序出现错误时,怎样避免影响整个系统?

当这三个问题逐渐想清楚以后,进程、线程、锁、内存、文件系统等概念就会自然地连接起来。

操作系统真正要解决的问题并不神秘。

它只是要让许多程序在有限的硬件上安全、有序、高效地运行。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐