2. 使用内核线程

任务目标

在 Zephyr RTOS 中创建并启动多个内核线程,通过设置不同优先级与延迟,观察线程调度顺序及优先级对执行次序的影响。本案例使用 Zephyr 原生线程 API(k_thread_create)与内核调度器,不依赖应用层抽象。

环境与依赖

  • Zephyr SDK(建议 v3.4+)
  • 开发板:qemu_x86(或任意支持多线程的板卡)
  • 所需头文件:<zephyr/kernel.h>、<zephyr/sys/printk.h>

核心实现

创建三个线程:高优先级线程(优先级 0)、中优先级线程(优先级 2)、低优先级线程(优先级 4)。每个线程循环打印自身标识并主动让出 CPU(k_yield),以便观察调度器按优先级选择就绪线程。主线程在启动子线程后退出,子线程持续运行。

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

/* 线程栈与线程控制块 */
#define STACK_SIZE 512

K_THREAD_STACK_DEFINE(high_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(mid_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(low_stack, STACK_SIZE);

struct k_thread high_thread;
struct k_thread mid_thread;
struct k_thread low_thread;

/* 线程入口函数 */
void thread_entry(void *arg1, void *arg2, void *arg3)
{
    int id = (int)(intptr_t)arg1;
    int count = 0;

    while (1) {
        printk("Thread %d (prio %d) running, count=%d\n",
               id, k_thread_priority_get(k_current_get()), count++);
        k_yield();  /* 主动让出 CPU,触发调度 */
        k_msleep(1); /* 避免单线程独占,模拟实际负载 */
    }
}

void main(void)
{
    printk("Main: creating threads...\n");

    /* 创建三个不同优先级的线程 */
    k_thread_create(&high_thread, high_stack, STACK_SIZE,
                    thread_entry, (void *)1, NULL, NULL,
                    0, K_USER, K_NO_WAIT);
    k_thread_create(&mid_thread, mid_stack, STACK_SIZE,
                    thread_entry, (void *)2, NULL, NULL,
                    2, K_USER, K_NO_WAIT);
    k_thread_create(&low_thread, low_stack, STACK_SIZE,
                    thread_entry, (void *)3, NULL, NULL,
                    4, K_USER, K_NO_WAIT);

    printk("Main: threads created, exiting main.\n");
    /* main 返回后,子线程继续运行 */
}

构建与运行

# 在 Zephyr 工程目录下(已设置 ZEPHYR_BASE)
west build -b qemu_x86 samples/hello_world/ -p always -d build/thread_demo \
  -- -DEXTRA_CONF_FILE=/dev/null  # 若需自定义配置可调整

# 若使用独立目录,将上述代码保存为 src/main.c 并创建 CMakeLists.txt:
# cmake_minimum_required(VERSION 3.20)
# find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
# project(thread_demo)
# target_sources(app PRIVATE src/main.c)

west build -b qemu_x86 -d build/thread_demo .
west build -t run -d build/thread_demo

验证方法与判定标准

运行后观察串口输出,应满足以下条件即判定通过:

  • 输出中出现 Thread 1、Thread 2、Thread 3 的交替打印,且 Thread 1(优先级 0)出现的频率明显高于其他线程。
  • 由于 k_yield 让出 CPU,调度器会选择当前最高优先级的就绪线程,因此高优先级线程会抢占执行,输出顺序呈现优先级 0 > 2 > 4 的统计特征。
  • 程序不会崩溃或卡死,三个线程持续运行,计数递增。

预期输出示例(仅展示调度顺序特征,实际次数因时序而异):

Main: creating threads...
Main: threads created, exiting main.
Thread 1 (prio 0) running, count=0
Thread 1 (prio 0) running, count=1
Thread 2 (prio 2) running, count=0
Thread 1 (prio 0) running, count=2
Thread 3 (prio 4) running, count=0
Thread 1 (prio 0) running, count=3
...

关键说明

  • 优先级数值越小优先级越高(Zephyr 调度规则)。
  • K_USER 表示用户线程;若使用 K_ESSENTIAL 则线程不可被抢占,此处用普通用户线程以观察调度。
  • k_msleep(1) 防止低优先级线程饿死,同时模拟真实负载;若移除,高优先级线程将独占 CPU,低优先级几乎无法运行——这也是优先级调度的直观体现。
  • 若需观察更明显的抢占效果,可将 k_msleep 注释掉,此时输出将几乎全部为 Thread 1,仅当高优先级线程阻塞时其他线程才有机会运行。