RT-thread-2022夏令营-学习总结-第二天

2023-05-16

RT-thread-2022夏令-第二天,今天的主要内容分为三大部分:

  • RT-Thread的启动流程
  • 线程的管理
  • 线程的创建

目录

一、RTT启动流程(基于HPM6750芯片)

1、系统上电后首先执行start.S启动文件

2、reset_handler(void)复位函数

3、entry()系统入口函数

4、rtthread_startup()系统启动函数

5、rt_application_init创建main()

6、$Super$$main() 

二、线程管理

1、线程调度

2、线程优先级

3、空闲线程idle

4、 时间片

 5、线程错误码

 6、线程控制块

7、线程状态

三、线程的创建

1、静态线程

2、动态线程创建

3、双线程运行实验


一、RTT启动流程(基于HPM6750芯片)

在芯片上电到进入用户服务函数main之间还存在RTT系统启动的操作

1、系统上电后首先执行start.S启动文件

该文件为汇编文件,主要实现对系统堆栈的一些初始化与驱动C中的复位函数

2、reset_handler(void)复位函数

该函数为系统启动中的第一个运行的C函数,主要功能有:使能PLIC中断、初始化系统虚实地址、调用平台特定的硬件初始化 、最后以entry()作为系统入口函数。

void reset_handler(void)
{
    /**
     * Disable preemptive interrupt
     */
    HPM_PLIC->FEATURE = 0;
    /*
     * Initialize LMA/VMA sections.
     * Relocation for any sections that need to be copied from LMA to VMA.
     */
    c_startup();

    /* Call platform specific hardware initialization */
    system_init();

    /* Do global constructors */
    __libc_init_array();

    /* Entry function */
    entry();
}

3、entry()系统入口函数

entry()入口函数只调用了rtthread_startup()启动代码

int entry(void)
{
    rtthread_startup();
    return 0;
}

4、rtthread_startup()系统启动函数

这部分代码主要实现了如下与系统启动相关的内容:

  • 初始化与系统相关的硬件;
  • 初始化系统内核对象,例如定时器、调度器、信号;
  • 创建 main 线程,在 main 线程中对各类模块依次进行初始化
  • 初始化定时器线程、空闲线程,并启动调度器。
int rtthread_startup(void)
{
    rt_hw_interrupt_disable();

    /* board level initialization
     * 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 /* RT_USING_SIGNALS */

    /* create init_thread */
    rt_application_init();

    /* timer thread initialization */
    rt_system_timer_thread_init();

    /* idle thread initialization */
    rt_thread_idle_init();

#ifdef RT_USING_SMP
    rt_hw_spin_lock(&_cpus_lock);
#endif /* RT_USING_SMP */

    /* start scheduler */
    rt_system_scheduler_start();

    /* never reach here */
    return 0;
}

5、rt_application_init创建main()

在 rtthread_startup() 函数中调用 rt_application_init() 函数,该函数会创建一个初始化线程该线程函数入口是 main_thread_entry(),在这个函数中会调用 $Super$$main(), 进入 main()。

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_USING_HEAP */

    rt_thread_startup(tid);
}

6、$Super$$main() 

该函数中主要就干了两件事:

  • 调用rt_components_init()对系统线程组件初始化;
  • 调用main()进入用户主函数。

至此,RT-thread系统算是启动完毕,接下来都是用户服务函数。

void main_thread_entry(void *parameter)
{
    extern int main(void);

#ifdef RT_USING_COMPONENTS_INIT
    /* RT-Thread components initialization */
    rt_components_init();
#endif /* RT_USING_COMPONENTS_INIT */

#ifdef RT_USING_SMP
    rt_hw_secondary_cpu_up();
#endif /* RT_USING_SMP */
    /* invoke system main function */
#ifdef __ARMCC_VERSION
    {
        extern int $Super$$main(void);
        $Super$$main(); /* for ARMCC. */
    }
#elif defined(__ICCARM__) || defined(__GNUC__) || defined(__TASKING__)
    main();
#endif
}

二、线程管理

线程是RTOS最重要的组成部分,把一个功能划分为多个任务,把每个任务做为一个线程,合理划分正确的执行后就能实现复杂问题简单化,并且也能满足实时系统的性能及时间的要求。

1、线程调度

线程调度器是线程最重要的内容,RT-thread的线程调度是抢占式的,根据优先级的高低来进行线程的调度,当就绪线程的优先级在线程列表中最高时,调度器就会让该线程进入CPU处理。

2、线程优先级

RT-thread 线程的优先级是表示线程被调度的优先程度。每个线程都具有优先级,线程越重要,赋予的优先级就应越高,线程被调度的可能才会越大。

RT-thread最大支持256个优先级(0-255),数值越小优先级越高,0最低一般给空闲线程用,用户不用。

3、空闲线程idle

如果没有其它线程可以运行, 系统就会为 CPU 创建一个空闲线程,这个时候 CPU 就运行空闲线程。 在 RTThread 中,空闲线程是系统在初始化的时候创建的优先级最低的线程,空闲线程主体主要是做一些系统内存的清理工作。

4、 时间片

RT-Thread 允许多个线程具有相同的优先级,相同优先级的线程之间采用时间片轮转的方式进行调度。创建线程的时候,可以配置线程的时间片参数。时间片仅对优先级相同的就绪线程有效。

注:时间片的作用是约束线程单次运行的时长,其单位是系统时钟节拍(OS Tick)。

 5、线程错误码

一个线程就是一个执行场景,错误码是与执行环境密切相关的,所以每个线程配备了一个变量用于保存错误码,线程的错误码有以下几种:

#define RT_EOK           0 /* 无错误     */
#define RT_ERROR         1 /* 普通错误     */
#define RT_ETIMEOUT      2 /* 超时错误     */
#define RT_EFULL         3 /* 资源已满     */
#define RT_EEMPTY        4 /* 无资源     */
#define RT_ENOMEM        5 /* 无内存     */
#define RT_ENOSYS        6 /* 系统不支持     */
#define RT_EBUSY         7 /* 系统忙     */
#define RT_EIO           8 /* IO 错误       */
#define RT_EINTR         9 /* 中断系统调用   */
#define RT_EINVAL       10 /* 非法参数      */

 6、线程控制块

线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态、程序与程序间连接用的链表结构等,在 RT-Thread 中,线程控制块由结构体 struct rt_thread 表示。

7、线程状态

RT-Thread 提供一系列的操作系统调用接口,使得线程的状态在这五个状态之间来回切换。几种状态间的转换关系如下图所示:

  • 线程通过调用函数 rt_thread_create/init() 进入到初始状态(RT_THREAD_INIT);
  • 初始状态的线程通过调用函数 rt_thread_startup() 进入到就绪状态(RT_THREAD_READY);
  • 就绪状态的线程被调度器调度后进入运行状态(RT_THREAD_RUNNING);
  • 当处于运行状态的线程调用 rt_thread_delay(),rt_sem_take(),rt_mutex_take(),rt_mb_recv() 等函数或者获取不到资源时,将进入到挂起状态(RT_THREAD_SUSPEND);
  • 处于挂起状态的线程,如果等待超时依然未能获得资源或由于其他线程释放了资源,那么它将返回到就绪状态。挂起状态的线程,如果调用 rt_thread_delete/detach() 函数,将更改为关闭状态(RT_THREAD_CLOSE);
  • 而运行状态的线程,如果运行结束,就会在线程的最后部分执行 rt_thread_exit() 函数,将状态更改为关闭状态。

三、线程的创建

1、静态线程

 所谓静态创建,就是在SRAM中提前给线程划分一部分内存空间,与线程有关的所有内存都在那一块内存空间中。创建流程如下:

  1. 定义线程栈
  2. 定义线程控制块
  3. 定义线程函数
  4. 初始化线程
  5. 启动线程
ALIGN(8)//栈空间地址对齐
static rt_uint8_t static_stack[2048];//定义栈空间大小2048*8
static struct rt_thread static_thread;//定义线程控制块
void thread1(void)//线程函数1
{
    while(1)
    {
       rt_thread_mdelay(100);
       rt_kprintf("1");
    }
}
void thread_test(void)
{
    rt_thread_init(&static_thread,    //线程控制块
                  "thread1",        //线程名称
                  thread1,          //线程入口函数
                  RT_NULL,          //线程入口函数参数
                  &static_stack[0], //线程栈起始地址
                  2048,             //栈大小
                  16,               //线程优先级
                  100);             //时间片
  rt_thread_startup(&static_thread);//启动线程
}

2、动态线程创建

动态线程创建时,系统自动从动态内存堆上分配栈空间与线程句柄,不用用户自己定义。但是我们要清楚线程栈在线程创建的时候创建,也就是线程栈是根据创建线程的大小来给定的,线程创建函数会返回一个指针,指向该线程的线程控制块,所以要预先定义一个线程控制块指针。

  1. 定义线程控制块指针
  2. 定义线程函数
  3. 初始化线程并将返回值赋给线程控制块指针
  4. 启动线程
rt_thread_t dynamic_thread = RT_NULL;//定义线程控制块指针

void thread2(void)//线程函数
{
    while(1)
    {
       rt_thread_mdelay(200);
       rt_kprintf("2");
    }
}
void thread_test(void)
{
  /* 利用rt_thread_create() 函数动态创建线程,会返回线程控制块指针 */ 
  dynamic_thread = rt_thread_create(  "thread2", //线程名称
                                      thread2,   //线程入口函数
                                      RT_NULL,   //线程入口函数参数
                                      2048,      //栈大小
                                      16,        //线程优先级
                                      100);      //时间片
  /* 判断动态线程是否创建成功*/ 
  if( dynamic_thread != RT_NULL )
      rt_thread_startup( dynamic_thread );//返回为线程控制块指针,所以不要取地址,直接用
  else
      return -1;
}

3、双线程运行实验

创建一动态线程一个静态线程,并且设置优先级不一样,再通过msh命令控制打印输出1和2。

首先了解一下RTT提供的MSH_CMD_EXPORT(command, desc),自定义msh命令函数,有带参和不带参数两种使用方法


MSH_CMD_EXPORT(hello , say hello to RT-Thread);//(要导出的命令,命令的描述)

/*导出有参数的命令时,函数的入参为 int argc 和 char**argv。
argc 表示参数的个数,argv 表示命令行参数字符串指针数组指针。导出有参数命令示例如下:*/
static void atcmd(int argc, char**argv)
{
    ……
}
MSH_CMD_EXPORT(atcmd, atcmd sample: atcmd <server|client>);

双线程实验代码:

ALIGN(8)//对齐
static struct rt_thread static_thread;//线程句柄指针
rt_uint8_t static_stack[2048];//空间
rt_thread_t dynamic_thread = RT_NULL;
void thread1(void)//线程1
{
    while(1)
    {
       rt_thread_mdelay(100);
       rt_kprintf("1\r\n");
    }
}
void thread2(void)//线程2
{
    while(1)
    {
       rt_thread_mdelay(200);
       rt_kprintf("2\r\n");
    }
}
void thread_test(void)
{
    /*静态线程,优先级16*/
  rt_thread_init(&static_thread,"thread1",thread1,RT_NULL,&static_stack[0],2048,16,100);
  rt_thread_startup(&static_thread);
    /*动态线程,优先级15*/
  dynamic_thread = rt_thread_create("thread2",thread2,RT_NULL,2048,15,500);
  rt_thread_startup(dynamic_thread);
}
MSH_CMD_EXPORT(thread_test,static thread / dynamic thread test sample);

打开终端查看结果,输出函数名调用函数开启线程,效果如下:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RT-thread-2022夏令营-学习总结-第二天 的相关文章

随机推荐

  • 干了十多年SAP顾问,聊聊从SAP最佳业务实践看企业管理

    干了十多年SAP顾问 xff0c 聊聊从SAP最佳业务实践看企业管理 干了十多年的SAP实施顾问 xff0c 经历了各种行业 xff08 机械 电力 石油 化工 制造 消费品等等 xff09 各种企业 xff08 国企 外企 民营 xff0
  • ESKF IMU+GPS融合定位 MATLAB、c++实现

    1 在展示公式和代码之前 xff0c 先理清一下旋转矩阵 xff0c 四元素 xff0c 轴角 xff0c 旋转向量之间的关系 假设一个旋转向量为 xff1a 将旋转向量表示成轴角形式 所以 轴角转换到旋转矩阵形式 轴角转换成四元数形式 反
  • VNC图形终端和分辨率设置

    在服务端通过vncserver 命令启动vnc 后 xff0c 控制台输出 xff1a New 39 favey 1 root 39 desktop is favey 1 Starting applications specified in
  • 什么是耦合,解耦?

    打个比方 xff1b 两兄弟的工资都共同存在一个银行卡里面 xff0c 假如有一天 xff0c 弟弟需要用钱 xff0c 那么需要找哥哥商量一下 xff0c 然后再取钱 xff0c 这就是耦合 xff1b 后来某天弟弟发现他自己的工资可以单
  • UnknownError: Failed to get convolution algorithm. This is probably because cuDNN failed to initial

    训练一个卷积神经网络时 xff0c 报这个错误 错误显示代码 xff1a 无法获取卷积算法 这可能是因为cuDNN初始化失败 xff0c 所以请尝试查看上面是否打印了警告日志消息 UnknownError Failed to get con
  • ARM指令集--ADD SUB LDR STM LDM STM LDIA STMDB

    ADD add r0 r1 4 r0 61 r1 43 4 SUB sub r0 r1 4 r0 61 r1 4 sub r0 r1 r2 r0 61 r1 r2 LDR LDR 寄存器 内存 LDR R0 61 0x50000050 r0
  • OpenCV图像基础操作一

    Mat类分为了两个部分 1 矩阵头和指向矩阵数据部分的指针 2 data就是指向矩阵数据的指针 下面是VS调试显示属性 xff1a flags xff1a flags是int类型 xff0c 共占32位 xff0c 从低位到高位 xff1a
  • Git超详解七 储藏 (看不懂算我输)

    储藏 1 储藏2 添加储藏3 恢复储藏4 删除储藏5 查看所有储藏6 储藏未跟踪的文件 1 储藏 有时候我们代码写到一半 xff0c 需要切换到另外一个分支上去工作 但是我本地这个工作还没做完 xff0c 如果我现在做一个commit那么会
  • 从零开始安装ubuntu18+P4+ONOS

    1 安装VMware Tools xff08 前面VM虚拟机安装Ubuntu的教程太多 xff0c 就不写了 xff09 先从虚拟机下载好 xff0c 然后将压缩包里的文件夹移动出来 xff08 可以用解压或者直接点开压缩包 xff0c 移
  • SLAM库学习: 从因子图到GTSAM

    SLAM库学习 xff1a 从因子图到GTSAM 一 从贝叶斯网络到因子图优化1 贝叶斯网络2 因子图3 非线性最小二乘问题4 线性最小二乘问题 1 QR分解 2 Cholesky 分解 二 iSAM2和贝叶斯树三 GTSAM实战1 Pos
  • 激光SLAM后端优化总结之图优化

    激光SLAM后端优化总结之图优化 1 图的稀疏性与边缘化2 位姿图3 因子图4 滤波与图优化 优化问题 xff0c 可以用图的方式表示 xff0c 图的节点是需要优化的变量 xff0c 边是优化变量之间的约束 节点可以是位姿 xff0c 也
  • GVINS源码解析

    GVINS是基于VINS MONO写的 xff0c 视觉 IMU部分与VINS MONO类似 xff0c 可参考我的前一篇文章VINS MONO学习 这篇文章主要解析与GNSS有关的部分 持续更新中 文章目录 estimator node
  • centos go

    1 安装 mercurial包 root 64 localhost yum install mercurial 2 安装git包 root 64 localhost yum install git 3 安装gcc root 64 local
  • SD-WAN技术实现方案(细节)-企业侧

    1组网模型 1 1组网场景 underlay 北京HUB xff1a CPE双线接入MPLS网络和宽带网络 xff0c 宽带网络具有全球IP 杭州HUB xff1a CPE双线接入MPLS网络和移动网络 xff0c 宽带网络具有全球IP S
  • win10应用商店无法下载报错,代码为0x80073D0A

    第一步按下组合键Windows 43 X组合键 xff0c 选择 命令提示符 xff08 管理员 xff09 xff0c 执行sfc scannow xff0c 等待修复完成 第二步将防火墙开启 第三步再去应用商店重新下载安装 产生问题原因
  • KVM虚拟机导出和导入

    KVM安装 Ubuntu20 安装KVM span class token function sudo span apt span class token function install span qemu kvm libvirt dae
  • Python中列表的基本操作

    目录 1 定义列表2 访问列表3 增删改操作4 合并列表5 列表切片6 遍历列表7 列表转换 列表通常用来存储多个数据 xff0c 每一个数据之间用逗号隔开 xff0c 列表中的数据被称为元素 xff0c 列表的左右两边带有中括号 1 定义
  • Python中的函数与变量

    一 函数 python中函数的基本格式则为 def 43 函数名 43 参数名 43 函数体 43 返回 xff0c python作为一门面向对象的语言 xff0c 同样可分为类函数 实例函数 span class token commen
  • 如何添加Typora主题皮肤(图解)

    如何添加Typora主题皮肤 首先 xff0c 先打开Typora官网下载主题皮肤 xff01 地址 xff1a https theme typora io 这里比如我下载设置一个Vue主题 下载后解压 打开偏好设置 点击打开主题文件夹后
  • RT-thread-2022夏令营-学习总结-第二天

    RT thread 2022夏令 第二天 xff0c 今天的主要内容分为三大部分 xff1a RT Thread的启动流程线程的管理线程的创建 目录 一 RTT启动流程 xff08 基于HPM6750芯片 xff09 1 系统上电后首先执行