🐧 第一章 计算机系统的概要
📄 一句话概括

一台机器只有四样东西:CPU、内存、外部存储器、输入输出;操作系统夹在应用和这四样之间,只干三件事——分配、隔离、抽象;你那句 open() 之所以能拿到文件,是因为有人替你跑了内核里的一整条路。全书要拆的就是这条路。

📝 本节和前面几节的关系

这是全书第一章,没有前置。它只干一件事:把地图先摊开——告诉你一台机器里谁在管谁,以及后面七章各拆的是哪一块。

一、一台计算机里只有四样东西

CPU:负责算。它的速度是纳秒级——一秒能算几十亿次。 内存:负责临时记。CPU 要的指令和数据都从这里取,速度也是纳秒级,但断电就没了。 外部存储器:硬盘、SSD。负责长期记,断电还在,但速度差了两三个数量级——微秒到毫秒级。 输入输出设备:键盘、网卡、显示器。负责和外面的世界换东西。

四样东西的速度差出百万倍,这是理解操作系统所有设计的起点:快的要等慢的,所以必须有人在中间调度、缓存、排队。 那个人就是操作系统。

二、操作系统只干三件事:分配、隔离、抽象

分配:CPU 只有一个(或几个核),却有一堆程序都想跑——谁先跑、跑多久,由调度器定。内存只有一块,谁拿哪一段、拿多少,由内存管理定。 隔离:你写的程序不能直接改别人的内存、不能直接命令硬盘、更不能一条指令把整个系统弄崩。这条边界是操作系统存在的第一理由。 抽象:硬盘上有几百万个扇区,它给你的是【文件】;CPU 有一堆寄存器和中断,它给你的是【进程】;网卡有一堆缓冲队列,它给你的是【套接字】。

三件事里,隔离最容易被忽略,也最重要。没有隔离,一个有 bug 的程序就能让整台机器停摆;有了隔离,崩溃被关在进程里。

三、用户模式和内核模式:那道门为什么要存在

CPU 硬件本身提供了至少两个特权级。绝大多数代码跑在【用户模式】:只能碰自己的内存,不能直接读写设备,不能改页表。 只有内核跑在【内核模式】:什么都能碰。 那应用程序想读文件怎么办?只能【请求】——发一个系统调用,CPU 切到内核模式,内核替它做完,再切回来。

这就是那道门。三个后果值得先记住: 1. 门是唯一的通道:用户程序碰不到硬件,只能请内核代劳。 2. 过门要钱:保存现场、切换特权级、检查参数、回来恢复现场。 3. 钱是固定的:搬 1 个字节和搬 1 MB,过门的那笔开销一样。

第 3 条直接决定了你以后怎么写程序:小批量、多次地调系统调用,等于一遍遍交过路费。下面 Demo 里量给你看。

四、这本书要拆的六条线

后面七章,一章拆一块,顺序是从【谁在跑】到【东西存在哪】: 1. 第 2 章 用户模式实现的功能:库和 shell 这一层,它们都在用户模式里跑。 2. 第 3 章 进程管理:进程怎么被造出来、怎么被换掉、怎么结束。 3. 第 4 章 进程调度器:一堆进程抢几个 CPU,谁先谁后、等多久。 4. 第 5 章 内存管理:虚拟内存、页表、写时复制——每个人以为自己独占整块内存。 5. 第 6 章 存储层次:缓存、TLB、页面缓存——快的太小、大的太慢,中间怎么办。 6. 第 7 章 文件系统:数据与元数据、日志、写时复制——断电之后凭什么还能对上账。 7. 第 8 章 外部存储器:HDD 与 SSD 的性能特性、I/O 调度、预读。

七章合起来回答一个问题:你敲一条命令之后,机器里到底发生了什么。

五、动手 Demo:一台机器的体检表——硬件有哪几样,谁在替你管

下面这段代码干两件事:先让操作系统把机器的底细报一遍(CPU 几核、内存多少、磁盘多大),再量一次过路费——同样读 1 MB,读一次和分 1024 次读,差多少。

代码里有个细节值得留意:取内存总量时用了两条路——Linux 上读 /proc/meminfo,Windows 上调 GlobalMemoryStatusEx。同一个问题、两种问法,这就是【内核提供的服务】长什么样:它不直接给你数字,它给你一个【问它的方式】。

import ctypes, os, platform, shutil, time

def mem_gb():
    # Linux 读 /proc/meminfo;Windows 调 GlobalMemoryStatusEx
    try:
        with open("/proc/meminfo") as f:
            for ln in f:
                if ln.startswith("MemTotal:"):
                    return int(ln.split()[1]) / 1024 / 1024
    except OSError:
        pass
    # Windows 分支:结构体 + 一次系统调用
    m = MS(); m.dwLength = ctypes.sizeof(MS)
    ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(m))
    return m.ullTotalPhys / 1024 ** 3

# ② 同样 1 MB:一次读完 vs 分 1024 次读(绕开 Python 缓冲)
fd = os.open(p, os.O_RDONLY)
os.read(fd, 1024 * 1024)          # 一次过门
os.close(fd)

fd = os.open(p, os.O_RDONLY)
for _ in range(1024):
    os.read(fd, 1024)             # 过 1024 次门
os.close(fd)

实跑输出:

--------------------------------------------------------------------
一台机器的体检表——硬件有哪几样,谁在替你管
--------------------------------------------------------------------
① 这台机器自己报出来的底细(都是程序向操作系统要来的)

  项目         值             谁提供的
  ---------------------------------------------
  操作系统     Windows        内核
  版本         10.0.19045     内核
  机器架构     AMD64          内核
  逻辑 CPU     8 个           内核(调度器用)
  物理内存     15.9 GB        内核(内存管理)
  磁盘容量     381.5 GB       内核(文件系统)

② 同样 1 MB,读一次和分 1024 次读

  读法                    总耗时        进内核几次
  -------------------------------------------------
  一次读完 1 MB           1.85 毫秒     1 次
  分 1024 次各读 1 KB     7.14 毫秒     1024 次

      后者多花                    3.9 倍

      两次读的都是同样 1 MB,数据量一样、活儿一样,**差的是过门次数。**
  **系统调用有一笔固定的过路费,跟搬多少货没关系——批量是唯一能摊薄它的办法。**

③ 这台机器的四层,谁管什么

  层       里面有谁                           它负责
  ---------------------------------------------------------------
  应用     浏览器、编辑器、你的 Python        你要办的事
  库       标准库、第三方库                   把常用活儿打包好
  内核     进程、调度、内存、文件、块设备     分配硬件、隔离越界
  硬件     CPU、内存条、磁盘、网卡            真的动电

  **下面三层你天天在用,但一层都没写过。**
  **这本书就是把这层纸捅破:内核到底替你做了什么,用多少钱做的。**

① 这张体检表上的每一项,都是程序【向内核要】来的。程序自己读不到 CPU 有几个核、内存有多少——它没有那个权限。 ② 1 MB 的账最能说明问题。同样 1 MB,一次读完与分 1024 次读,数据一样多、活儿一样多,后者多花三四倍,多的全是过门钱。 如果过门不要钱,1024 次就该和 1 次一样快。现在不一样,说明每趟都在交费。 ③ 所以【读写要批量】不是优化技巧,是这门课最基本的省钱方式——后面第 6 章的页面缓存、第 8 章的预读,都是同一笔账的延伸。

📋 在真 Linux 上复核:cat /proc/cpuinfo | grep -c processor 数核、free -h 看内存、df -h 看磁盘;过路费用 strace -c 统计,它会按系统调用名列出次数与耗时。

30 秒实验

  1. cat /proc/cpuinfo | grep "model name" | head -1——看 CPU 型号。这个文件不是真的磁盘文件,是内核现编的。
  2. free -h 看内存,df -h 看磁盘。两个命令一个向内核要、一个向文件系统要。
  3. 同一条 ls 命令,分别用 time ls 和 time ls -l 跑,看 -l 多出来的时间花在哪(多查了多少次文件属性)。

常见误区

  1. 【操作系统就是那个黑窗口】。黑窗口是 shell,一个普通用户程序;内核在它下面一层,你看不见。
  2. 【程序直接读写硬盘】。程序只能发系统调用,硬盘是内核管的。用户模式下连【硬盘】这个设备都不存在,只有一个文件描述符。
  3. 【系统调用越少越好】。不是越少越好,是别把大批量拆成小批量。该调的一次也不能省,能并的就并成一次。
自测三问(点开对答案) 1. 一台计算机的四大件是什么?为什么说它们的速度差是理解操作系统的起点? —— CPU、内存、外部存储器、输入输出。四者速度差出百万倍,快的要等慢的,所以必须在中间调度、缓存、排队。 2. 操作系统只干哪三件事?哪一件最容易被忽略? —— 分配、隔离、抽象。**隔离**最容易被忽略——没有它,一个有 bug 的程序能拖垮整台机器。 3. 为什么【过门要钱】这条会直接影响你的代码写法? —— 因为过门开销是固定的,与搬多少数据无关;把 1 MB 拆成 1024 次读,等于交 1024 次过路费。**批量是为了摊薄固定开销。**

相关笔记