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

前言

本文基于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

### 实现 RT-Thread 操作系统的概述 RT-Thread 是一个开源的实时操作系统 (RTOS),具有高度可扩展性和模块化设计。要从头开始理解和实现 RT-Thread,可以从以下几个方面入手: #### 1. 理解基本概念 为了更好地掌握 RT-Thread 的工作原理,建议先熟悉一些基础的概念和技术,比如多任务调度、中断管理、内存管理和同步机制等。 #### 2. 获取源码并研究其结构 通过命令 `git clone https://gitee.com/RT-Thread/rt-thread.git` 可以获取最新的 RT-Thread 源代码仓库[^4]。下载完成后,仔细阅读项目中的文档和注释有助于理解各个组件的功能及其相互关系。 #### 3. 配置编译环境 按照官方指南完成开发环境的设置是非常重要的一步。这通常涉及到安装必要的工具链(如 MDK)、版本控制系统(Git),以及其他辅助软件或库文件[^2]。确保所有的依赖项都已正确安装之后再继续下一步操作。 #### 4. 探索核心功能——时钟节拍与时序控制 任何操作系统都需要有一个稳定的时钟信号作为计时依据,在 RT-Thread 中这个角色由时钟节拍承担。它决定了系统内部各种基于时间的操作精度,例如线程切换的时间间隔就是依靠此参数设定而来。具体来说,可以通过修改宏定义 `RT_TICK_PER_SECOND` 来改变默认每秒产生的脉冲次数,默认情况下该值被设为 1000 Hz 即每隔 1 ms 发生一次中断请求[^3]。 ```c #define RT_TICK_PER_SECOND 1000 ``` #### 5. 学习高级特性与应用编程接口(APIs) 除了上述提到的基础部分外,还应该关注其他重要特性的学习,像线程间通信方式(消息队列、邮箱等)、设备驱动框架以及图形界面支持等等。这些都可以帮助开发者更灵活地利用 RT-Thread 构建复杂的应用程序。 #### 6. 动手实践 理论知识固然重要,但是真正掌握一门技术还需要大量的实战经验积累。可以尝试编写简单的测试案例来验证所学的知识点;也可以参与社区贡献代码或者解决他人遇到的问题从而加深对整个生态的理解程度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值