1. 进程、线程、协程
这是一个从“重”到“轻”,从“底层”到“上层”的演进过程。
进程
- 是什么:资源分配的最小单位。一个程序运行的实例。它拥有独立的内存空间(堆、栈)、文件描述符、网络连接等。进程之间是相互隔离的,一个进程崩溃通常不会影响其他进程。
- 切换成本:高。因为切换进程需保存和恢复整个内存映像、寄存器等,这被称为“上下文切换”。
- 对PHP的意义:
- PHP-FPM:你最熟悉的PHP进程管理器。它采用Master-Worker多进程模型。
- Master进程:负责管理,读取配置、分配端口、管理Worker进程(启动、关闭、重启)。
- Worker进程:多个独立的进程,每个进程内部串行地处理HTTP请求。这就是为什么在PHP中,全局变量、静态变量在同一个请求内是共享的,但在不同请求间是隔离的——因为每个请求都由一个全新的或复用的Worker进程处理。
- Cliché:一个请求,一个进程。
- PHP-FPM:你最熟悉的PHP进程管理器。它采用Master-Worker多进程模型。
线程
- 是什么:CPU调度的最小单位通过。一个进程能够包含多个线程,这些线程共享进程的大部分资源(如内存空间、文件句柄),但每个线程有自己独立的栈和寄存器。
- 切换成本:中。比进程低,由于它们共享地址空间,无需切换内存映射。
- 对PHP的意义:
- PHP本身对多线程的帮助并不广泛。存在pthreads扩展,但它要求ZTS(Zend Thread Safety)模式,且与很多扩展不兼容,生产环境很少应用。
- 重要性在于理解:很多你用到的软件是多线程的。例如,MySQL、Redis、Java应用服务器。理解线程能帮你理解这些组件的并发模型和潜在的死锁问题。
协程
- 是什么:用户态的轻量级线程,也常被称为“微线程”。协程的调度完全在用户空间由程序员或运行时库(如Swoole,Swow)控制,而不是由操作系统内核调度。协程在同一个线程内执行。
- 核心机制:协作式调度。一个协程执行到某个点(如遇到I/O操作)时,会主动让出(yield)CPU,让另一个协程运行。等I/O准备好了,再恢复(resume)执行。
- 切换成本:极低。因为切换只在用户空间进行,不涉及内核态的系统调用,只应该保存少量寄存器状态。
- 对PHP的意义:革命性的。
- Swoole/Swow:这些异步并行框架的核心就是协程。它们让你能用同步的代码写法,实现异步的性能。
// 同步阻塞写法(在FPM中) $result1 = $db->query('SELECT * FROM table1'); $result2 = $db->query('SELECT * FROM table2'); // 总时间 ~ 时间1 + 时间2 // 协程异步写法(在Swoole中) go(function () use ($db) { $result1 = $db->query('SELECT * FROM table1'); }); go(function () use ($db) { $result2 = $db->query('SELECT * FROM table2'); }); // 总时间 ~ max(时间1, 时间2),因为两个查询并发执行了。- 这使得PHP能够轻松处理数万甚至数十万的并发连接,应用于IM、物联网、微服务等场景。
2. I/O模型
这是解决“当程序需要等待外部数据(如数据库查询、API调用、读取资料)时,CPU应该做什么”的问题。
阻塞I/O
- 场景:PHP-FPM的默认模式。
- 过程:Worker进程发起一个I/O调用(如
file_get_contents)→ 进程被操作系统挂起,进入睡眠状态 → 直到材料准备好,操作系统才唤醒进程,进程继续执行。 - 比喻:你在餐厅点餐,点完后就一直坐在那里盯着厨房门,什么都不做,直到服务员把菜端上来。
非阻塞I/O
- 过程:进程发起I/O调用 → 如果数据没准备好,立即返回一个错误(EAGAIN/EWOULDBLOCK),而不是挂起进程 → 进程可以继续做其他事情,并时不时地回来“问一下”(轮询)材料好了没。
- 比喻:你点完餐后,不坐在那里干等去玩手机、和朋友聊天,但就是,而每隔几分钟就跑去厨房门口问一句“好了吗?”。
I/O多路复用
这是非阻塞I/O的高效实践方案,是Nginx、Redis、Swoole等高性能服务器的基石。
- 核心:使用
select、poll、epoll(Linux)或kqueue(BSD)这些系统调用。一个进程许可同时监视多个文件描述符(如网络连接)的状态。 - 过程:进程把成百上千个连接交给
epoll监视 → 然后阻塞在epoll_wait上 → 当其中任何一个连接的数据准备好了,epoll_wait就返回,告诉进程哪些连接准备好了 → 进程再去处理这些准备好的连接。 - 比喻:餐厅有一个服务员专门帮你盯着(
epoll)。你点完餐后就可以去玩手机。当你的菜好了,该服务员会主动来通知你,你再去取。这个服务员可以同时为很多顾客服务。
异步I/O
- 过程:进程发起一个I/O调用,并指定一个回调函数 →调用立即返回,进程继续执行 → 内核在整个I/O操作(从发起请求到数据从内核空间拷贝到用户空间)完成后,主动通知进程并执行回调函数。
- 与I/O多路复用的区别:I/O多路复用通知的是“数据准备好了,你能够来读了”(读这个动作还是需要进程自己阻塞地去完成),而AIO通知的是“信息已经读好了,给你”。
- 比喻:你点了一份外卖,付完钱就可以完全不管了。外卖小哥会把餐直接送到你手上。
为什么这对PHP程序员至关重要?
理解PHP-FPM的瓶颈:你知道为什么一个FPM Worker在数据库查询时会被“卡住”,以及为什么需要通过调整
pm.max_children来应对并发,因为它是阻塞式的多进程模型。工艺选型的依据:
- 当得开发一个便捷的Web展示站时,Laravel + FPM是绝佳选择,创建效率高。
- 当需要开发一个实时聊天服务或高性能API网关时,你会知道必须选择Swoole 或 Swow,因为它们是协程 + I/O多路复用通过模型,能够轻松应对海量并发。
性能优化的方向:
- 在FPM下,优化主要靠减少I/O等待(如加缓存、优化SQL)和增加进程数。
- 在Swoole下,优化主要靠利用协程实现并发和调整协程参数。
理解整个技术栈:你能明白为什么Nginx能轻松处理数万并发连接(I/O多路复用),而传统的Apache prefork模型(多进程,一连接一进程)在高并发下会很吃力。
总结:
| 概念 | 在PHP世界的体现 | 解决的问题 |
|---|---|---|
| 进程 | PHP-FPM Worker | 资源隔离、稳定性 |
| 线程 | 较少使用,但需理解周边生态 | CPU密集型任务并行(在其它语言中) |
| 协程 | Swoole, Swow | 高并发I/O,用同步代码实现异步性能 |
| 阻塞I/O | 标准PHP函数(fread, file_get_contents) | 编程模型简便 |
| I/O多路复用 | Nginx, Swoole事件循环 | 用单线程/少量进程处理海量连接 |
| 异步I/O | 理想的模型,Linux AIO不完善 | 极致的I/O性能 |
掌握这些操作系统基础知识,你就不再是一个仅仅“写业务逻辑”的码农,而是一个能够理解系统运行原理、诊断深层挑战、并做出正确架构决策的工程师。这正是你技能高度的体现。