线程 --- 嵌入式(Linux)

1、线程的基本概念

线程是进程内的最小执行单元,也被称为轻量级进程(LWP)。一个进程可以包含多个线程,所有线程共享进程的核心资源,同时拥有自己独立的执行上下文(如程序计数器、寄存器、栈)。

2、线程和进程的区别

维度 进程 线程
资源分配 操作系统资源分配的最小单元(独立的地址空间、文件描述符、内存等) 操作系统CPU任务调度的最小单元(不独立分配资源,共享所属进程的资源)
地址空间 每个进程有独立的虚拟地址空间 无独立地址空间,共享进程的文本区、数据区、堆区、文件描述符、信号处理等;仅独享栈区、寄存器上下文、程序计数器 (PC)、线程 ID
独立性 完全独立,一个进程崩溃不影响其他进程 高度依赖进程,一个线程崩溃会导致整个进程终止(进程内所有线程退出)
通信 / 同步 需借助 IPC(管道、消息队列、共享内存等),成本高 直接读写共享变量即可通信,需通过锁 / 信号量等机制解决同步问题
切换开销 大(需切换地址空间、页表等) 小(仅切换执行上下文,无需修改地址空间)

3、线程的创建

  • 调用 pthread_create 创建线程时,系统会为该线程分配独立的栈空间 (Linux 默认栈大小为 8MB,可通过 ulimit -s 查看 / 修改);
  • 线程的文本区(代码)、数据区(全局 / 静态变量)、堆区(动态分配内存)、文件描述符等,均复用所属进程的资源;
  • 线程创建成功后,新线程与主线程并发执行(无固定执行顺序,由 CPU 调度决定)。

4、线程的调度

  • 线程调度规则基本等同于进程调度(支持抢占式调度、时间片轮转、优先级调度等);
  • 宏观上:多个线程 "同时" 执行(并行),因为 CPU 多核或快速切换;
  • 微观上:单核 CPU 中,同一时刻只有一个线程在执行(串行),依赖 CPU 时间片切换实现 "伪并行"。

5、线程的消亡

  • 等同于进程的消亡,线程消亡需要回收线程空间
  • 线程消亡的触发条件:
    1. 线程函数执行完毕(隐式退出);
    2. 调用 pthread_exit 主动退出;
    3. 进程终止(所有线程随之消亡);
    4. pthread_cancel 取消(被动退出)。
  • 线程消亡后必须回收其资源(线程空间):
    • 若未回收,线程会变成 "僵尸线程",占用系统资源(类似僵尸进程);
    • 回收方式:pthread_join(阻塞回收)、pthread_detach(分离线程,系统自动回收)。

6、多线程和多进程优缺点

特性 多进程 多线程
安全性(稳定性) 高:地址空间独立,一个进程崩溃不影响其他进程 低:共享进程资源,一个线程崩溃导致整个进程终止;需处理资源竞争 / 线程安全问题
执行效率 低(切换 / 通信开销大)因为切换进程任务时需要映射不同的物理地址空间,增大系统开销 高(切换 / 通信开销小)因为在同一进程空间内部切换不同的任务
编程复杂度 低(无需处理资源竞争) 高(需处理同步 / 互斥问题)
资源占用 高(每个进程独立占资源) 低(共享进程资源)
适用场景 独立任务、对稳定性要求高(如后台服务、多程序运行) 高并发、数据共享频繁(如网络服务、GUI 程序)
资源竞争 无:地址空间独立,无共享资源竞争 有:共享数据区 / 堆区,需通过互斥锁、条件变量等防止 "竞态条件"
通信难度 高:需 IPC(管道、共享内存等)多进程通信不方便,因为空间独立,没有共享空间 低:直接访问共享变量(全局 / 堆),仅需同步机制(锁 / 信号量)多线程通信非常方便,多线程数据区、堆区共享

7、线程相关函数接口

7.1 pthread_create

  • 函数原型
cpp 复制代码
#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
  • 功能:创建一个新线程,线程启动后执行start_routine函数;
  • 参数:
    • thread:输出参数,存放新线程的 ID(注意:可用 %#x pthread_self()获取id );
    • attr:线程属性(如栈大小、分离状态),默认属性传NULL
    • start_routine:线程函数入口(函数指针),必须是 void* (*)(void*) 类型(返回值和参数均为 void*);
    • arg:传给线程函数的参数(若传递栈变量地址,需确保变量生命周期长于线程(避免野指针);
  • 返回值:成功返回 0,失败返回错误码 (不是 errno,需用 strerror() 解析,如 perror 不适用);
  • 编译注意:必须链接 pthread 库,编译命令加-lpthread(如gcc test.c -o test -lpthread)。

7.2 pthread_exit

  • 函数原型
cpp 复制代码
void pthread_exit(void *retval);
  • 功能:主动终止当前线程,且不会影响同进程的其他线程;
  • 参数:retval:线程退出的返回值(可被pthread_join()获取),若无需返回值传NULL
  • 注意:主线程调用pthread_exit()仅终止自身,子线程仍会继续执行;若主线程直接return,则整个进程终止。
  • 返回值:缺省

7.3 pthread_join

  • 函数原型
cpp 复制代码
int pthread_join(pthread_t thread, void **retval);
  • 功能:阻塞等待指定线程终止,并回收其资源(避免僵尸线程);
  • 参数:
    • thread:要回收的线程 ID;
    • retval:输出参数,存放线程的返回值(即pthread_exit()retval),无需获取则传NULL
  • 返回值:成功返回 0,失败返回错误码(如线程已分离、线程不存在);
  • 核心作用:
    1. 阻塞回收线程空间资源;
    2. 实现线程同步(主线程等待子线程执行完毕)。
    3. 注意:一个线程只能被一个 pthread_join 回收,重复调用会失败。

示例(创建 + 回收线程):

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

void *thread_func(void *arg) {
    char *msg = (char *)arg;
    printf("子线程ID:%lu,收到参数:%s\n", (unsigned long)pthread_self(), msg);
    
    // 堆内存,手动分配,主线程回收
    int *ret_val = (int *)malloc(sizeof(int));
    *ret_val = 100;
    pthread_exit((void *)ret_val); // 返回堆地址
}

int main() {
    pthread_t tid;
    char *msg = "Hello Thread";
    int err;

    err = pthread_create(&tid, NULL, thread_func, (void *)msg);
    if (err != 0) {
        printf("创建线程失败:%s\n", strerror(err));
        exit(1);
    }
    printf("主线程:创建的子线程ID为%lu\n", (unsigned long)tid);

    void *ret;
    err = pthread_join(tid, &ret);
    if (err != 0) {
        printf("回收线程失败:%s\n", strerror(err));
        exit(1);
    }
    // 访问堆数据
    printf("主线程:子线程退出,返回值为%d\n", *(int *)ret);
    free(ret); // 必须手动释放堆内存,避免内存泄漏

    return 0;
}
相关推荐
wsad05322 小时前
Linux Shell脚本执行方式全解析:source、点号、路径、bash与exec的区别
linux·运维·bash·shell
Whoami!2 小时前
⓬⁄₅ ⟦ OSCP ⬖ 研记 ⟧ Linux权限提升 ➱ 寻觅暴露的机密信息实现提权
linux·网络安全·信息安全·权限提升
济6172 小时前
ARM Linux 驱动开发篇--嵌入式 Linux LED 驱动开发实验(2)--Linux 下 LED 灯驱动开发代码编写-- Ubuntu20.04
linux·arm开发·驱动开发
草莓熊Lotso2 小时前
Linux 磁盘基础:从物理结构到 CHS/LBA 寻址,吃透数据存储底层逻辑
linux·运维·服务器·c++·人工智能
叠叠乐2 小时前
EasyTier 免费自建自用5$每个月的服务器
linux·运维·bash
yueyin1234562 小时前
Linux下MySQL的简单使用
linux·mysql·adb
鸠摩智首席音效师11 小时前
如何在 Linux 中将文件复制到多个目录 ?
linux·运维·服务器
香蕉你个不拿拿^11 小时前
Linux进程地址空间解析
linux·运维·服务器
人间打气筒(Ada)11 小时前
Linux学习~日志文件参考
linux·运维·服务器·学习·日志·log·问题修复