Linux pthread 实时线程开发总结

Linux pthread 实时线程开发总结

核心工程架构:硬件中断独占Core0,所有上层业务线程跑在Core1,是嵌入式设备最稳的实时隔离方案

一、pthread 是什么?为什么BSP工程师一定要学?

做BSP开发,我们主要负责底层驱动适配、设备节点注册、中断调试,保证硬件能正常工作。但设备真正跑起来的上层功能,比如屏幕显示、触摸点击、屏幕背光、摄像头拍照录像,全部离不开多线程。

这些功能的运行频率、响应要求、运行逻辑都不一样。如果只用单线程串行跑,一定会出现屏幕卡顿、触摸丢点、画面花屏、数据丢失等问题。而 Linux 上层实现并发的核心手段,就是 pthread 多线程。简单说:BSP把硬件打通,pthread把上层业务跑稳

1.1 日常开发常用核心API

平时写上层实时业务,用到的pthread函数就这一套,所有多线程项目通用:

  • pthread_create:创建工作线程,可以绑定调度策略、优先级、CPU核心

  • pthread_join:等待线程跑完,回收内核资源,避免产生僵尸线程

  • pthread_exit:线程主动退出,支持自定义返回数据

  • pthread_attr_init / pthread_attr_destroy:初始化、销毁线程配置参数(属性模板)

  • pthread_setschedparam:设置线程调度策略和实时优先级

  • pthread_setaffinity_np:线程绑定指定CPU核心,隔离中断和业务,减少卡顿抖动

  • pthread_cancel / pthread_setcancelstate:管控线程退出,保证关键业务不会被中途杀崩

二、pthread 线程完整生命周期

这是绝大多数新手绕不过的坑:线程还在运行,为什么代码里要提前销毁attr属性?

核心原因就一句话:配置模板 和 真正的线程是两个完全独立的东西

2.1 两种资源彻底分清(必记)

  • 线程属性 attr(pthread_attr_t)------ 只是配置图纸

    它只是用户态的临时结构体,只存优先级、调度策略、绑核这些配置。只在创建线程的那一刻有用,线程创建成功后,这张"图纸"就彻底没用了,删掉完全不影响线程运行。

  • 线程实体 tid(pthread_t)------ 真正干活的线程

    这是内核真正创建出来的任务,包含线程栈、运行上下文,是真正在循环跑业务的主体。这个资源必须用pthread_join 回收,不然会产生僵尸线程、内存泄漏。

2.2 工程标准执行流程(所有业务线程统一这套逻辑)

不管是背光、触控、屏幕刷新、摄像头采集,所有常驻线程流程完全一致:

  1. 初始化attr属性模板

  2. 配置FIFO调度、实时优先级、绑定CPU核心

  3. 调用pthread_create创建线程(内核自动复制配置,和本地attr解绑)

  4. 立刻销毁attr模板(释放临时内存,防污染、防泄漏)

  5. 线程while(1)常驻跑业务逻辑

  6. 需要休眠/退出时,安全cancel终止线程

  7. 主线程join回收内核线程资源

2.3 线程退出机制?

  • 常驻线程不主动退出、不 cancel → 永不释放。

  • 线程退出但不 join → 内核资源泄漏。

  • join 成功 / 进程退出 → 彻底释放内核资源。

2.4 为什么创建完线程一定要销毁attr?

  • 多个线程共用属性模板会导致参数错乱,优先级、绑核配置互相干扰

  • 长期上电运行的设备,不释放会累积内存泄漏

  • 是Linux嵌入式量产代码的规范写法

三、实时线程两种调度策略怎么选?

Linux实时线程只有两种可用策略:SCHED_FIFO、SCHED_RR。普通的SCHED_OTHER没有实时优先级,只能做普通后台任务,不适合交互、采集类核心业务。

3.1 SCHED_FIFO(工程首选)

这是95%的嵌入式实时业务都会用的策略。

  • 规则:高优先级线程一旦运行,会一直占用CPU,自己休眠/阻塞才会让出

  • 优点:延迟极低、运行稳定、没有随机卡顿

  • 适用:屏幕刷新、触摸读取、摄像头采集、定时控制等核心实时业务

3.2 SCHED_RR(时间片轮转,极少用)

  • 规则:优先级不同依旧高优先;优先级相同时,按时间片轮流执行

  • 作用:防止单个任务卡死CPU,给同优先级任务均分资源

  • 适用:多个同等重要、同等优先级的实时任务并发跑的场景

3.3 最终选型结论

做屏幕、触摸、相机、控制类项目,直接默认用 SCHED_FIFO ,稳定、延迟低。只有遇到多个平级实时任务需要互相谦让CPU资源时,才用SCHED_RR。

ARM Linux嵌入式实时开发中,95%以上的上层核心业务优先使用SCHED_FIFO ,保障人机交互、数据采集的低抖动、高确定性;仅当多个优先级一致的实时任务并发运行 ,需要均衡CPU资源、防止单任务独占核心时,选用SCHED_RR

四、系统优先级层级与业务分级标准

做实时开发最关键的一点:线程优先级永远不能高于硬件中断,否则会阻塞中断、丢数据、系统卡死。

4.1 系统整体优先级从高到低

硬件中断 > 软中断 > 实时线程(FIFO/RR) > 普通线程

另外要记住:ARM Linux 硬件中断优先级是芯片GIC硬件固定的,软件改不了,我们只能调整线程优先级。

4.2 线程优先级数值范围

  • 实时线程:1~99,数字越大优先级越高

  • 普通线程:固定0级,无实时性

  • 避坑:不要设99满级,会抢占系统内核线程,导致死机、卡顿,最高用到90即可

4.3 上层业务优先级分层(行业通用标准)

根据人机交互体验重要程度,优先级从高到低排布,直接套用即可:

业务类型 调度策略 推荐优先级 说明
LCD画面刷新 SCHED_FIFO 70~80 界面流畅度优先级最高,杜绝卡顿、撕裂
触控TP读取 SCHED_FIFO 60~70 保证触摸跟手,不丢点、不延迟
摄像头图像采集 SCHED_FIFO 60~75 防止丢帧、花屏、图像数据覆盖
背光调节任务 SCHED_FIFO 30~40 后台低频任务,不抢占核心交互业务
普通UI/业务逻辑 SCHED_OTHER 0 非实时任务,页面跳转、状态展示等

五、工业标准实时架构:中断与业务核隔离

想要设备长期稳定、低抖动,必须用大小核隔离方案,这是所有工控、消费设备的通用架构。

5.1 核心分工

  • Core0(专属中断核):只跑硬件中断,LCD、TP、Camera、背光等所有硬件中断都绑在这里,不跑任何业务线程

  • Core1(专属业务核):所有pthread上层线程全部跑在这里,只管业务逻辑,不干扰硬件中断

这么做的好处非常明显:硬件中断响应零延迟,上层业务再繁忙也不会拖垮底层硬件。

六、线程安全退出机制(Cancel实战用法)

常驻线程不能一直死跑,设备休眠、页面退出、异常重启时,需要安全杀掉线程。但绝对不能乱杀,否则会导致数据错乱、花屏、崩溃。

6.1 两个核心函数

  • pthread_setcancelstate:控制当前线程能不能被外部杀死

    • PTHREAD_CANCEL_ENABLE:允许取消,用于普通空闲阶段

    • PTHREAD_CANCEL_DISABLE:禁止取消,用于关键数据处理阶段

  • pthread_cancel:发送线程退出请求,不会立刻杀死线程,只会在sleep、read等阻塞点位生效

6.2 各业务线程保护规则(实战必守)

  • LCD刷新、图像采集:帧拷贝、画面渲染期间禁止取消,防止花屏和内存泄漏

  • 触控读取:坐标解析、事件上报期间禁止取消,避免触摸逻辑错乱

  • 背光线程:逻辑简单,全程允许取消,方便设备休眠快速释放资源

七、完整可编译实战代码(背光+触控+LCD刷新)

下面代码整合了上面所有知识点:核隔离、FIFO调度、分层优先级、安全退出、标准线程生命周期,可直接在ARM Linux设备编译运行。

c 复制代码
/*****************************************
 * 嵌入式上层多业务实时线程实战代码
 * 架构:Core0跑中断、Core1跑所有上层业务
 * 功能:同时常驻背光检测、触控采集、LCD画面刷新
 * 适配:ARM Linux + PREEMPT-RT 实时内核
 * 特点:完全工程化流程、安全线程、无内存泄漏
 *****************************************/
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sched.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>

// 业务线程统一绑定Core1,和中断核Core0物理隔离
#define CPU_CORE 1

// 业务优先级分层(按人机体验重要程度排序)
#define LCD_REFRESH_PRIO  80   // 画面刷新优先级最高,保证流畅
#define TP_INPUT_PRIO    65   // 触控次之,保证跟手
#define BACKLIGHT_PRIO   35   // 背光后台任务,优先级最低

/*****************************************
 * 函数:set_rt_thread_attr
 * 功能:统一配置实时线程属性,代码复用
 * 配置内容:FIFO调度、自定义优先级、绑定Core1
 * 说明:attr仅作为创建线程的临时模板
 *****************************************/
void set_rt_thread_attr(pthread_attr_t *attr, int prio)
{
    struct sched_param param;
    cpu_set_t cpuset;

    pthread_attr_init(attr);
    pthread_attr_setschedpolicy(attr, SCHED_FIFO);
    param.sched_priority = prio;
    pthread_attr_setschedparam(attr, &param);
    pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);

    // 绑定到业务核心Core1
    CPU_ZERO(&cpuset);
    CPU_SET(CPU_CORE, &cpuset);
    pthread_attr_setaffinity_np(attr, sizeof(cpu_set_t), &cpuset);
}

/*****************************************
 * 背光线程
 * 功能:轮询检测息屏、调整亮度
 * 特性:后台低优先级、可随时取消
 *****************************************/
void *backlight_thread(void *arg)
{
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    while(1)
    {
        printf("Core1 背光线程:检测息屏与亮度状态\n");
        usleep(500000);
    }
    return NULL;
}

/*****************************************
 * 触控TP线程
 * 功能:高频读取触摸坐标、手势
 * 特性:关键读取阶段禁止取消,防止数据异常
 *****************************************/
void *tp_input_thread(void *arg)
{
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    while(1)
    {
        // 关键业务段保护,禁止线程被杀死
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

        printf("Core1 触控线程:采集触摸坐标数据\n");
        usleep(30000);

        // 空闲阶段恢复取消能力
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    }
    return NULL;
}

/*****************************************
 * LCD刷新线程
 * 功能:界面UI渲染、帧缓冲区刷新
 * 特性:最高业务优先级,60fps稳定刷新
 *****************************************/
void *lcd_refresh_thread(void *arg)
{
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    while(1)
    {
        printf("Core1 LCD刷新线程:渲染界面画面\n");
        usleep(16000); // 16ms 60fps
    }
    return NULL;
}

/*****************************************
 * 主函数:标准工程线程创建流程
 * 初始化模板 -> 创建线程 -> 销毁模板 -> 常驻保活
 *****************************************/
int main()
{
    pthread_t tid_backlight, tid_tp, tid_lcd;
    pthread_attr_t attr_bl, attr_tp, attr_lcd;

    // 1. 初始化各路线程配置
    set_rt_thread_attr(&attr_bl, BACKLIGHT_PRIO);
    set_rt_thread_attr(&attr_tp, TP_INPUT_PRIO);
    set_rt_thread_attr(&attr_lcd, LCD_REFRESH_PRIO);

    // 2. 批量创建常驻业务线程
    pthread_create(&tid_backlight, &attr_bl, backlight_thread, NULL);
    pthread_create(&tid_tp, &attr_tp, tp_input_thread, NULL);
    pthread_create(&tid_lcd, &attr_lcd, lcd_refresh_thread, NULL);

    // 3. 线程创建完成,立即销毁属性模板(释放临时资源)
    pthread_attr_destroy(&attr_bl);
    pthread_attr_destroy(&attr_tp);
    pthread_attr_destroy(&attr_lcd);

    // 4. 主线程常驻等待,回收内核资源
    pthread_join(tid_backlight, NULL);
    pthread_join(tid_tp, NULL);
    pthread_join(tid_lcd, NULL);

    return 0;
}

八、代码编译、运行权限配置

8.1 编译命令

pthread属于扩展库,编译必须加 -lpthread 链接:

gcc rt_business_thread.c -o rt_business_thread -lpthread

8.2 运行权限问题

Linux 普通用户没有权限修改实时优先级,直接运行会失败,必须用root权限:

sudo ./rt_business_thread

8.3 普通用户永久开启实时权限

调试频繁可以修改权限配置文件,永久放开权限,不用每次都sudo。修改 /etc/security/limits.conf,添加:

Plain 复制代码
* soft rtprio 99
* hard rtprio 99

重启设备后生效。

九、核心总结

整篇笔记浓缩为5条工程铁律,做上层实时开发完全够用:

  1. 模板和线程相互独立:attr属性只用来创建线程,创建完立刻销毁;线程资源必须join回收

  2. 调度策略优先FIFO:绝大多数核心实时业务直接用FIFO,稳定低延迟

  3. 优先级严格分层:画面刷新 > 触控采集 > 后台背光,保证用户体验优先

  4. 大小核隔离架构:Core0扛中断、Core1跑业务,彻底解决调度抖动

  5. 关键代码防取消:数据读写、画面渲染阶段禁止线程取消,保证业务稳定

相关推荐
Ai拆代码的曹操13 小时前
K8s Pod Pending 逐层排查:从 FailedScheduling 到 PVC StorageClass 不存在的实战记录
java·linux·kubernetes
wangbing112514 小时前
JPA下自定义主键
java·linux·服务器
bush415 小时前
正点原子imx6ull-uboot,奇怪的问题
linux·学习
dddwjzx15 小时前
嵌入式Linux C应用编程——Framebuffer应用编程
linux·嵌入式
weixin_4640780715 小时前
Altium Designer自定义变压器
嵌入式硬件
QQ52862112416 小时前
芯片程序提取的具体流程和方法
stm32·单片机·嵌入式硬件·程序·pcb抄板
今天也想躺ping16 小时前
linux系统移植pjsua库实现sip通话功能
linux·sip·交叉编译
LingzhiPi17 小时前
零知派ESP32-S3-智能小车控制系统(4.2)-红外双目跟随模块使用
单片机·嵌入式硬件
hkj880817 小时前
Linux 总线-设备-驱动(Bus-Device-Driver)完整协作原理
linux·运维·microsoft
早点睡啊Y17 小时前
深入学 LangChain 官方文档(一):总览、安装与快速开始
linux·服务器·langchain