Unix 发展史概览
这里是一个简明清晰的 Unix 发展史概览,涵盖从起源到现代的重要节点和演变过程。
Unix 发展史概览
1. Unix 起源(1969年)
- 贝尔实验室:Ken Thompson 和 Dennis Ritchie 开发出 Unix 操作系统。
- 最初设计目标:简洁、可移植、多任务。
- 用汇编语言开发,后期移植为 C 语言,极大推动了跨平台发展。
2. 早期版本(1970年代)
-
Unix Version 1 - 6(1971-1975):
- 基础系统设计与完善。
- Version 6 (V6) 是首个广泛传播的版本,成为众多高校教学和研究基础。
-
Unix Version 7(1979):
- 被称为第一个“现代Unix”,功能更完备。
- 引入 Bourne Shell、多种系统调用等。
3. Unix 分支与商业化(1980年代)
-
BSD Unix(伯克利软件发行版)
- 加入网络协议栈(TCP/IP)、虚拟内存支持。
- BSD 4.2 和 4.3 成为高性能的学术与商业系统基础。
-
AT&T System V
- 商业版本 Unix,广泛推广。
- 引入 System V Interface Definition(SVID),统一标准。
-
Unix Wars
- BSD 和 System V 争夺市场主导权,导致标准割裂。
4. 标准化与统一(1990年代)
-
POSIX 标准
- 统一操作系统接口,促使各种 Unix 系统兼容。
-
Unix 品牌管理
- IEEE 和 The Open Group 推出 “The Single UNIX Specification”。
-
Linux 崛起(1991起)
- Linus Torvalds 开发的免费 Unix-like 内核,快速普及。
5. 现代 Unix 与 Unix-like 系统(2000年代至今)
-
商业 Unix 系统
- 如 IBM AIX、HP-UX、Solaris 等继续发展。
-
开源 Unix-like 系统
- BSD 系列(FreeBSD、OpenBSD、NetBSD)广泛应用。
- Linux 作为主流服务器和嵌入式平台。
小结时间线
1969 Unix诞生(贝尔实验室)
1970s Unix早期版本(V1-V7)
1980s BSD Unix & System V 发展与分裂
1990s POSIX标准化,Linux兴起
2000s 现代Unix与开源Unix-like系统共存
在 UNIX V6 中(特别是 PDP-11 架构下),只有进程(process)概念,没有线程(thread) 的支持。我们来详细对比一下:
🧠 1. UNIX V6 的执行单元是「进程」
✅ 每个进程有:
- 独立的 地址空间(虽然没有 MMU,但通过 relocation/base 寄存器实现简单隔离)
- 独立的 堆栈、代码段、数据段
- 独立的 打开的文件描述符列表
- 独立的 用户态 / 内核态栈
- PCB(进程控制块):
proc
结构体
📌 换句话说:一个进程就是一个完整的程序的运行实例
❌ 2. UNIX V6 没有线程
🚫 不存在:
- 多个线程共享同一地址空间
- 用户态或内核态的线程库
- 没有 pthread、也没有类似
clone()
的系统调用
在 UNIX V6 中,线程等同于进程的最小执行单元还未诞生。
📚 补充:现代对比
特性 | UNIX V6 | 现代 Linux / Windows |
---|---|---|
线程支持 | ❌ 无 | ✅ 有 pthread / clone / TCB |
地址空间隔离 | ✅ 基于 relocation | ✅ 基于 MMU |
并发机制 | ✅ 多进程 + fork() | ✅ 多进程 + 多线程 |
同步原语 | ❌ 无 | ✅ Mutex、Sem、Cond 等 |
✳️ 总结一句话:
UNIX V6 的“并发”全靠
fork()
创建多个独立进程完成,没有线程这个概念。
UNIX 在早期(包括 UNIX V6/V7)没有线程的概念,所有的并发都是通过 fork() 创建进程来实现的,每个进程有独立的地址空间,没有共享内存,也没有线程同步原语。
“LWP(轻量级进程,Light Weight Process)” 是线程发展历史中一个非常关键的概念,尤其在早期 UNIX(如 Solaris)中引入,是连接用户线程与内核线程的中间层。下面我详细解释:
✅ 什么是 LWP(Light Weight Process)?
LWP 是一种内核调度单位,它像一个普通进程一样能被内核调度,但:
- 它和传统的 UNIX 进程相比更轻量
- 允许多个用户线程绑定到一个或多个 LWP 上
- 引入 用户线程(user-level thread)与内核线程(kernel-level thread)之间的分离
🧠 为什么引入 LWP?
早期 UNIX:
- 每个进程是调度单位
- 多线程程序会导致所有线程都阻塞(因为调度粒度是整个进程)
引入 LWP 的目的:
- 实现线程调度 不完全依赖内核
- 用户空间可以快速切换线程,减少系统调用
- 实现 M:N 模型(多个用户线程映射到多个内核线程)
🔧 结构示意图
用户空间+--------------------------------+| 用户线程1 用户线程2 ... |+--------------------------------+↓ ↓LWP1 LWP2 <- 绑定关系↓ ↓内核线程1 内核线程2 <- LWP由内核调度
📘 Solaris 中的 LWP 模型(经典代表)
概念 | 描述 |
---|---|
用户线程 | 程序员使用的线程接口(如 thr_create ) |
LWP | 用户线程与内核之间的桥梁,由内核调度 |
内核线程 | OS 级别线程,是 CPU 的实际执行单位 |
M:N 模型 | 多个用户线程通过多个 LWP 共享少量内核线程 |
🧵 与 POSIX 线程的关系
- POSIX
pthreads
抽象的是用户线程接口 - 在 Solaris 中,
pthreads
是基于 LWP 实现的 - 在 Linux 中,
pthreads
实际上就是 每个线程 = 一个 LWP = 一个内核线程,是 1:1 模型
🔍 LWP 的优劣
优点 | 缺点 |
---|---|
用户线程调度效率高 | 实现复杂(需要管理映射关系) |
可以复用 LWP,节省系统资源 | 用户线程阻塞时,LWP 也会被阻塞 |
更灵活:适合 IO 密集 + CPU 密集 | 很难在 Linux 等平台通用实现 |
🧠 总结一句话:
LWP 是一种由内核调度的轻量进程,用于支持用户线程的执行,是早期 M:N 多线程模型的关键组成。
现代主流系统(如 Linux、macOS)已经普遍采用 1:1 模型(每个线程一个内核线程),放弃了复杂的 M:N 和 LWP,但它在线程设计历史上具有里程碑意义。
是的,RT-Thread Smart 中的 LWP(Light Weight Process) 与 Solaris、早期 UNIX 系统中的 LWP 含义有相似之处,但也有一些区别,下面我来详细说明:
✅ RT-Thread Smart 中的 LWP 是什么?
RT-Thread Smart 是 RT-Thread 的类 Unix 扩展(用户空间 + 内核空间),LWP 是 Smart 中的用户态进程模型实现的核心结构,代表一个用户空间进程。
在 RT-Thread Smart 中,LWP(轻量级进程):
- 是一种用户态进程,具有独立的地址空间
- 拥有独立的虚拟内存、文件描述符表、信号处理等
- 内核调度的是线程(
rt_thread
),线程属于某个 LWP - 一个 LWP 可以有多个线程(共享用户空间)
🧠 和传统 UNIX 的 LWP 比较
特性 | Solaris / UNIX LWP | RT-Thread Smart LWP |
---|---|---|
所属系统 | UNIX(如 Solaris) | RT-Thread Smart |
实质 | 用户线程的桥梁 / 调度单位 | 用户空间进程 |
是否被内核调度 | 是(间接调度) | 不是(线程调度) |
是否有独立地址空间 | 否(多个线程共享) | 是(每个 LWP 是独立地址空间) |
线程关系 | 多个线程通过 LWP 映射到内核线程 | 多个线程属于同一个 LWP,共享资源 |
内核视角 | LWP 是调度单位 | LWP 是运行环境(调度单位是 rt_thread ) |
➤ 可以简单类比为:
- UNIX LWP ≈ 内核调度用的线程容器
- RT-Thread LWP ≈ 独立的用户进程(带地址空间)
📦 RT-Thread Smart 中的进程-线程模型
结构大致如下:
内核空间
+----------------------------+
| RT-Thread |
| +---------------------+ |
| | rt_thread(线程) | <----- 调度单位
| +---------------------+ |
| | LWP(进程) | <----- 用户进程环境
| +---------------------+ |
+----------------------------+用户空间(每个进程)
+----------------------------+
| main() 用户代码 |
| libc, heap, stack, ... |
+----------------------------+
✳️ 为什么叫 LWP?
这个命名是借鉴 UNIX 世界的“轻量级进程”的概念,强调:
- 它不是传统大内核进程(如 Linux task_struct 那种)
- 它比传统进程更“轻”,适用于嵌入式环境
- 支持独立地址空间、用户态运行,是 Smart 支持应用层的核心机制
✅ 总结
RT-Thread Smart 的 LWP 是“用户态进程”的抽象,与传统 UNIX 的 LWP 都强调轻量、可调度、可扩展,但意义略有差异。
方面 | RT-Thread Smart 中的 LWP |
---|---|
本质 | 用户进程(独立地址空间) |
是否支持线程 | 是,多个线程属于一个 LWP |
是否被调度 | 否,调度的是线程 |
内存隔离 | 支持 MMU 的隔离(Smart 模式) |
在具有多线程与多进程支持的操作系统(如 Linux、RT-Thread Smart 等)中,调度属于不同进程的线程和调度同一进程内的线程,在实现层面有一些关键区别,主要体现在以下几个方面:
✅ 1. 地址空间切换(上下文)
✅ 不同进程的线程切换
- 需要切换地址空间(切换页表或 MMU 上下文);
- 内核必须刷新 MMU 页表基地址,切换到另一个进程的内存视图;
- 代价更高,因为涉及 TLB flush、cache coherence 等;
- 所有进程私有的数据(堆、栈、代码段)都会发生变化。
👉 举个例子:
进程 A 的线程 T1 → 切换 → 进程 B 的线程 T2
系统必须切换页表,所有内存访问都按 B 的上下文来解释。
✅ 同一进程的线程切换
- 无需切换地址空间,因为线程共享进程的地址空间;
- 只需要切换线程的 CPU 上下文(寄存器、堆栈等);
- 开销较小,调度速度快;
- 常见于用户态多线程库、POSIX 线程(pthread)等。
👉 举个例子:
进程 A 的线程 T1 → 切换 → 进程 A 的线程 T2
页表不动,仅换栈、寄存器等上下文。
✅ 2. 资源管理与权限
不同进程的线程:
- 拥有独立的资源和权限;
- 文件描述符、信号处理器、环境变量等互不干扰;
- 更适合服务隔离、安全场景。
同一进程的线程:
- 共享几乎所有资源:堆、静态区、文件句柄、socket 等;
- 适合高效协作、低开销计算,但不利于隔离。
✅ 3. 调度粒度与策略
调度项目 | 同进程线程切换 | 跨进程线程切换 |
---|---|---|
时间片轮转 | 通常一样 | 通常一样 |
优先级调度 | 适用 | 适用 |
地址空间切换 | 否(保留) | 是(重载) |
系统负担 | 较低(仅 CPU 上下文) | 较高(CPU + MMU 上下文) |
✅ 4. 在 RT-Thread Smart 中的例子
RT-Thread Smart 的 LWP + thread 模型支持这两类调度:
示例结构:
进程 A(LWP 1)├── 线程 T1└── 线程 T2进程 B(LWP 2)└── 线程 T3
T1 ↔ T2
切换:共享地址空间,仅切换rt_thread
T2 ↔ T3
切换:需要执行地址空间切换(虚拟内存重新映射)
✅ 总结表格
比较项 | 同进程线程切换 | 跨进程线程切换 |
---|---|---|
是否切换地址空间 | 否 | 是 |
切换开销 | 低(快) | 高(慢) |
资源共享程度 | 高(几乎完全共享) | 低(完全隔离) |
安全性 | 差 | 好 |
典型用途 | 多线程并发 | 多任务/服务隔离 |
好问题!关于“进程和线程的关系”以及“多进程多线程的调度”,我来详细解释一下:
1. 一个进程是一个内核线程吗?
-
传统意义上:不完全是。
-
进程(Process) 是资源分配的单位,拥有独立地址空间、打开的文件等。
-
线程(Thread) 是 CPU 调度的基本单位,负责执行代码。
在不同操作系统模型中的关系:
模型类型 | 说明 |
---|---|
1:1 模型(如 Linux) | 每个线程是一个内核线程,进程是线程集合。 进程本身不调度,调度的是线程。 |
M:N 模型(如 Solaris 旧版、RT-Thread Smart) | 用户线程与内核线程映射不是一一对应。进程里可有多个用户线程,通过多个内核线程调度。 |
单线程进程模型(如早期 UNIX) | 一个进程对应一个执行单元(线程),即进程就是内核线程。 |
2. 多个进程多个线程如何调度?
在现代系统(1:1 模型,如 Linux):
-
内核维护所有线程的 PCB(Linux 中是 task_struct,每个线程一个);
-
调度器按时间片、优先级等策略从所有线程里选择一个运行;
-
线程调度时,如果跨进程(切换地址空间),内核完成页表切换;
-
同进程线程调度时,内核切换 CPU 上下文,无需切换地址空间。
举个例子:
假设有两个进程:
- 进程A 有线程A1、A2
- 进程B 有线程B1、B2、B3
内核调度器会在这 5 个线程中轮换执行,比如:
A1 → A2 → B1 → B2 → B3 → A1 → ...
切换不同进程线程时切换地址空间,同进程线程切换时地址空间不变。
3. 调度器怎么实现?
-
维护就绪队列:里面存放所有可运行的线程;
-
上下文切换:保存当前线程状态,恢复下一个线程状态;
-
切换页表(跨进程)或保持不变(同进程);
-
抢占机制:支持抢占式多任务,定时器中断触发调度。
4. 在 RT-Thread Smart 中
-
进程被称为 LWP(轻量级进程);
-
内核调度单位是线程(
rt_thread
); -
每个线程属于某个 LWP(进程);
-
调度器从所有线程中选一个运行;
-
切换不同 LWP 线程时,完成地址空间切换。
总结:
概念 | 说明 |
---|---|
进程 | 资源容器,拥有独立地址空间 |
线程 | CPU 调度单位,执行代码的“实体” |
一个进程=一个内核线程? | 早期 UNIX 是,现代系统否。 |
多进程多线程调度 | 统一调度所有线程,切换时根据是否跨进程切换地址空间。 |
下面是一个用 Mermaid 画的关于 多进程多线程调度 的简化流程图示例,重点表现线程调度时是否需要切换地址空间:
flowchart TDStart[调度器触发]SelectThread[选择下一个线程执行]SameProcess{是否与当前线程同一进程?}SwitchContext[切换线程上下文]SwitchAddressSpace[切换地址空间(页表切换)]RunThread[线程运行]TimerInterrupt[定时器中断]Start --> SelectThreadSelectThread --> SameProcessSameProcess -- 是 --> SwitchContextSameProcess -- 否 --> SwitchAddressSpaceSwitchAddressSpace --> SwitchContextSwitchContext --> RunThreadRunThread --> TimerInterruptTimerInterrupt --> Start
说明:
-
调度器周期性触发(通常由定时器中断触发)。
-
选择下一个要执行的线程。
-
判断是否是同一进程线程:
- 是:只切换线程上下文(寄存器、栈指针等)。
- 否:需要先切换地址空间(更新 MMU 页表),再切换上下文。
-
切换完成后,线程开始运行。
-
运行过程中,定时器中断再次触发调度。