资料合集
链接:https://pan.quark.cn/s/770d9387db5f
从“剧本”到“戏剧”:
当我们双击一个应用图标,或者在终端敲下 并回车时,一个神奇的转换发生了:一个静静躺在硬盘上的文件,瞬间“活”了过来,变成了屏幕上一个可以交互的窗口或后台运行的任务。
./a.out
这个从静态到动态的飞跃,正是计算机科学中两个最基本的概念——程序(Program)与进程(Process)——的核心区别。理解它们,就等于拿到了进入操作系统殿堂的第一把钥匙。
一、 静态的“剧本”:到底什么是程序?
在我们探讨“运行”之前,先来看看它的起点——程序。
定义:程序是一个静态的文件,通常是经过编译链接后生成的可执行二进制文件(在 Linux 中,我们最熟悉的莫过于 )。特性:它就像一部写好了的剧本。剧本本身不发声、不动、不消耗舞台资源(CPU、内存),它只安安静静地躺在书架上(磁盘),占用一些存储空间。
a.out
【实验一:见证一个“剧本”的诞生】
让我们来编写并编译一个最简单的 C 语言程序。
代码 ()
demo.c
#include <stdio.h>
#include <unistd.h>
int main() {
printf("I am a program, my destiny is to become a process.
");
// sleep(10); // 暂时注释掉,下一节再用
return 0;
}
编译与观察
# 1. 编译 C 代码,生成可执行文件 a.out
gcc demo.c
# 2. 查看文件详情
ls -l a.out
# 3. 查看文件类型
file a.out
运行结果
$ ls -l a.out
-rwxr-xr-x 1 user user 16696 Nov 20 10:30 a.out
$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), ...
结果分析: 和
ls 命令的结果明确告诉我们,
file 只是一个躺在磁盘上的、具有特定格式(ELF 64-bit)的可执行文件。此刻,它没有消耗任何 CPU 或内存,它就是一个静态的“剧本”。
a.out
二、 动态的“戏剧”:进程的登场
当操作系统加载并执行这个“剧本”时,**进程(Process)**就诞生了。
定义:进程是程序的一次动态执行过程。特性:它是一场正在上演的戏剧。为了上演,它需要占用各种系统资源:
舞台(CPU):获得计算能力。场地和道具(内存):加载代码、数据,并拥有独立的虚拟地址空间。演员(各种数据):在舞台上活动。
【实验二:唤醒进程并观察它】
现在,我们让 里的进程多活一会儿,以便我们能“抓住”它。
demo.c
修改代码 ()
demo.c
#include <stdio.h>
#include <unistd.h>
int main() {
printf("I am a process now! My PID is: %d
", getpid());
printf("You have 20 seconds to find me using 'ps' command...
");
sleep(20); // 让进程睡眠20秒,而不是立即退出
printf("Time is up, I am exiting.
");
return 0;
}
我们使用了 来获取进程的唯一标识符(PID),并用
getpid() 让它持续运行20秒。
sleep(20)
编译与运行
重新编译:在第一个终端运行:
gcc demo.c -o demo【关键】立即打开第二个终端,使用
./demo 命令查找我们的进程。
ps
ps aux | grep demo
运行结果
终端一:
I am a process now! My PID is: 23451
You have 20 seconds to find me using 'ps' command...
(光标在此处等待20秒)
Time is up, I am exiting.
终端二 (在20秒内执行):
$ ps aux | grep demo
user 23451 0.0 0.0 2388 860 pts/0 S+ 10:35 0:00 ./demo
user 23453 0.0 0.0 6432 720 pts/1 S+ 10:35 0:00 grep --color=auto demo
结果分析: 命令的结果抓到了一个活生生的进程!PID 为
ps 的
23451 进程正在运行(状态
./demo 表示在前台睡眠)。它占用了 CPU (0.0%) 和内存,拥有独立的进程ID。这证明,进程是程序运行时的动态实例。
S+
一个剧本,多场戏剧 (1:N 关系) 如果我们在后台同时运行多个 实例呢?
./demo
./demo &
./demo &
ps aux | grep demo
你会发现,同一个 程序,产生了多个拥有不同 PID 的进程。这完美诠释了程序和进程之间 1:N 的关系。
demo
三、 并发之舞:单核 CPU 如何上演“多线程”大戏?
我们都知道,现在的操作系统可以“同时”听歌、写代码、聊微信。这种现象叫做并发(Concurrency)。但在一个单核 CPU 上,任意时刻真的只能做一件事。它是如何创造出“同时”运行的假象的呢?
答案是:时间片轮转(Time-Slicing)。
单道程序设计:像古老的 DOS 系统,任务必须排队。听完歌才能看电影。CPU 在等待 I/O 时会完全闲置,效率极低。多道程序设计:现代操作系统将 CPU 时间切成极小的片段(毫秒甚至微秒级),称为时间片。它通过一个调度程序,飞快地在多个进程之间切换。
进程 A 运行一个时间片 -> 切换进程 B 运行一个时间片 -> 切换进程 C 运行一个时间片 -> 切换 -> 回到 A …
由于 CPU 的切换速度(纳秒级)远超人类的感知极限(毫秒级),在我们看来,所有进程就好像在并行执行。这就是**“宏观并行,微观串行”**。
【实验三:模拟并发的幻觉】
我们将编写一个程序,它有两个任务:打印 'A' 和打印 'B'。
代码 ()
concurrency_sim.c
#include <stdio.h>
#include <unistd.h>
// 任务A:持续打印 'A'
void task_a() {
for (int i = 0; i < 20; i++) {
printf("A");
fflush(stdout); // 立即刷新缓冲区,强制输出
usleep(100000); // 模拟耗时操作
}
}
// 任务B:持续打印 'B'
void task_b() {
for (int i = 0; i < 20; i++) {
printf("B");
fflush(stdout);
usleep(100000);
}
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("--- Simulating Single-Tasking ---
");
task_a();
task_b();
printf("
");
} else {
if (argv[1][0] == 'A') {
task_a();
} else if (argv[1][0] == 'B') {
task_b();
}
}
return 0;
}
编译与运行
gcc concurrency_sim.c -o sim
1. 模拟单道执行:
./sim
结果:
--- Simulating Single-Tasking ---
AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBB
分析:非常清晰,任务 A 必须完全结束后,任务 B 才能开始。这是典型的串行执行。
2. 模拟多道并发执行: 我们将启动两个进程,一个执行任务 A,一个执行任务 B,让操作系统来调度它们。
# 在后台启动任务A,然后立即在后台启动任务B
./sim A & ./sim B &
# 等待几秒钟让它们执行完毕
结果 (每次可能略有不同):
AABABBABABABBABABABA
分析:看到了吗?'A' 和 'B' 的输出被完美地交织在了一起!这并不是因为它们在“同时”运行,而是因为操作系统调度器在两个 进程之间进行了高速切换。我们亲手制造并观察到了并发的“幻觉”。
./sim
知识小结
|
概念 |
本质 |
状态 |
资源占用 |
关系 |
比喻 |
|
程序 |
二进制文件 |
静态 |
仅磁盘空间 |
1 : N |
剧本 |
|
进程 |
运行中的程序实例 |
动态 |
CPU, 内存等 |
N : 1 |
戏剧 |
|
并发 |
宏观并行,微观串行 |
动态切换 |
CPU时间片 |
多进程共享 |
多场戏剧在同一舞台上分时上演 |
















暂无评论内容