【学习笔记】RT-Thread内核视频学习笔记[1-16章]

写在前面的话:

这个文章很长,主要是对RT-Threadd的教学视频 (视频链接,)的总结和回顾。是一个比较个人化的东西,其实比较专业的内核指导在RTT官网上也有总结(内核链接)。只是视频中有一些比较基础的官网上没提到的地方做下总结。

文章目录

第一章 初识RT-Thread

1.1嵌入式系统

  • 嵌入式系统是一种完全嵌入在装置或设备内部、为满足特定需求而设计的计算机系统
  • 嵌入式操作系统是应用于嵌入式系统的软件,用来对接嵌入式底层硬件和上层应用
    • 多任务管理
    • 任务间通信
    • 内存管理
    • 定时器管理
    • 设备管理

1.2RT-Thread

  • 实时操作系统
  • 开源、免费;许可证类似FreeRTOS
  • 国产嵌入式操作系统,不仅是一个RTOS,还包含网络、文件系统、GUI界面等组件的中间件平台,具有极强的扩展性
  • 支持市面上所有的主流编译工具,如IAR、GCC、Keil
  • 完成了超过50款MCU上和所有主流CPU架构上的移植工作

1.3目录结构

  • RT-Thread内核源码目录
|----+ RT-Thread:
|------src:RT-Thread内核代码文件
|------libcpu:各类芯片/内核移植代码
|------include:RT-Thread内核头文件
|------components:RT-Thread外部组件代码
  • 工程源码目录
|-------+ project
|----------application:用户应用代码
|----------drivers:RT-Thread的驱动,不同平台底层驱动的具体实现
|----------kernel-sample:内核例程
|----------Libraries:STM32芯片固件库
|----------rt-thread:RT-Thread源代码
  • 工程目录
|-------+ project
|----------Application:用户应用代码
|----------Drivers:RT-Thread的底层驱动代码
|----------STM32_HAL:存放STM32固件库文件【Libraries】
|----------Kernel:存放RT-Thread内核核心代码【src】
|----------CORTEX-M3:存放CORTEX-M3移植代码【libcpu】
|----------DeviceDrives:驱动框架源码【components->d】
|----------finsh:RT-Thread命令行和finsh组件【components->f】
|----------kernel-sample:RT-Thread内核例程

1.4启动分析

int main(void)
{
    return 0;
}

无法从main函数中看到RT-Thread的启动过程

  • 启动流程
  • kernel -->components.c -->$sub$$main
int $Sub$$main(void)
{
    rt_hw_interrupt_disable();//关中断
    rtthread_startup();//RTT启动函数
    return 0;
}

关于$Sub$$main和main之间的说法,我之前有解释过,可以看链接

  • RTT启动入口
int rtthread_startup(void)
{
    rt_hw_interrupt_disable();//关中断

    /* board level initalization
     * NOTE: please initialize heap inside board initialization.
     */
    rt_hw_board_init();//硬件平台初始化

    /* show RT-Thread version */
    rt_show_version();//显示版本号码

    /* timer system initialization */
    rt_system_timer_init();//系统时钟初始化

    /* scheduler system initialization */
    rt_system_scheduler_init();//系统调度器的初始化

#ifdef RT_USING_SIGNALS
    /* signal system initialization */
    rt_system_signal_init();//系统信号机制的初始化
#endif

    /* create init_thread */
    rt_application_init();//很重要,因为用户的任务在这里面创建的

    /* timer thread initialization */
    rt_system_timer_thread_init();

    /* idle thread initialization */
    rt_thread_idle_init();

    /* start scheduler */
    rt_system_scheduler_start();//使rtt跑起来

    /* never reach here */
    return 0;
}
  • main就是在app中创建的
void rt_application_init(void)
{
    rt_thread_t tid;

#ifdef RT_USING_HEAP
    tid = rt_thread_create("main", main_thread_entry, RT_NULL,
                           RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
    RT_ASSERT(tid != RT_NULL);
#else
    rt_err_t result;

    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);
	
    /* if not define RT_USING_HEAP, using to eliminate the warning */
    (void)result;
#endif

    rt_thread_startup(tid);
}

第二章 动态内存堆的使用

2.1 回顾堆栈的概念

  • 栈(stack):由编译器自动分配释放
  • 堆(heap) :一般由程序员分配和释放
int a = 0char *p1
int main()
{
   
    int b;
    char s[] = "abc";
    char *p2;
    char *p3 = "123456";
    static int c = 0;
    p1 = (char *)malloc(10);
    p2 = (char *)malloc(20);
}
  • 定义的局部变量都来源于栈空间栈是由编译器自动分配和释放的,当函数结束,栈空间就释放掉
  • malloc函数申请的都来源于堆空间

2.2 裸机系统动态内存分配

裸机系统动态内存分配

2.3 RTT中动态内存

在board.c中,需要一个函数事先帮我们配置好动态内存的api
在board.c中,也就是进行相关内存初始化的时候,会去配置动态内存。会根据实际芯片,或者是板卡上的内存去配置。

rt_system_heap_init((void *)HEAP_BEGIN,(void *)HEAP_END)//从系统中取到一块内存设置为动态内存区,然后可以通过RTT特定的rt_malloc和rt_free,申请和释放内存
//一般在board.c中调用,在进行板级初始化的时候,配置动态内存,根据芯片或板卡的实际情况配置

函数需要提供一个起始地址HEAP_BEGIN结束地址HEAP_END,中间的这段空间会被系统当作动态内存空间使用。
本节示例使用的芯片,具有64kram空间。
在这里插入图片描述

  • 从系统中取到一块内存设置为动态内存区,然后可以通过RTT特定的rt_mallocrt_free申请和释放内存
  • 一般在board.c中调用,在进行板级初始化的时候,配置动态内存,根据芯片或板卡的实际情况配置
    函数原型在men.c
    men.c
起始地址
#define HEAP_BEGIN  ((void *)&Image$$RW_IRAM$$Limit)
HEAP_BEGIN = (void *)&Image$$RW_IRAM$$Limit
  • Image$$RW_IRAM$$Limit连接器导出的符号,代表ZI段的结束,也就是程序执行去的RAM结束后的地址,反过来就是我们执行去的RAM未使用的区域的起始地址
  • 也就是,定义全局变量,或者静态全局变量,所剩下的空间分配给动态内存堆中
    通过查看工程中的map文件
    ,可以看到整个工程所用到的静态ram空间用到了22.05k,也就是说,除了RW和ZI外,整个加起来用到了22.05k。那么整个芯片64k的空间,除去22.05k,剩下的这段空间,肯定是有一个起始地址的。这个地址,就是通过宏定义Image$$RW_IRAM$$Limit表示的。
    map
结束地址

我们一般以片内ram的结束地址,作为结束地址。

Total RW+heap size = MCU total RAM size

2.4 RTT动态内存的使用

  • 首先创建一个任务
int dynmem_sample(void)
{
   
    rt_thread_t tid;

    /* 创建线程1 */
    tid = rt_thread_create("thread1",
                           thread1_entry, RT_NULL,
                           THREAD_STACK_SIZE,
                           THREAD_PRIORITY,
                           THREAD_TIMESLICE);
    if (tid != RT_NULL)
        rt_thread_startup(tid);

    return 0;
}
  • thread1_entry:任务的入口函数
/* 线程入口 */
void thread1_entry(void *parameter)
{
    int i;
    char *ptr = RT_NULL; /* 内存块的指针 */

    for (i = 0; ; i++)
    {
        /* 每次分配 (1 << i) 大小字节数的内存空间 */
        ptr = rt_malloc(1 << i);

        /* 如果分配成功 */
        if (ptr != RT_NULL)
        {
            rt_kprintf("get memory :%d byte\n", (1 << i));
            /* 释放内存块 */
            rt_free(ptr);
            rt_kprintf("free memory :%d byte\n", (1 << i));
            ptr = RT_NULL;
        }
        else
        {
            rt_kprintf("try to get %d byte memory failed!\n", (1 << i));
            return;
        }
    }
}

使用rt_malloc申请内存,如果申请内存失败,会返回RT_NULL[常量],申请的空间要少于系统空闲的空间才能成功
通过命令行的方式,导出特定的符号
在这里插入图片描述

2.5 动态内存注意事项

  • 内存复位
    当我们每次申请到新的内存块之后,建议对申请到的内存块进行清零操作
    因为整个动态内存空间是公用的,申请到的空间内可能有原来的应用留下来的数据,初始值可能不为零。
p = rt_malloc(10);
if(p != RT_NULL)
{
   
rt_memset(p,0,10);
}
  • 内存泄漏
    是指程序中已动态分配的堆内存由于某种原因程序未释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
    rt_malloc和rt_free配套使用

2.6 其他相关API

void *rt_realloc(void *tmen,rt_size_t newsize)
  • 在已分配内存块的基础上重新分配内存块的大小(增加或者缩小)
  • 在进行重新分配内存块时,原来的内存块数据保持不变(缩小的情况下,后面的数据被自动截断)

如果前面分配了50个字节的内存空间,后面发现不够,想加到100,或者原本分配了50,太大了,需要20就够了

void *rt_calloc(rt_size_t count, rt_size_t size)
  • 从内存堆中分配连续内存地址的多个内存块
  • malloc的形参只有size 的大小
    calloc的形参有count 和size,分配到的空间为count * size ,而且空间连续

第三章 线程的概念

3.1线程的概念

  • 在设计一个较为复杂的应用程序时,也通常把一个大型任务分解成多个小任务,然后通过运行这些小任务,最终达到完成大人物的目的
  • 在rtt中,与上述小问题对应的程序实体就叫做“线程”(任务),rtt就是一个能对这些小“线程”进行管理和调度多“线程”操作系统
  • 线程是实现任务的载体,是rtt中最基本的调度单位,她描述了一个任务执行的运行环境,也描述了这个任务的优先等级。

3.2线程组成

RTT中,线程有三部分组成:线程代码(入口函数)、线程控制块线程堆栈

  • 无限循环结构
void thread_entry(void *parameter)
{
   
    whlie(1)
    {
   
        /* 等待事件的发生 */
        
        /* 处理事件 */
    }
}

while中往往要加入让出CPU使用权的API函数,如果不加,其他线程得不到执行。

  • 顺序执行结构
void thread_entry(void *parameter)
{
   
    /* 事务1处理 */
    /* 事务2处理 */
}
3.2.1线程控制块

线程控制块是操作系统用于管理线程的一个数据结构,他会存放线程的一些信息,例如,优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构、线程等待时间集合等。

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

#ifdef RT_USING_MODULE
    void       *module_id;                              /**< id of application module */
#endif

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

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

    /* error code */
    rt_err_t    error;                                  /**< error code */

    rt_uint8_t  stat;                                   /**< thread status */

    /* priority */
    rt_uint8_t  current_priority;                       /**< current priority */
    rt_uint8_t  init_priority;                          /**< initialized priority */
#if RT_THREAD_PRIORITY_MAX > 32
    rt_uint8_t  number;
    rt_uint8_t  high_mask;
#endif
    rt_uint32_t number_mask;

#if defined(RT_USING_EVENT)
    /* thread event */
    rt_uint32_t event_set;
    rt_uint8_t  event_info;
#endif

#if defined(RT_USING_SIGNALS)
    rt_sigset_t     sig_pending;                        /**< the pending signals */
    rt_sigset_t     sig_mask;                           /**< the mask bits of signal */

    void            *sig_ret;                           /**< the return stack pointer from signal */
    rt_sighandler_t *sig_vectors;                       /**< vectors of signal handler */
    void            *si_list;                           /**< the signal infor list */
#endif

    rt_ubase_t  init_tick;                              /**< thread's initialized tick */
    rt_ubase_t  remaining_tick;                         /**< remaining tick */

    struct rt_timer thread_timer;                       /**< built-in thread timer */

    void (*cleanup)(struct rt_thread *tid);             /**< cleanup function when thread exit */

    /* light weight process if present */
#ifdef RT_USING_LWP
    void        *lwp;
#endif

    rt_uint32_t user_data;                             /**< private user data beyond this thread */
};
typedef struct rt_thread *rt_thread_t;
3.2.2线程栈
  • RTT每个线程都具有独立的栈空间,当线程切换时,系统会将当前线程的上下文保存在线程栈中,当线程要恢复运行时,再从线程栈中读取上下文信息,恢复线程的运行。
  • 线程上下文是指线程执行时的环境,具体来说就是【各个变量和数据】,包括所有寄存器变量、堆栈信息、内存信息等。
  • 线程栈在形式上是一段连续的内存空间,我们可以通过定义一个数组或者申请一段动态内存来作为线程的栈。

3.3线程创建

  • 创建线程
  • 创建静态线程
rt_thread_t rt_thread_create(const char* name, 
                                    void (*entry)(void* parameter), 
                                    void* parameter, 
                                    rt_uint32_t stack_size, 
                                    rt_uint8_t priority, 
                                    rt_uint32_t tick);
  • 创建动态线程
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);
  • 静态线程需要实现定义好线程控制块与栈空间
  • 动态线程的堆栈 是动态分配的
  • 启动线程
rt_err_t rt_thread_startup(rt_thread_t thread);

调用此函数后创建的线程会被加入到线程的就绪队列,执行调度。

3.4创建线程事例

  • 动态线程
#include <rtthread.h>

#define THREAD_PRIORITY         25
#define THREAD_STACK_SIZE       512
#define THREAD_TIMESLICE        5

static rt_thread_t tid1 = RT_NULL;

/* 线程1的入口函数 */
static void thread1_entry(void *parameter)

/* 创建线程1,名称是thread1,入口是thread1_entry*/
    tid1 = rt_thread_create("thread1",
                            thread1_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
    
    /* 如果获得线程控制块,启动这个线程 */
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);
  • 创建动态线程
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程2入口 */
static void thread2_entry(void *param)
{
   
}

/* 初始化线程2,名称是thread2,入口是thread2_entry */
    rt_thread_init(&thread2,
                   "thread2",
                   thread2_ent
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值