RT-Thread源码阅读(1)——基本框架

发布于:2025-05-23 ⋅ 阅读:(21) ⋅ 点赞:(0)

前言

本文基于RT-Thread V4.1.1和STM32F103(Cortex-M3)

编译环境为STM32CubeIDE(GCC)

本文旨在理解RT-Thread设计的基本逻辑,为了让文章简短易懂,所以展出的源码都是精简过的,一些好理解的开关中断,宏代码等会省略掉

可以看懂基本逻辑后查看源码领悟具体细节

关于RT-Thread的移植可以参考



双向链表

双向环形链表的定义,基本所有的核心操作都离不开这个双向链表

struct rt_list_node
{
    struct rt_list_node *next;                          /**< point to next node. */
    struct rt_list_node *prev;                          /**< point to prev node. */
};
typedef struct rt_list_node rt_list_t;                  /**< Type for lists. */

将节点 n 插入到 l 后面,分4步完成

rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
    l->next->prev = n;
    n->next = l->next;

    l->next = n;
    n->prev = l;
}

将节点 n 插入到 l 前面,分4步完成

rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
    l->prev->next = n;
    n->prev = l->prev;

    l->prev = n;
    n->next = l;
}
// 链表初始化 即自己指向自己
rt_inline void rt_list_init(rt_list_t *l)
{
    l->next = l->prev = l;
}

// 判断链表是否为空
rt_inline int rt_list_isempty(const rt_list_t *l)
{
    return l->next == l;
}
    
// 获取链表长度
rt_inline unsigned int rt_list_len(const rt_list_t *l)
{
    unsigned int len = 0;
    const rt_list_t *p = l;
    while (p->next != l)
    {
        p = p->next;
        len ++;
    }

    return len;
}



通过元素找到内核对象地址

在RT-Thread中所有对象(线程,信号量等)都会有list元素,如下操作是通过list地址反推对象地址,如下是以rt_thread线程对象为例:

#define rt_list_entry(node, type, member) rt_container_of(node, type, member)
#define rt_container_of(ptr, type, member) ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

找到当前链表所在结构体的首地址,巧妙的利用&((type *)0)->member算了链表的偏移量,使用示例如下:

struct rt_thread *thread;

thread = rt_list_entry(list->next, struct rt_thread, tlist);

struct rt_thread
{
    char        name[RT_NAME_MAX];                      /**< the name of thread */
    rt_uint8_t  type;                                   /**< type of object */
    rt_uint8_t  flags;                                  /**< thread's flags */

    rt_list_t   list;                                   /**< the object list */
    rt_list_t   tlist;                                  /**< the thread list */

    void       *sp;                                     /**< stack point */
    void       *entry;                                  /**< entry */
    void       *parameter;                              /**< parameter */
    void       *stack_addr;                             /**< stack address */
    rt_uint32_t stack_size;                             /**< stack size */
    
    ......
}



启动RTOS

在没有OS的工程中,是从main()中开始运行的

RT-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一启动入口

一般执行顺序是:系统先从启动文件开始运行,然后进入 RT-Thread 的启动函数rtthread_startup(),最后进入用户入口函数 main()

使用GCC编译时需要修改启动文件

使用MDK时可以不用修改,可以使用$Sub$$main,如果可以参考博文

int rtthread_startup(void)
{
    rt_hw_interrupt_disable();

    /* 板级初始化:需在该函数内部进行系统堆的初始化 */
    rt_hw_board_init();

    /* 打印 RT-Thread 版本信息 */
    rt_show_version();

    /* 硬件定时器初始化 */
    rt_system_timer_init();

    /* 调度器初始化 */
    rt_system_scheduler_init();

    /* 由此创建一个用户 main 线程 */
    rt_application_init();

    /* 软件定时器线程初始化 */
    rt_system_timer_thread_init();

    /* 空闲线程初始化 */
    rt_thread_idle_init();

    /* 启动调度器 */
    rt_system_scheduler_start();

    /* 不会执行至此 */
    return 0;
}



调度器初始化

与调度相关的有两个非常重要的变量,在rt_system_scheduler_init()中就是初始化这两个变量

rt_thread_priority_table 是一个ready链表数组,同一优先级的线程放同一链表中

rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];

rt_thread_ready_priority_group 是一个32位整型数,每1位都代表着对应优先级是否有ready的线程,0 优先级代表最高优先级

rt_uint32_t rt_thread_ready_priority_group;

与其相关的操作节选如下:

// 线程启动(UP)或改变优先级的时候赋值
thread->number_mask = 1 << thread->current_priority;	

// rt_schedule_insert_thread 中调用
rt_thread_ready_priority_group |= thread->number_mask;

// rt_schedule_remove_thread 中调用  
rt_thread_ready_priority_group &= ~thread->number_mask;

顺带介绍一下与优先级相关的函数

_scheduler_get_highest_priority_thread()

获取已经ready的最高优先级线程指针

其中__rt_ffs()函数用来计算整数中从低位开始的第一个非零位的位置,和内建函数__builtin_ffs()功能一致

static struct rt_thread* _get_highest_priority_thread(rt_ubase_t *highest_prio)
{
    register struct rt_thread *highest_priority_thread;
    register rt_ubase_t highest_ready_priority;

    highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;

    /* get highest ready priority thread */
    highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
                              struct rt_thread,
                              tlist);

    *highest_prio = highest_ready_priority;

    return highest_priority_thread;
}

rt_schedule_insert_thread()

将线程插入调度列表

void rt_schedule_insert_thread(struct rt_thread *thread)
{
    /* READY thread, insert to ready queue */
    thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK);
    
    /* insert thread to ready list */
    rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
                          &(thread->tlist));

    rt_thread_ready_priority_group |= thread->number_mask;
}

rt_schedule_remove_thread()

将线程从调度列表中移除

void rt_schedule_remove_thread(struct rt_thread *thread)
{
    /* remove thread from ready list */
    rt_list_remove(&(thread->tlist));
    
    if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority])))
    {
 		// 需要通过rt_list_isempty() 判断同优先级是否有其他已ready线程 没有才清除对应位
        rt_thread_ready_priority_group &= ~thread->number_mask;
    }
}



创建用户 main 线程

调用创建线程函数,这里以静态创建为例

void rt_application_init(void)
{
    rt_thread_t tid;

    tid = &main_thread;
    result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
                            main_stack, sizeof(main_stack), RT_MAIN_THREAD_PRIORITY, 20);
    RT_ASSERT(result == RT_EOK);

    rt_thread_startup(tid);
}

rt_err_t rt_thread_init(struct rt_thread *thread, const char *name, void (*entry)(void *parameter), void *parameter,
                        void *stack_start, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick)
{
    /* initialize thread object */
    rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);

    return _thread_init(thread, name, entry, parameter, stack_start, stack_size, priority, tick);
}

这里先通过rt_object_init函数给线程类型句柄rt_thread_t tid初始化rt_object部分

需要说明是,RT-Thrad中所有对象(线程,信号量等)的结构体开头都包括rt_object

rt_object中有对象类型,名称等信息



开关中断简述

开关中断函数总是成对的出现,rt_hw_interrupt_disable在关中断的同时,会返回关之前的中断状态

所以rt_hw_interrupt_enable并不一定是真的开中断了,只有最外层的rt_hw_interrupt_enable才会真的开中断

rt_base_t rt_hw_interrupt_disable(void);
void rt_hw_interrupt_enable(rt_base_t level);
    .global rt_hw_interrupt_disable
    .type rt_hw_interrupt_disable, %function
rt_hw_interrupt_disable:
    MRS     R0, PRIMASK
    CPSID   I
    BX      LR

    .global rt_hw_interrupt_enable
    .type rt_hw_interrupt_enable, %function
rt_hw_interrupt_enable:
    MSR     PRIMASK, R0
    BX      LR


网站公告

今日签到

点亮在社区的每一天
去签到