目标:理解线程与进程的本质区别、状态模型、调度机制,以及在 OpenHarmony 轻量系统中的实现


一、进程(Process)是什么?

1.1 核心定义

进程是操作系统进行资源分配和调度的基本单位,是程序的一次执行实例。

┌─────────────────────────────────────────┐
│              进程(Process)              │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  │
│  │ 代码段  │  │ 数据段  │  │ 堆栈段  │  │
│  │ (Text)  │  │ (Data)  │  │ (Stack) │  │
│  └─────────┘  └─────────┘  └─────────┘  │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  │
│  │ 打开文件 │  │ 信号处理 │  │ 地址空间 │  │
│  │  列表   │  │  程序   │  │ (虚拟内存)│  │
│  └─────────┘  └─────────┘  └─────────┘  │
│                                         │
│  资源所有权:内存、文件、设备、句柄等       │
└─────────────────────────────────────────┘

1.2 进程的组成

组成部分 说明 特点
代码段(Text) 程序指令的二进制代码 只读,可被多个进程共享
数据段(Data) 全局变量、静态变量 可读写
堆(Heap) 动态分配的内存(malloc) 向上增长,需手动管理
栈(Stack) 局部变量、函数参数、返回地址 向下增长,自动管理
进程控制块(PCB) 操作系统管理进程的数据结构 包含 PID、状态、优先级等

1.3 进程的状态模型

        ┌─────────┐
        │  创建   │  ← fork() / 系统初始化
        │ (New)   │
        └────┬────┘
             ↓
        ┌─────────┐     调度器选择     ┌─────────┐
   ┌──→ │  就绪   │ ────────────────→ │  运行   │
   │    │(Ready)  │                   │(Running)│
   │    └─────────┘ ←──────────────── └────┬───┘
   │         ↑      时间片用完/被抢占       │
   │         │                             ↓ I/O 请求 / 等待事件
   │    ┌────┴────┐                  ┌─────────┐
   └────┤  阻塞   │ ← I/O完成/事件到达 │  阻塞   │
        │(Blocked)│                  │(Blocked)│
        └─────────┘                  └─────────┘
                                           ↓
                                      ┌─────────┐
                                      │  终止   │ ← exit() / 被杀死
                                      │(Terminated)
                                      └─────────┘

三态模型:就绪(Ready)→ 运行(Running)→ 阻塞(Blocked)


二、线程(Thread)是什么?

2.1 核心定义

线程是 CPU 调度的基本单位,是进程内的执行流。一个进程可以包含多个线程,它们共享进程的资源,但拥有独立的执行上下文。

┌─────────────────────────────────────────┐
│              进程(Process)              │
│         共享资源:代码、数据、堆、文件等     │
│  ┌─────────────────────────────────────┐ │
│  │  线程1  │  线程2  │  线程3  │ ...   │ │
│  │ ┌────┐ │ ┌────┐ │ ┌────┐ │         │ │
│  │ │寄存器│ │ │寄存器│ │ │寄存器│ │         │ │
│  │ │程序计数器│ │ │程序计数器│ │ │程序计数器│ │         │ │
│  │ │栈指针 │ │ │栈指针 │ │ │栈指针 │ │         │ │
│  │ │状态字 │ │ │状态字 │ │ │状态字 │ │         │ │
│  │ └──┬─┘ │ └──┬─┘ │ └──┬─┘ │         │ │
│  │    │    │    │    │    │    │         │ │
│  │ 独立栈  │  独立栈  │  独立栈  │         │ │
│  └─────────────────────────────────────┘ │
│         线程私有:寄存器、程序计数器、栈      │
└─────────────────────────────────────────┘

2.2 线程的组成

组成部分 说明 是否共享
线程 ID(TID) 线程唯一标识 私有
程序计数器(PC) 下一条指令地址 私有
寄存器组 通用寄存器、状态寄存器 私有
栈(Stack) 局部变量、函数调用链 私有
线程局部存储(TLS) 线程私有全局变量 私有
代码段 程序指令 ✅ 进程内共享
数据段/堆 全局变量、动态内存 ✅ 进程内共享
打开文件 文件描述符表 ✅ 进程内共享

2.3 线程的状态

线程状态与进程类似,但更轻量:

┌─────────┐   创建   ┌─────────┐   调度   ┌─────────┐
│  新建   │ ──────→ │  就绪   │ ──────→ │  运行   │
│ (New)   │         │(Ready)  │         │(Running)│
└─────────┘         └────┬────┘         └───┬────┘
                         ↑                  │
                         │    阻塞事件       ↓
                    ┌────┴────┐        ┌─────────┐
                    │  阻塞   │ ←───── │  阻塞   │
                    │(Blocked)│  事件到达 │(Blocked)│
                    └─────────┘        └─────────┘
                                              ↓
                                         ┌─────────┐
                                         │  终止   │
                                         │(Dead)   │
                                         └─────────┘

三、进程 vs 线程:核心对比

对比维度 进程(Process) 线程(Thread)
定义 资源分配的基本单位 CPU 调度的基本单位
内存空间 独立的地址空间 共享进程的地址空间
切换开销 大(需切换页表、刷新 TLB) 小(只需切换寄存器和栈)
通信方式 IPC(管道、消息队列、共享内存、套接字) 直接读写共享变量(需同步机制)
创建开销 大(复制地址空间) 小(共享大部分资源)
崩溃影响 不影响其他进程 可能导致整个进程崩溃
并发性 低(重量级) 高(轻量级)
系统限制 数量受内存限制 数量受栈空间限制

3.1 形象比喻

比喻 进程 线程
工厂模型 一家独立的工厂(有独立场地、设备、工人) 工厂内的生产线(共享场地设备,独立执行任务)
厨房模型 独立的厨房(有独立食材、厨具、厨师) 厨房内的厨师(共享食材厨具,各自做菜)
程序模型 打开的两个 Word 文档(独立编辑) 一个 Word 内的拼写检查和自动保存(同时运行)

四、OpenHarmony 轻量系统中的线程

4.1 系统架构

OpenHarmony 轻量系统(LiteOS-M)是单进程多线程架构:

┌─────────────────────────────────────────┐
│         OpenHarmony 轻量系统             │
│         (单进程,多线程)               │
│                                         │
│  ┌─────────────────────────────────────┐ │
│  │           内核空间                   │ │
│  │  ┌─────────┐  ┌─────────┐          │ │
│  │  │ 线程调度 │  │ 内存管理 │          │ │
│  │  │ (Tick)  │  │ (Heap)  │          │ │
│  │  └─────────┘  └─────────┘          │ │
│  └─────────────────────────────────────┘ │
│                                         │
│  ┌─────────────────────────────────────┐ │
│  │           用户空间                   │ │
│  │  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐  │ │
│  │  │主线程│ │任务A│ │任务B│ │中断 │  │ │
│  │  │Init │ │Thread│ │Thread│ │Handler│ │
│  │  └─────┘ └─────┘ └─────┘ └─────┘  │ │
│  │                                     │ │
│  │  共享:全局变量、代码段、堆内存        │ │
│  │  私有:栈空间(每个线程独立)          │ │
│  └─────────────────────────────────────┘ │
└─────────────────────────────────────────┘

关键特点

  • 没有独立的"进程"概念,整个系统运行在一个地址空间
  • 所有线程共享全局内存
  • 线程切换只需保存/恢复寄存器和栈指针

4.2 线程在 Hi3861 中的实现

Hi3861 使用 LiteOS-M 内核,线程即 LiteOS 的 任务(Task)

┌─────────────────────────────────────────┐
│           LiteOS-M 任务管理              │
│                                         │
│  ┌─────────────────────────────────────┐ │
│  │  就绪队列(按优先级排序)              │ │
│  │  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐   │ │
│  │  │Prio0│→│Prio1│→│Prio2│→│Prio3│→...│ │
│  │  │High │ │     │ │     │ │Low  │    │ │
│  │  └─────┘ └─────┘ └─────┘ └─────┘   │ │
│  └─────────────────────────────────────┘ │
│                                         │
│  ┌─────────────────────────────────────┐ │
│  │  任务控制块(TCB)                    │ │
│  │  ┌─────────┐  ┌─────────┐          │ │
│  │  │任务ID   │  │栈指针    │          │ │
│  │  │优先级   │  │程序计数器│          │ │
│  │  │状态     │  │寄存器保存│          │ │
│  │  │栈大小   │  │任务入口  │          │ │
│  │  └─────────┘  └─────────┘          │ │
│  └─────────────────────────────────────┘ │
└─────────────────────────────────────────┘

4.3 CMSIS-RTOS2 线程 vs LiteOS-M 任务

CMSIS-RTOS2 概念 LiteOS-M 实现 说明
osThread LOS_Task 线程 = 任务
osThreadNew() LOS_TaskCreate() 创建任务
osThreadSuspend() LOS_TaskSuspend() 挂起任务
osThreadResume() LOS_TaskResume() 恢复任务
osThreadTerminate() LOS_TaskDelete() 删除任务
osPriorityNormal 优先级数值 数值越小优先级越高

五、线程调度机制

5.1 抢占式调度

LiteOS-M 使用 优先级抢占 + 时间片轮转 调度:

时间轴 →

Tick 0:  [Task A: Prio 3] ← 运行
         [Task B: Prio 5]   就绪
         [Task C: Prio 7]   就绪

Tick 10: [Task B 就绪,Prio 5 > Task A 的 3?否] → Task A 继续

Tick 20: [Task D 就绪,Prio 2] → 抢占!
         [Task D: Prio 2] ← 运行(抢占)
         [Task A: Prio 3]   就绪(被抢占)
         [Task B: Prio 5]   就绪
         [Task C: Prio 7]   就绪

Tick 30: [Task D 阻塞/I/O] → 调度最高就绪
         [Task A: Prio 3] ← 运行(恢复)

规则

  1. 高优先级任务就绪 → 立即抢占低优先级任务
  2. 同优先级任务 → 时间片轮转(默认 10ms)
  3. 任务阻塞(等待 I/O、延时)→ 让出 CPU

5.2 线程状态转换

        ┌─────────┐
        │  创建   │  osThreadNew()
        │ (New)   │
        └────┬────┘
             ↓
        ┌─────────┐     调度器选择     ┌─────────┐
   ┌──→ │  就绪   │ ────────────────→ │  运行   │
   │    │(Ready)  │                   │(Running)│
   │    └────┬────┘ ←──────────────── └────┬────┘
   │         ↑      时间片用完/被抢占      │
   │         │                             ↓ osDelay / osMutexAcquire
   │    ┌────┴────┐                  ┌─────────┐
   └────┤  阻塞   │ ← 延时到期/锁释放  │  阻塞   │
        │(Blocked)│                  │(Blocked)│
        │         │                  │         │
        │ osDelay │                  │ osMutex │
        │ 到期    │                  │ 释放    │
        └─────────┘                  └─────────┘
                                           ↓
                                      ┌─────────┐
                                      │  终止   │  osThreadTerminate()
                                      │(Dead)   │
                                      └─────────┘

六、线程栈(Stack)详解

6.1 栈的作用

栈是线程私有的内存区域,用于:

用途 说明
局部变量 函数内定义的变量
函数参数 传递给子函数的参数
返回地址 函数调用后返回到哪里
寄存器保存 上下文切换时保存寄存器值
中断现场 中断发生时保存 CPU 状态

6.2 栈溢出危害

栈空间(假设 1024 字节):
┌─────────────────────────────────────────┐ ← 栈底(高地址)
│              已使用区域                   │
│  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐     │
│  │main │ │funcA│ │funcB│ │funcC│ ...  │
│  │局部 │ │局部 │ │局部 │ │大数组│      │
│  │变量 │ │变量 │ │变量 │ │[1024]│      │  ← 栈溢出!超过 1024 字节
│  └─────┘ └─────┘ └─────┘ └─────┘     │
│              ↑                        │
│           栈指针 SP                    │
├─────────────────────────────────────────┤
│              未使用区域                   │
│              ...                        │
└─────────────────────────────────────────┘ ← 栈顶(低地址)

溢出后果:
1. 覆盖其他线程的栈 → 数据损坏
2. 覆盖全局变量 → 程序逻辑错误
3. 覆盖代码段 → 程序崩溃(HardFault)

Hi3861 栈大小建议

线程类型 建议栈大小 说明
简单任务 512 ~ 1024 字节 少量局部变量,无深层调用
一般任务 1024 ~ 2048 字节 正常函数调用,中等局部变量
复杂任务 2048 ~ 4096 字节 深层递归、大数组、网络协议栈
中断处理 与线程共享 中断使用当前线程的栈

七、线程安全与临界区

7.1 什么是线程安全?

线程安全:多个线程同时访问共享资源时,程序行为正确,数据一致。

线程不安全示例(无保护):

Thread A:  读取 g_count = 5  →  计算 5+1 = 6  → [被抢占]
Thread B:  读取 g_count = 5  →  计算 5+1 = 6  →  写入 g_count = 6
Thread A:  [恢复] 写入 g_count = 6

结果:g_count = 6,但期望 = 7(两次++)
      一次更新丢失!

7.2 临界区保护方法

方法 适用场景 特点
关闭中断 单核、极短临界区 最简单,但影响实时性
互斥锁(Mutex) 长临界区、可阻塞 支持递归、优先级继承
自旋锁(Spinlock) 多核、短临界区 忙等待,不阻塞
原子操作 简单变量读写 硬件指令保证,无锁

Hi3861 是单核 Cortex-M0+,常用方法

// 方法1:关中断(最轻量)
uint32_t intSave = LOS_IntLock();   // 保存并关闭中断
// ... 临界区 ...
LOS_IntRestore(intSave);             // 恢复中断

// 方法2:互斥锁(推荐,支持阻塞等待)
osMutexAcquire(mid, osWaitForever);
// ... 临界区 ...
osMutexRelease(mid);

八、总结

要点 内容
进程 资源分配单位,独立地址空间,重量级
线程 CPU 调度单位,共享进程资源,轻量级
OpenHarmony 轻量系统 单进程多线程,线程 = LiteOS-M 任务
线程组成 私有:寄存器、PC、栈;共享:代码、数据、堆
调度机制 优先级抢占 + 时间片轮转
栈管理 每个线程独立栈,注意溢出风险
线程安全 共享资源需临界区保护(关中断 / 互斥锁)

九、下一步

继续 Day 4:软件定时器(Timer) —— 异步事件处理机制。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐