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,仅当高优先级线程阻塞时其他线程才有机会运行。