Skip to content

概念先知道

  • POSIX:一套操作系统接口标准(pthread 线程、semaphore 信号量、mqueue 消息队列等)。在 Linux 上写多线程程序用的就是这些 API。
  • 兼容层(Compatibility Layer):让嵌入式系统“假装”自己是 Linux——FreeRTOS 上实现 POSIX API,代码可先在 PC 上写好、再移植到单片机。
  • pthread:POSIX 线程。pthread_create 创建线程,pthread_mutex_lock/unlock 加解锁,pthread_cond_wait/signal 条件变量,pthread_barrier_wait 栅栏同步。
  • POSIX 信号量与消息队列sem_open/sem_wait/sem_postmq_open/mq_send/mq_receive,是任务间同步和传数据的标准接口。
  • 与 FreeRTOS 原生 API 的关系:兼容层底层仍是 FreeRTOS 的任务/队列/信号量,只是换了 POSIX 的“皮肤”。选哪种取决于习惯与代码可移植性。

例程功能简介

本页对应博流官方 SDK 的 freertos_posix 例程(examples/posix/freertos_posix),演示 FreeRTOS POSIX 兼容层的用法:

  • 例程是一个 POSIX API 测试套件:9 组测试覆盖 pthread(创建/属性/互斥锁/条件变量/栅栏)、信号量、消息队列、时钟定时器、sched/unistd;
  • 通过 shell 命令 posix_test 一次跑全部,posix_test_single <group> 只跑指定组;
  • 每个用例统计 run/pass/fail/skip,最后 print_test_summary() 打印汇总;
  • 开启了 CONFIG_POSIX=y 后,工程中即可直接 #include <pthread.h><semaphore.h><mqueue.h> 等标准头文件。

提示

本页代码量不大但概念密:想动手写自己的多线程代码,先跑通 posix_test,再参考 test/ 目录下的用例写法。

操作步骤

1
进入例程目录

在终端进入 SDK 的 POSIX 兼容层例程目录(前提:已按快速开始(Linux)Windows搭建好环境):

cd examples/posix/freertos_posix
2
确认开启 POSIX 配置

例程 defconfig 已含 CONFIG_POSIX=y;若你自己新建工程,需要在 defconfig 中加上该行,或执行 make menuconfig 勾选 POSIX 支持后保存。

grep CONFIG_POSIX defconfig
3
编译工程

执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:

make CHIP=bl616 BOARD=bl616dk
4
烧录固件

用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
5
运行验证

打开串口助手(波特率 2000000),在 bouffalolab /> 提示符输入 posix_test 回车,串口依次运行 9 组测试(pthread、pthread_attr、mutex、cond、barrier、sem、mqueue、clock、sched)并打印 PASSED/FAILED 汇总;也可用 posix_test_single pthread 只跑一组。

代码执行流程

例程从启动到测试完成的流程如下:

例程调用的 API 介绍

pthread_create / pthread_join / pthread_attr_*(...)

创建/等待线程,设置线程属性(分离状态、栈大小、调度参数)。对应 test/test_pthread.c 等用例。

参数

  • thread:线程 ID 输出
  • attr:线程属性(可 NULL 用默认值)
  • start_routine / arg:线程函数与参数

返回值0 成功;错误码失败

pthread_mutex_lock / pthread_mutex_unlock(mutex)

互斥锁加锁/解锁,保护共享数据。测试中还覆盖了属性(pthread_mutexattr_*)与超时变体(pthread_mutex_timedlock)。

参数

  • mutex:互斥锁对象

返回值0 成功;错误码失败

sem_open / sem_wait / sem_post(...)

创建命名信号量并执行 P/V 操作(等待/释放),用于任务同步。

参数

  • name:信号量名(如 /sem0
  • value:初值
  • sem:信号量指针

返回值0 成功;-1 失败并置 errno

mq_open / mq_send / mq_receive(...)

创建消息队列并收发消息,是任务间传数据(带优先级)的标准接口。

参数

  • name:队列名
  • attr:队列属性(长度、消息大小)
  • msg / prio:消息数据与优先级

返回值mqd_t 描述符 / 成功发送字节数;失败返回 (mqd_t)-1

完整代码

以下为 posix/freertos_posix/main.c 完整源码,与官方示例一致,默认折叠,点击展开(各测试组在 test/ 子目录):

📜 点击展开 posix/freertos_posix/main.c 完整代码
c
#include <stdio.h>
#include <string.h>
#include "board.h"
#include "shell.h"
#include "bflb_core.h"
#include <FreeRTOS.h>
#include "task.h"
#include "test_common.h"

/* Forward declaration for shell_init_with_task (not declared in shell.h) */
extern void shell_init_with_task(struct bflb_device_s *uart);

/* Stack overflow hook - called when FreeRTOS detects stack overflow */
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
    printf("\r\n[STACK OVERFLOW] Task: %s\r\n", pcTaskName);
    while (1) {
        /* Halt to allow debugging */
    }
}

/* Global test counters */
int g_tests_run = 0;
int g_tests_passed = 0;
int g_tests_failed = 0;
int g_tests_skipped = 0;

/* External declarations for each test group */
extern void test_pthread_run(void);
extern void test_pthread_attr_run(void);
extern void test_pthread_mutex_run(void);
extern void test_pthread_cond_run(void);
extern void test_pthread_barrier_run(void);
extern void test_semaphore_run(void);
extern void test_mqueue_run(void);
extern void test_clock_timer_run(void);
extern void test_sched_unistd_run(void);

/* Run all tests */
static void run_all_tests(void)
{
    printf("\r\n===== POSIX API Test Suite Start =====\r\n\r\n");

    /* Reset counters */
    g_tests_run = 0;
    g_tests_passed = 0;
    g_tests_failed = 0;
    g_tests_skipped = 0;

    printf("--- 1. pthread tests ---\r\n");
    test_pthread_run();

    printf("\r\n--- 2. pthread_attr tests ---\r\n");
    test_pthread_attr_run();

    printf("\r\n--- 3. pthread_mutex tests ---\r\n");
    test_pthread_mutex_run();

    printf("\r\n--- 4. pthread_cond tests ---\r\n");
    test_pthread_cond_run();

    printf("\r\n--- 5. pthread_barrier tests ---\r\n");
    test_pthread_barrier_run();

    printf("\r\n--- 6. semaphore tests ---\r\n");
    test_semaphore_run();

    printf("\r\n--- 7. mqueue tests ---\r\n");
    test_mqueue_run();

    printf("\r\n--- 8. clock/timer tests ---\r\n");
    test_clock_timer_run();

    printf("\r\n--- 9. sched/unistd tests ---\r\n");
    test_sched_unistd_run();

    print_test_summary();
}

/* Shell command: run all tests */
static void cmd_posix_test(int argc, char **argv)
{
    /* Run tests directly in shell task context */
    run_all_tests();
}
SHELL_CMD_EXPORT_ALIAS(cmd_posix_test, posix_test, Run all POSIX tests);

/* Shell command: run single test group */
static void cmd_posix_test_single(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: posix_test_single <group>\r\n");
        printf("Groups: pthread, pthread_attr, mutex, cond, barrier, sem, mqueue, clock, sched\r\n");
        return;
    }

    g_tests_run = 0;
    g_tests_passed = 0;
    g_tests_failed = 0;
    g_tests_skipped = 0;

    if (strcmp(argv[1], "pthread") == 0) {
        test_pthread_run();
    } else if (strcmp(argv[1], "pthread_attr") == 0) {
        test_pthread_attr_run();
    } else if (strcmp(argv[1], "mutex") == 0) {
        test_pthread_mutex_run();
    } else if (strcmp(argv[1], "cond") == 0) {
        test_pthread_cond_run();
    } else if (strcmp(argv[1], "barrier") == 0) {
        test_pthread_barrier_run();
    } else if (strcmp(argv[1], "sem") == 0) {
        test_semaphore_run();
    } else if (strcmp(argv[1], "mqueue") == 0) {
        test_mqueue_run();
    } else if (strcmp(argv[1], "clock") == 0) {
        test_clock_timer_run();
    } else if (strcmp(argv[1], "sched") == 0) {
        test_sched_unistd_run();
    } else {
        printf("Unknown test group: %s\r\n", argv[1]);
        return;
    }

    print_test_summary();
}
SHELL_CMD_EXPORT_ALIAS(cmd_posix_test_single, posix_test_single, Run single POSIX test group);

int main(void)
{
    board_init();

    printf("\r\n========================================\r\n");
    printf("  FreeRTOS POSIX Test Application\r\n");
    printf("========================================\r\n");
    printf("\r\nShell commands:\r\n");
    printf("  posix_test         - Run all tests\r\n");
    printf("  posix_test_single  - Run single test group\r\n");
    printf("\r\n");

    /* Initialize shell task */
    struct bflb_device_s *uart0 = bflb_device_get_by_name("uart0");
    shell_init_with_task(uart0);

    vTaskStartScheduler();   /* starts scheduler, never returns */

    while (1) {
        /* Should never reach here */
    }
}

FAQ

编译报找不到 pthread.h / semaphore.h?

说明工程没开 POSIX 兼容层。在 defconfig 中加入 CONFIG_POSIX=y(例程已带),或 make menuconfig 勾选后重新编译。

POSIX 接口和 FreeRTOS 原生接口,用哪个好?

没有绝对优劣:POSIX 接口可移植性好(代码可搬到 Linux 或其他 RTOS),FreeRTOS 原生接口更贴近底层、文档多。课程/作业常要求“标准接口”,工程实践常两者混用。

`posix_test` 跑出 FAILED 怎么办?

先确认板子型号与 defconfig 与例程一致(PSRAM/堆大小影响线程创建)。若个别用例失败,多半是栈或堆配置偏小,调大 configTOTAL_HEAP_SIZE 或线程 stacksize 再试。

测试耗时多久?

9 组全跑约几秒到十几秒,取决于时钟/定时器测试的等待时间。只调试某类接口时用 posix_test_single <group> 更快。

Released under the MIT License. Build Time 2026-09-11 14:52:23