phase 5 part 5: cs algorithms, network-os, web-security topics with tested examples; all 43 stacks pass --strict
This commit is contained in:
1 parent
f695bbd7cc
commit
691f738f5f
22 files changed
+1955
-37
No files matched your search
@@ -0,0 +1,90 @@
|
||||
---
|
||||
id: cs.network-os.os-basics
|
||||
title: 操作系统基础:进程线程协程、I/O 模型与多路复用、内存与虚拟内存、文件描述符、死锁
|
||||
type: lesson
|
||||
domain: cs
|
||||
stack: [network-os]
|
||||
tags: [进程, 线程, 协程, GIL, I/O 多路复用, epoll, 虚拟内存, OOM, 文件描述符, 死锁]
|
||||
level: basic
|
||||
prerequisites: [cs.network-os.http-tcp]
|
||||
est_hours: 3
|
||||
related: [python.asyncio.index, devops.linux.troubleshooting, cs.network-os.interview]
|
||||
week: [14, 16]
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 操作系统基础:进程线程协程、I/O 模型与多路复用、内存与虚拟内存、文件描述符、死锁
|
||||
|
||||
## 解释说明
|
||||
|
||||
### 进程、线程、协程
|
||||
|
||||
| | 进程 | 线程 | 协程 |
|
||||
| --- | --- | --- | --- |
|
||||
| 资源 | 独立地址空间 | 共享进程的内存 | 共享线程 |
|
||||
| 切换 | 内核,开销最大 | 内核,开销中等 | 用户态,开销最小 |
|
||||
| 通信 | 管道、套接字、共享内存 | 共享变量 + 锁 | 共享变量(单线程内无需锁,但要注意 await 之间的状态变化) |
|
||||
| 例子 | Gunicorn 多 worker | Java 线程池 | Go goroutine(多线程调度)、Python asyncio(单线程) |
|
||||
|
||||
**Python 的 GIL**:同一进程中同一时刻只有一个线程执行 Python 字节码;I/O 密集用线程或 asyncio 都可以,CPU 密集用多进程(Python 3.13 起有可选的无 GIL 自由线程构建,3.14 起官方支持,生态仍在适配,以官方文档为准)。
|
||||
|
||||
### I/O 模型
|
||||
|
||||
- 阻塞 I/O:一个连接占一个线程。
|
||||
- **I/O 多路复用**(select / poll / **epoll**):一个线程监视大量文件描述符,内核告诉你哪些就绪。Nginx、Redis、Node.js、asyncio 都基于它。
|
||||
- 异步 I/O(io_uring 等):提交请求,完成后通知。
|
||||
|
||||
### 内存
|
||||
|
||||
- **虚拟内存**:每个进程看到独立的地址空间,由页表映射到物理内存;缺页时从磁盘加载。
|
||||
- 栈(函数调用、局部变量)和堆(动态分配)。
|
||||
- **OOM**:内存不足时内核选择进程杀掉;容器超过内存上限被 OOMKilled(退出码 137)。
|
||||
|
||||
### 文件描述符
|
||||
|
||||
Linux 中一切皆文件:文件、套接字、管道都是文件描述符(fd)。每个进程有上限(`ulimit -n`),连接泄漏会导致 “Too many open files”。
|
||||
|
||||
### 死锁
|
||||
|
||||
四个必要条件:互斥、持有并等待、不可抢占、循环等待。预防:固定加锁顺序、加锁超时、减小锁粒度。数据库中的死锁同理(两个事务以相反顺序更新两行)。
|
||||
|
||||
## 案例
|
||||
|
||||
Python Agent 服务用 Uvicorn(asyncio):大量请求在等待模型返回(I/O),单个进程就能支撑很多并发连接;但如果在请求处理中同步调用了一个耗时的 CPU 操作(例如解析大 PDF),整个事件循环被阻塞,所有请求都卡住 —— 应该放到进程池或单独的任务队列。
|
||||
|
||||
## 代码示例
|
||||
|
||||
单线程 + `selectors`(Linux 上为 epoll)同时服务 50 个连接([multiplex_server.py](examples/tcp-basics/multiplex_server.py),运行通过):
|
||||
|
||||
```python
|
||||
sel = selectors.DefaultSelector()
|
||||
sel.register(srv, selectors.EVENT_READ, data=None)
|
||||
while not stop.is_set():
|
||||
for key, _ in sel.select(timeout=0.05):
|
||||
if key.data is None: # 新连接
|
||||
conn, _ = srv.accept()
|
||||
conn.setblocking(False)
|
||||
sel.register(conn, selectors.EVENT_READ, data="client")
|
||||
else: # 已有连接可读
|
||||
data = key.fileobj.recv(1024)
|
||||
...
|
||||
```
|
||||
|
||||
```text
|
||||
$ python multiplex_server.py
|
||||
单线程同时服务 50 个连接,全部回显正确
|
||||
```
|
||||
|
||||
## 易错点
|
||||
|
||||
- 在 asyncio 中调用阻塞函数(`time.sleep`、同步 HTTP 客户端、CPU 密集计算)。
|
||||
- 认为多线程一定能提升 Python 的 CPU 密集任务性能。
|
||||
- 连接、文件打开后不关闭,fd 耗尽。
|
||||
- 容器内存上限设得过小,进程被 OOMKilled 却在应用日志中找不到原因。
|
||||
|
||||
## 面试常问
|
||||
|
||||
- 进程、线程、协程的区别?
|
||||
- 什么是 I/O 多路复用?epoll 相比 select 好在哪里?
|
||||
- 什么是死锁?怎么避免?
|
||||
- Python 的 GIL 是什么?对你的服务有什么影响?
|
||||
Reference in new issue
Block a user