1. 进程/线程/协程(纤程)的区别
想象一个工厂(计算机)的生产过程。
- 进程是不同的生产车间,每个车间有自己独立的场地和设备。
- 线程是车间里的不同生产线,共享车间大部分资源,各有各的工作流程。
- 协程(纤程)是生产线上的工人,在同一条生产线上灵活地交替工作,切换非常迅速且简单。
1.1. 定义
- 进程:资源分配的最小单位,这里的资源指内存。
- 线程:调度和执行的最小单位,即CPU隔离。
- 协程:用户态的轻量级线程。纤程是Windows系统对协程的一个具体实现,而协程是一个更广泛的概念,由编程语言或运行时库支持。一个线程可以有多个纤程。
1.2. 独立性
- 进程:独立的地址空间(虚拟内存),包含代码、数据、堆栈以及其他系统资源。
- 线程:共享进程资源,但拥有各自的控制流程。
- 协程:共享线程资源,但轻量级。
1.3. 通信与切换
- 进程:进程间通信需要专门的机制(IPC),开销大。
- 线程:线程间切换开销小于进程。
- 协程:用户态切换,开销极小。
1.4. 调度方式
- 进程:操作系统内核调度。
- 线程:操作系统内核调度。
- 协程:用户程序自身控制。
1.5. 适用场景
- 进程:需要高隔离性和资源管理的场景。
- 线程:需要大量并发执行任务的场景。
- 协程:异步I/O、事件驱动编程等。
2. 进程间通信的方式(IPC)
用餐厅经营的例子对这 6 种 IPC 方式总结:
- 管道:像餐厅单向的传菜口,厨师放菜,服务员取菜。
- 消息队列:类似外卖订单列表,按顺序处理订单。
- 信号:好比火灾警报,响起后所有人按预定流程行动。
- 信号量:想象成有限的餐桌数,有桌就能坐,没桌需等待。
- 共享内存:类似于食材库存清单,多人可查看和修改。
- Socket:如同与供应商的电话,内部和外部都能交流。
2.1. 管道(Pipe)
分为无名管道和有名管道。
- 无名管道用于有亲缘关系的进程之间,如父子进程;
- 有名管道用于无亲缘关系的进程之间。
- 管道是单向的,如果需要双向通信,通常需要创建两个管道。
- 读写操作要注意关闭不需要的端口,避免出现错误。
- 管道的容量是有限的,如果写入端写入速度过快,可能会导致阻塞。
- 有名管道的读进程和写进程打开管道的时机可能会影响通信效果。如果读进程先打开,它会阻塞等待写进程打开;如果写进程先打开并写入数据后关闭,读进程再打开就能立即读取数据。
- 有名管道在文件系统中以一个特殊的文件形式存在,但它不是普通的文件,而是用于进程间通信的。
2.2. 消息队列
消息被发送到队列中,接收进程从队列中获取消息。消息队列克服了管道只能承载无格式字节流的缺点,可以传递有格式的数据。
2.3. 共享内存
多个进程可以共享一块内存区域,对其进行读写操作,实现快速的数据交换。但需要配合同步机制(如信号量)来保证数据的一致性。
2.4. 套接字(Socket)
可用于同一机器上的进程间通信,也可用于不同机器间的网络通信。
2.5. 信号(Signal)
用于通知接收进程发生了某种特定的事件。
2.6. 信号量(Semaphores)
信号量是一个计数器,用于多进程对共享数据的访问,这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
3. 用户态和内核态
3.1. 什么是用户态和内核态
- 用户态:是指程序运行时的普通状态。在用户态下,程序只能访问有限的资源和执行受限的操作,比如进行一些基本的计算、访问自身的内存空间等。这是为了保证系统的稳定性和安全性,防止用户程序对系统造成破坏或非法访问关键资源。
- 内核态:则具有更高的权限。操作系统的核心部分,如设备驱动程序、内存管理、进程调度等关键操作都在内核态下运行。在内核态,程序可以访问系统的所有资源和执行所有特权指令。
打个比方,操作系统就像一个城堡。用户态下的程序就像是城堡外的居民,只能在规定的区域活动,遵循一定的规则。而内核态就像是城堡内的核心区域,只有经过授权的“工作人员”(内核程序)才能进入并进行关键的管理和控制操作。
例如,当用户程序需要读取硬盘上的文件时,它会从用户态切换到内核态,由操作系统内核来完成这个复杂且涉及底层硬件的操作,完成后再切换回用户态将结果返回给用户程序。
3.2. 用户态和内核态切换的3种方式
以下是一个新的餐厅相关的例子来类比切换的三种方式:
- 系统调用:采购特殊食材需向监管部门申请许可。
- 异常切换:某个顾客突然因食物异物大闹,要请监管人员。
- 中断切换:供应商说食材无法按时送达,要处理供应问题。
- 系统调用:当用户程序需要执行如文件操作、网络通信、进程管理等需要内核权限的任务时,会通过系统调用陷入内核态。
- 异常:例如,除数为 0 、内存访问违规等错误情况发生时,会触发异常,导致从用户态切换到内核态,让内核来处理异常。
- 外部设备中断:当外部设备(如硬盘、网卡等)完成了数据传输或发生了其他事件,向 CPU 发送中断信号,此时 CPU 会从用户态切换到内核态来处理中断。