chore: 将所有外部图片本地化到仓库

- 下载 110 张外部图片到根目录 images/ 文件夹
- 更新所有 README.md 中的图片引用为统一路径 images/xxx.png
- 55 张图片成功下载(PNG 格式)
- 55 张失效图片创建占位文件(SVG/PNG)
- 移除所有外部图片链接依赖

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
riba2534
2026-01-05 16:34:13 +08:00
parent d44ecdf807
commit 0d17c981ee
128 changed files with 691 additions and 232 deletions

View File

@@ -28,7 +28,7 @@
每个进程的内存空间都由保存全局变量的「数据区」、向 malloc 等函数动态分配提供空间的堆Heap、函数运行时间使用的栈Stack构成。每个进程都有独立的这种空间多个进程的内存结构如图所示
![](https://i.loli.net/2019/02/02/5c55aa57db3c7.png)
![](images/5c55aa57db3c7.png)
但如果以获得多个代码执行流为目的,则不应该像上图那样完全分离内存结构,而只需分离栈区域。通过这种方式可以获得如下优势:
@@ -37,7 +37,7 @@
实际上这就是线程。线程为了保持多条代码执行流而隔开了栈区域,因此具有如下图所示的内存结构:
![](https://i.loli.net/2019/02/02/5c55ab455e399.png)
![](images/5c55ab455e399.png)
如图所示,多个线程共享数据区和堆。为了保持这种结构,线程将在进程内创建并运行。也就是说,进程和线程可以定义为如下形式:
@@ -46,7 +46,7 @@
如果说进程在操作系统内部生成多个执行流,那么线程就在同一进程内部创建多条执行流。因此,操作系统、进程、线程之间的关系可以表示为下图:
![](https://i.loli.net/2019/02/02/5c55ac20aa776.png)
![](images/5c55ac20aa776.png)
### 18.2 线程创建及运行
@@ -124,11 +124,11 @@ gcc thread1.c -o tr1 -lpthread # 线程相关代码编译时需要添加 -lpthre
运行结果:
![](https://i.loli.net/2019/02/02/5c55b5eb4daf6.png)
![](images/5c55b5eb4daf6.png)
上述程序的执行如图所示:
![](https://i.loli.net/2019/02/02/5c55b6943255b.png)
![](images/5c55b6943255b.png)
可以看出,程序在主进程没有结束时,生成的线程每隔一秒输出一次 `running thread` ,但是如果主进程没有等待十秒,而是直接结束,这样也会强制结束线程,不论线程有没有运行完毕。
@@ -200,13 +200,13 @@ gcc thread2.c -o tr2 -lpthread
运行结果:
![](https://i.loli.net/2019/02/02/5c55bd6032f1e.png)
![](images/5c55bd6032f1e.png)
可以看出线程输出了5次字符串并且把返回值给了主进程
下面是该函数的执行流程图:
![](https://i.loli.net/2019/02/02/5c55bdd3bb3c8.png)
![](images/5c55bdd3bb3c8.png)
#### 18.2.2 可在临界区内调用的函数
@@ -255,7 +255,7 @@ gcc -D_REENTRANT mythread.c -o mthread -lpthread
下面的示例是计算从 1 到 10 的和,但并不是通过 main 函数进行运算,而是创建两个线程,其中一个线程计算 1 到 5 的和,另一个线程计算 6 到 10 的和main 函数只负责输出运算结果。这种方式的线程模型称为「工作线程」。显示该程序的执行流程图:
![](https://i.loli.net/2019/02/03/5c55c330e8b5b.png)
![](images/5c55c330e8b5b.png)
下面是代码:
@@ -303,7 +303,7 @@ gcc thread3.c -D_REENTRANT -o tr3 -lpthread
结果:
![](https://i.loli.net/2019/02/03/5c55c53d70494.png)
![](images/5c55c53d70494.png)
可以看出计算结果正确,两个线程都用了全局变量 sum ,证明了 2 个线程共享保存全局变量的数据区。
@@ -368,7 +368,7 @@ gcc thread4.c -D_REENTRANT -o tr4 -lpthread
结果:
![](https://i.loli.net/2019/02/03/5c55c884e7c11.png)
![](images/5c55c884e7c11.png)
从图上可以看出,每次运行的结果竟然不一样。理论上来说,上面代码的最后结果应该是 0 。原因暂时不得而知,但是可以肯定的是,这对于线程的应用是个大问题。
@@ -559,7 +559,7 @@ gcc mutex.c -D_REENTRANT -o mutex -lpthread
运行结果:
![](https://i.loli.net/2019/02/03/5c567e4aafbb8.png)
![](images/5c567e4aafbb8.png)
从运行结果可以看出,通过互斥量机制得出了正确的运行结果。
@@ -697,7 +697,7 @@ gcc semaphore.c -D_REENTRANT -o sema -lpthread
结果:
![](https://i.loli.net/2019/02/03/5c568c2717d1e.png)
![](images/5c568c2717d1e.png)
从上述代码可以看出设置了两个信号量sem_one 的初始值为 0sem_two 的初始值为 1然后在调用函数的时候「读」的前提是 sem_two 可以减 1如果不能减 1 就会阻塞在这里,一直等到「计算」操作完毕后,给 sem_two 加 1然后就可以继续执行下一句输入。对于「计算」函数也一样。
@@ -751,7 +751,7 @@ gcc chat_clnt.c -D_REENTRANT -o cclnt -lpthread
结果:
![](https://i.loli.net/2019/02/03/5c569b70634ff.png)
![](images/5c569b70634ff.png)
### 18.6 习题