元数据卡
- 前置知识:会用电脑,能开浏览器
- 预计时间:35 分钟
- 核心难度:
- 阅读模式: 轻松漫游
- 完成标志:能够解释程序、文件、进程、内存之间的关系;知道在操作系统中找到它们
你的进度
你站在一扇巨大的铁门前,门楣上刻着两行字:
你即将踏入开发者工坊。 认识你的工具之前,先认识你自己——还有你的装备。
铁门缓缓打开,里面是一座灯火通明的工坊。墙上挂满了各种工具——但在你伸手抓之前,工坊的主人清了清嗓子。
"新手,你知道电脑到底是什么吗?"
你的任务
你说"知道"——不就是个发光的盒子吗?但你说不出来里面到底在干什么。每次双击一个图标,几万个零件在一秒内完成了几十亿次动作。你打开浏览器、写文档、放音乐、关掉,整座城市在芯片上繁荣又沉寂。
这章的任务很简单:在你出发之前,知道你的装备里到底装着什么。程序是什么?文件是什么?那个可以让十几个窗口同时运行的魔法——到底是什么?
你不需要深入 CPU 微架构,也不需要记住内存地址。你只需要知道:当你双击一个 .exe 或运行一条命令之后,里面到底发生了什么。
看完这章,你就能回答:"这台电脑里现在到底在跑什么?"
本章分层
- 必读:程序是什么、文件是什么、进程和程序的区别、内存的作用、操作系统如何让多个程序"同时运行"
- 选读:用
ps aux和top查看系统进程、用free -h查看内存、理解 PID 的作用- 进阶:僵尸进程与信号(SIGTERM/SIGKILL)、父子进程关系
本章不会要求你掌握
- 线程与进程的区别——先掌握进程就够
- 虚拟内存和内存分页的实现细节
- CPU 调度算法或进程调度策略
破局 · 溯源
第一幕:程序只是一个文件
"工坊里有一把好锤子,和一张锤子的图纸——它们有什么区别?"
工坊主人冷不丁地问了你一句。你愣住了——锤子图纸画得再精美,它不能敲钉子。但你把图纸上的设计做出来,它就变成了一把能用的锤子。
"程序也是一样。"他递过来一个小盒子。"这是一个计算器程序。它在硬盘上安安静静地躺着——像一张图纸。但当它被启动后,它在内存里活了过来。"
你坐在工坊的桌前,面前是一台显示器。你点开一个叫 calc 的图标——计算器弹了出来。
看上去像魔法?其实不是。
计算器本质上只是一个文件。一个保存在硬盘上的、很长的、机器能读懂的指令列表。在 Windows 上它叫 calc.exe,在 Mac 上它藏在 /Applications/Calculator.app 里。它不动声色地躺在那里,像一本关着的书——你打开它,它才开始运作。
打开终端(别怕它,第二章会详细说),运行这句话:
# 在 Linux / Mac 上查看一个可执行文件的类型
file /bin/ls
# 预期输出:
# /bin/ls: ELF 64-bit LSB executable x86-64 version 1 (SYSV)语言:Shell (Bash) 如何运行:打开终端,粘贴后按回车 预期输出:显示 /bin/ls 是一个 ELF 格式的可执行文件,64位,运行在 x86-64 架构上 你试试:换成 /bin/bash、/usr/bin/python3 或任意可执行文件的路径
输出的意思很简单:/bin/ls 这个文件里,装的不是文字,不是图片,是机器可以直接执行的指令。
程序 = 存在硬盘上的一个文件。
它的内容不是人类读的文字,是机器的指令。
这和 .txt 文件没有本质区别——都是硬盘上的一串字节。只不过 .txt 由你的文本编辑器读取、展示成文字;.exe 由操作系统读取、加载到内存里、一条一条执行。
所以,你在桌面上看到的每一个图标,背后都是一个或几个文件。它们静静躺在硬盘上,等你去打开。
第二幕:文件就是信息
"刚才你从材料架上拿了一块铜板,但你有没有想过——那块铜板上的标记本身是什么?"
工坊主人把一个石头板递到你面前。"这个板子上刻了三个字:'购物清单'。你说它是文件吗?"
你点头。"那如果我在这个板子上画一朵花呢?它还是文件吗?"他又问。你有点不确定了。
"文件不在于你刻什么,"他拍了拍石板,"而在于它放在哪、叫什么名字、有多长。"
在我们继续之前,先确认一个最根本的东西:文件。
你大概已经知道文件是什么:双击一个 Word 文档,它能编辑;双击一个照片,它能显示。但如果你站近一点看,文件到底是什么?
文件 = 硬盘上的一个名字 + 一段字节。
工坊主人的话更直接:文件是信息的最小容器。每个文件有一个路径、一个大小、一个类型。操作系统用文件系统来管理它们——文件系统就是硬盘上的一个大型索引——像图书馆的卡片柜一样,告诉你每一本书(文件)在哪个架子上(目录)。
你可以在终端里看看你的文件:
# 列出当前目录下所有文件,显示详细信息
ls -lh
# 预期输出样例(你的文件不同):
# -rw-r--r-- 1 steven steven 1.2K Jun 23 10:00 README.md
# drwxr-xr-x 2 steven steven 4.0K Jun 23 09:30 src
#
# 第一列:文件权限(-开头是普通文件,d开头是目录)
# 第三、四列:文件所有者和所属组
# 第五列:文件大小
# 最后一列:文件名语言:Shell (Bash) 如何运行:打开终端,输入 ls -lh预期输出:当前目录下的所有文件和子目录,带大小、修改时间、权限 你试试:ls -lh / 查看根目录,看看系统文件
对操作系统来说,一切皆是文件。你的文档、程序、硬件设备(比如键盘输入)、进程信息,都以"文件"的形式暴露出来。这不是比喻,是 Unix 设计哲学的核心。
不过现在,你只需要记住:文件是硬盘上的信息单元。程序、文档、图片、音乐——全是文件。区别只在于谁来解读这些字节。
第三幕:拉一下手柄——进程诞生了
"好了,我们知道程序是一个文件——躺在硬盘上的图纸。那怎么让它活过来?"
工坊主人把计算器图标放在桌上,指了指它。"这张图纸你拿到了。现在告诉我,你能用它敲钉子吗?"
你摇头。"除非……你把它做成锤子?"
"对!"他一拍桌子,"从图纸到锤子,需要一个步骤:拉一下手柄。双击鼠标,拉一下。"
你双击了计算器图标。
操作系统做了几件事:
- 在硬盘上找到
calc.exe这个文件 - 读取它的内容——那一长串机器指令
- 在内存里划出一块区域
- 把指令复制进去
- 准备一个"控制面板"记录它的一切(进程ID、状态、占用资源)
- 让 CPU 开始执行第一条指令
那一刻,程序变成了进程。
程序是静态的,进程是动态的。书关着是程序,打开在读是进程。
# 查看当前运行的所有进程
ps aux
# 预期输出(简化):
# USER PID %CPU %MEM START TIME COMMAND
# steven 1234 0.1 2.3 10:00 0:02 /usr/bin/gnome-terminal
# steven 5678 0.0 0.5 10:05 0:00 /usr/bin/calc
#
# PID = 进程ID,操作系统给每个进程的唯一编号
# %CPU = 这个进程占用了多少 CPU
# %MEM = 占了多少内存
# COMMAND = 它跑的是什么程序语言:Shell (Bash) 如何运行:打开终端,输入 ps aux 或 ps aux | head -20 只看前20行 预期输出:所有正在运行的进程列表,包含 PID、CPU/内存使用情况、启动时间、完整命令路径 你试试:在另一个窗口打开一个程序(比如记事本或浏览器),回到终端再跑一次 ps aux,看看多了哪一行
你看到的那一串进程列表,就是你的电脑此时此刻正在做的一切——每个窗口、每个后台服务、每个检查更新的小程序,都在一条记录里。
看到那个 PID 了吗?它叫进程 ID,是操作系统给每个进程的身份证号。你在工坊的后续冒险中会大量用到它——查、杀、看、跟踪。
第四幕:内存——工坊的工作台
"程序变成进程了,对吧?那进程住在哪?"
工坊主人把你带到一面工具墙前。墙上有大大小小的格子——螺丝刀、扳手、钳子、锉刀,整整齐齐。
"这是我的工作台。"他用手划了一圈。"每次我干活的时候,我把需要的工具从柜子里拿出来放到台面上。用完了再放回去。柜子大但麻烦——你得走过去翻。台面就在手边,但地方就那么大。"
"进程也一样。它需要一个工作台。"
还记得我们在第三幕说的"在内存里划出一块区域"吗?
内存是进程的工作台。
想象你的桌面(物理桌子,不是显示器)。你在做一件事的时候,会把需要用到的工具摆到桌面上。内存就是那张桌子。硬盘是你的储物柜——空间大但慢,拿东西要弯腰翻半天。内存是工作台——空间小但快,伸手就能拿到。
当你打开一个程序,操作系统把它的指令从硬盘(储物柜)复制到内存(工作台),然后 CPU(你的手)开始一条一条地拿取指令来执行。
# 查看系统的内存使用情况
free -h
# 预期输出(数字可能不同):
# total used free shared buff/cache available
# Mem: 15Gi 5.2Gi 3.1Gi 512Mi 6.7Gi 9.1Gi
# Swap: 2.0Gi 0.0Gi 2.0Gi
#
# total = 物理内存总量
# used = 正在被进程使用的
# free = 完全空闲的
# available = 可用的(包括可以回收的缓存)语言:Shell (Bash) 如何运行:输入 free -h(-h 表示人类可读格式) 预期输出:内存总量、已用、空闲、可用等情况 你试试:开一个大程序(或很多浏览器标签页)后再运行,观察 used 和 available 的变化
这里有两个关键点:
内存是有限的。15GB 总内存,开几个大程序就会用完。所以操作系统会"交换"(swap)——把暂时不用的数据挪回硬盘。内存不够时,电脑会变慢。
每个进程有自己的内存空间。A 程序不能直接访问 B 程序的内存。这是操作系统强制保护的安全机制——如果一个程序崩溃了,不会带走其他程序。
这就是为什么拍一张照片只需要几 MB,但打开 Photoshop 要吃掉几个 GB——程序自己住在内存里,它操作的数据也住在内存里。
第五幕:它跑起来之后
"好,现在你拉动了几个手柄——仪表盘亮了几个窗口:材料测量仪、成品目录册、工坊操作台。它们全变成了进程。那我问你:这些进程是不是同时在工作?"
工坊主人笑着伸出两只手。"我左手画圈,右手画方——你觉得我画得好吗?"
他同时动了两只手,画出来的两个半圆歪歪扭扭。"比一个一个画差多了,对吧?"他收起左手,右手画了一个标准的圆。"因为我的脑子一次只能专注一件事。电脑也一样——只不过它切换得飞快,快到你以为它在同时做。"
你打开浏览器,五个标签页;打开了终端;打开了 VS Code;还开着微信。
现在你打开任务管理器(Windows: Ctrl+Shift+Esc,Mac: Activity Monitor,Linux: htop 或 top),看到了一大串名单。
每一个条目都是一个进程。五个浏览器标签页可能是五个进程(现代浏览器把每个标签页隔离成独立进程,为了防止一个崩了全崩)。再加上终端、编辑器、聊天工具、系统服务……
你的电脑在同时运行几十甚至上百个进程。
但不是同时执行的——是操作系统在"演戏"。
你的 CPU 可能有 4 个核或 8 个核,每一核一次只能执行一个任务。但操作系统每秒把 CPU 时间切成很小的片(时间片,大约几十毫秒),飞快地在所有就绪的进程之间切换。切到进程 A 跑 30ms,切到进程 B 跑 30ms,再切回去……
因为切换速度太快,你感受到的是"同时运行"。这叫并发。
# 实时查看当前运行中的进程(类似任务管理器)
top -o %CPU
# 按 q 退出语言:Shell (Bash) 如何运行:输入 top 或 htop(有的系统需要先 sudo apt install htop) 预期输出:实时更新的进程列表,默认按 CPU 使用率排序,按 q 退出 你试试:运行 yes > /dev/null & 创建一个疯狂用 CPU 的进程,在 top 里观察它;然后用 kill %1 杀掉它(问清楚再用)
你可以在这里直观地看到:哪些进程在用 CPU(正在被执行的指令),哪些在用内存,哪个吃了最多的 I/O。这些都是真实的系统指标。
常见陷阱
陷阱一:任务管理器里关了,但后台还在
你删除程序或者关闭窗口,以为它消失了。但有些程序(特别是安装器和后台服务)以守护进程(daemon)的形式在后台持续运行。
你在 ps aux 里看到的那一堆 systemd、cron、dbus-daemon——它们不像计算器那样有个窗户,但它们一直在那里。
怎么办:如果你想彻底关掉一个后台进程,需要用 kill <PID> 或 pkill <进程名>。
陷阱二:一个进程占满了一个 CPU 核
你的电脑风扇狂转,但只看到一个进程在拼命跑。这不是病毒——更可能是程序写了死循环。
在 top 里按 P(大写 P)按 CPU 排序,找到 PID,然后用 kill <PID> 杀掉它。如果杀不掉,加 -9:
# 先找到 PID,然后
kill -9 12345 # 12345 换成实际进程号等一下,kill -9 很暴力。 它不会让进程清理临时文件或关闭网络连接。能用 kill <PID> 就用,除非真的杀不掉。
深入冒险
进程不只是计算器那么简单
你以后会遇到这些概念,现在只需要知道它们存在:
- 父子进程:一个进程可以创建子进程。你在终端里运行
ls,终端进程会创建一个子进程来执行ls。子进程结束后,终端重新获得控制权。 - 僵尸进程:子进程结束了,但父进程没有"确认"它的退出状态。进程记录就卡在那里,变成一个僵尸——看
ps aux时,状态是Z。 - 信号:
kill不只是在"杀死"进程,它是在发送信号。kill <PID>发送SIGTERM(请求优雅终止),kill -9发送SIGKILL(强制杀掉,无法阻挡)。还有其他信号比如SIGHUP(挂断)、SIGINT(Ctrl+C 触发)。
这些你现在不需要深究。知道它们存在就好,等遇到时你至少见过它们的名字。
好奇者实验
以下内容不是主线必需。当你对某个现象感到好奇时可以试试,但读完不影响继续下一章。
文件删了但空间没释放——谁还在用它?
你删除了一个大文件,但磁盘空间没有释放。
原因:进程可能还在使用这个文件。操作系统里,删除文件只是删掉了文件名,实际数据直到最后一个打开它的进程关闭后才被真正释放。
# 查找被进程打开但已删除的文件
lsof | grep deleted
# 如果你不想安装 lsof,可以用 /proc 查看
ls -la /proc/*/fd/ 2>/dev/null | grep deleted语言:Shell (Bash) 如何运行:lsof | grep deleted(需要先安装 lsof: sudo apt install lsof 或 macOS 自带) 预期输出:显示被某进程打开但已标记为删除的文件及其 PID
谁在监听我的端口?
# 查看哪些进程在监听 80 端口(HTTP 服务)
lsof -i :80如果没有输出,说明没有 Web 服务在运行。
为什么这值得好奇?
lsof和/proc是 Unix 系统中"一切皆文件"哲学的极致体现——连网络端口和进程文件句柄都被当作文件来管理。
常见陷阱
真实故事:我在哪删了东西?
有次我写了一个脚本,ls 输出一堆文件,想删掉旧的。我写了 rm -rf ./*.log——但当前目录是 /(根目录)。
幸运的是现代系统有保护机制,让我输出了:
rm: it is dangerous to operate recursively on '/'但如果你加 --no-preserve-root,系统会真的执行。
教训:执行 rm、mv、覆盖文件之前,用 echo 或 ls 先看到底会操作哪些东西。先打印,再执行。
通关挑战
- 热身(5 分钟,必做)
- 打开终端,运行
ps aux | wc -l,看看你的电脑当前有多少进程在跑。正常系统通常在 100-300 之间。 - 运行
free -h,看看总内存、用了多少、还剩多少。 - 运行
ls -la ~,看看你用户目录下有哪些文件(特别是隐藏文件,以.开头的)。
- 挑战(30 分钟,选做)
- 打开浏览器,打开 5 个标签页。运行
ps aux | grep chrome(或firefox或safari),看看有多少个进程。现代浏览器通常每个标签页一个进程。 - 运行一个死循环程序作为测试:
# 在一个终端运行:
python3 -c "while True: pass" &
# 这会创建一个 Python 进程,无限循环。
# 在第二个终端:
top -o %CPU -p $(pgrep -f "while True")
# 关掉测试:
kill %1- 用
ps aux | grep -E "chrome|firefox|safari"看看浏览器的进程数量。
验收标准
- 你能用一句话说清楚"程序和进程的区别"。
- 你知道什么叫进程 ID(PID)和在哪里找到它。
- 你能打开终端,用
ps aux、free -h、top查看系统状态。 - 你能解释为什么电脑可以"同时运行"很多程序——时间片切换。
- 你知道文件是硬盘上的字节集合,文件系统管理它们。
常见卡点
"我运行 free 说用了 80%,是不是不够用?不是。 Linux 会把空闲内存用于缓存(cache/buffers),在有需要时释放给程序。available 才是真正可用的,free 列显示的是绝对空闲,通常很小。
"为什么 ps aux 出来一大堆看不懂的东西?" 很多是操作系统自带的守护进程。你只需要关注 PID 和你认识的程序名(如 bash、chrome、code)。等你看多了,系统进程的名字也会慢慢认识。
"进程和线程有什么区别?" 这章贴心地不回答这个问题。现在你只需要知道进程就行。线程是一个进程内部的"轻量级子任务"——多个线程可以共享同一块内存空间。你先掌握进程,线程在 Vol 3 会彻底讲清楚。
现在不需要理解
- CPU 到底是怎样执行指令的(流水线、乱序执行、分支预测)——这些在 Vol 3 会讲
- 虚拟内存和内存分页的细节——同上
- 文件系统的底层实现(ext4、NTFS、Btrfs 的区别)
- 进程调度算法(CFS、时间片大小调优)
- 线程是什么——这章我们只讨论进程级别
旅人笔记
程序是硬盘上存着的文件,静态不动; 进程是运行起来的程序,动态、ID 编号、占内存和 CPU; 文件是信息的基本单元,一切皆是文件; 内存是进程的工作台,有限、快速、独立隔离; 操作系统在 CPU 核之间切来切去,让你感觉"同时运行"。
六个字:程序 → 进程 → 运行
→ 下一站预告
现在你知道了电脑里装了什么——但你不会用。你的工具安静地躺在终端里等着。下一章,我们会打开通往终端的大门,学会和电脑对话的第一句话。你还记得工坊主人说的那句话吗——"拉一下手柄"?那个手柄叫 回车。
等一下,你需要先学会用键盘打字。