3. 优先级抢占

任务目标

在ThreadX中创建两个不同优先级的线程,通过共享变量和延时操作,直观验证高优先级线程能够抢占正在运行的低优先级线程。本案例使用QEMU模拟的ARM Cortex-M3平台,无需真实硬件即可运行。

环境依赖

  • ThreadX内核源码(本案例使用6.x版本)
  • ARM GCC交叉编译器(arm-none-eabi-gcc)
  • QEMU系统模拟器(qemu-system-arm)

核心实现

/* main.c - ThreadX优先级抢占演示 */
#include "tx_api.h"

/* 线程控制块与栈 */
TX_THREAD high_priority_thread;
TX_THREAD low_priority_thread;
static uint8_t high_stack[1024];
static uint8_t low_stack[1024];

/* 共享变量:记录当前运行线程ID */
volatile uint32_t current_thread_id = 0;
/* 抢占计数器:记录高优先级线程抢占次数 */
volatile uint32_t preemption_count = 0;

/* 高优先级线程(优先级5) */
void high_priority_entry(ULONG arg)
{
    (void)arg;
    while (1)
    {
        /* 记录当前运行线程 */
        current_thread_id = 1;
        
        /* 模拟短暂工作后主动让出CPU */
        tx_thread_sleep(10);
        
        /* 如果低优先级线程正在运行,说明发生了抢占 */
        if (current_thread_id == 2)
        {
            preemption_count++;
        }
    }
}

/* 低优先级线程(优先级15) */
void low_priority_entry(ULONG arg)
{
    (void)arg;
    while (1)
    {
        /* 记录当前运行线程 */
        current_thread_id = 2;
        
        /* 长时间运行,不主动让出CPU */
        volatile uint32_t delay = 0;
        for (delay = 0; delay < 200000; delay++);
        
        /* 如果高优先级线程在此期间运行过,则抢占已发生 */
        if (current_thread_id == 1)
        {
            preemption_count++;
        }
    }
}

/* 应用程序初始化 */
void tx_application_define(void *first_unused_memory)
{
    (void)first_unused_memory;
    
    /* 创建高优先级线程 */
    tx_thread_create(&high_priority_thread, "High",
                     high_priority_entry, 0,
                     high_stack, sizeof(high_stack),
                     5, 5, TX_NO_TIME_SLICE, TX_AUTO_START);
    
    /* 创建低优先级线程 */
    tx_thread_create(&low_priority_thread, "Low",
                     low_priority_entry, 0,
                     low_stack, sizeof(low_stack),
                     15, 15, TX_NO_TIME_SLICE, TX_AUTO_START);
}

编译与运行

# 编译(假设ThreadX源码位于./threadx)
arm-none-eabi-gcc -c -I./threadx/common/inc -I./threadx/ports/cortex_m3/gnu/inc \
    main.c -o main.o
arm-none-eabi-gcc -c ./threadx/common/src/*.c ./threadx/ports/cortex_m3/gnu/src/*.c
arm-none-eabi-gcc -T ./threadx/ports/cortex_m3/gnu/example/build/linker.ld \
    *.o -o tx_demo.elf -nostdlib -lgcc

# 运行(QEMU模拟Cortex-M3)
qemu-system-arm -machine lm3s6965evb -kernel tx_demo.elf -nographic

验证方法

在QEMU运行环境中,通过以下方式验证抢占行为:

  1. 在high_priority_entry和low_priority_entry中分别设置断点,观察执行顺序:低优先级线程开始运行后,高优先级线程就绪时会立即打断低优先级线程。
  2. 检查preemption_count变量:由于高优先级线程每次唤醒后都会检查低优先级线程是否在运行,该计数器会持续增长,证明抢占反复发生。
  3. 观察current_thread_id的变化:该变量会频繁在1和2之间切换,且切换方向总是从2(低优先级)变为1(高优先级),说明高优先级线程主动抢占。

通过条件:程序正常运行不崩溃,preemption_count值持续递增,且current_thread_id从2变为1的次数与preemption_count一致。

关键说明

  • 高优先级线程使用tx_thread_sleep(10)主动让出CPU,使低优先级线程有机会运行;低优先级线程通过忙等待占用CPU,不主动让出。
  • 当高优先级线程从睡眠中唤醒时,ThreadX调度器会立即暂停低优先级线程,切换到高优先级线程——这就是优先级抢占的核心机制。
  • 两个线程共享current_thread_id变量,用于标记当前持有CPU的线程,从而在代码层面确认抢占行为确实发生。