IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> RT_Thread自问自答 -> 正文阅读

[数据结构与算法]RT_Thread自问自答

在看韦东山老师“RT-Thread内部机制”的视频,挺有意思的,有些概念的理解还是要深究一下,不然可能就是“我知道,这不就是那个....那个....那个”,哪个嘛,并不是因为嘴笨说不好,是因为确实没想过真说不清楚,下面是一些自问自答。

1、线程是什么?

我的理解,线程就是指挥函数的运行。【一个线程对应一个入口函数】

  • 控制ta在何时运行,可以运行多久;
  • 暂停运行去切换别的线程时,保存当前的环境,以便下次继续运行;

2、线程切换时要保存的环境是什么?

首先函数是存在FLASH中的,不需要保存;

其次就是函数用到的变量,包括全局变量和局部变量,全部变量是大家都能用的不需要线程保存,局部变量是存放在各自的线程栈中的;

如果不知道线程临走时要带什么,可以想想当继续运行时要干什么?

要知道从哪一条(汇编)语句开始执行,执行中又会用到哪些CPU中寄存器的值,这就是我们要保存的。

P.S. CPU中寄存器,包括参数寄存器,状态寄存器,所谓“执行到哪”的程序计数器PC等。

3、创建线程时做了什么?

用rt_thread_init或rt_thread_create创建线程,我们传了一些参数:入口函数、函数参数、线程栈、优先级、时间片,然后线程就创建好了。

emm...鸭子和啤酒放进锅里,是不会变成啤酒鸭的。

以rt_thread_create为例:

  • 申请线程控制块的内存;申请线程栈的内存
  • 初始化线程链表
  • 初始化线程控制块的各种参数:函数入口、函数参数、栈的起始地址、栈大小、线程掩码、初始时间片、剩余时间片、线程错误代码、线程状态、线程清除函数、用户数据;
  • 初始化线程栈
  • 初始化线程定时器
  • 初始化线程钩子函数

3.1、初始化线程栈

先将申请到的线程栈的内存,全部初始化为'#';

rt_memset(thread->stack_addr, '#', thread->stack_size);

然后调用rt_hw_stack_init函数初始化栈;

    thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
                                          (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),
                                          (void *)rt_thread_exit);

第三个参数stack_addr+stack_size-8,stack_addr是申请内存时分配出的地址,指示栈底的低地址,加stack_size就到了栈顶,然后减8。进入函数里又加4,以8位倍数向下对齐,反正一通操作就是找到对齐后栈顶的地址。

函数里有一个结构体stack_frame,里面是保存CPU寄存器的值的,定义如下:?

struct exception_stack_frame
{
    rt_uint32_t r0;
    rt_uint32_t r1;
    rt_uint32_t r2;
    rt_uint32_t r3;
    rt_uint32_t r12;
    rt_uint32_t lr;
    rt_uint32_t pc;
    rt_uint32_t psr;
};

struct stack_frame
{
#if USE_FPU
    rt_uint32_t flag;
#endif /* USE_FPU */

    /* r4 ~ r11 register */
    rt_uint32_t r4;
    rt_uint32_t r5;
    rt_uint32_t r6;
    rt_uint32_t r7;
    rt_uint32_t r8;
    rt_uint32_t r9;
    rt_uint32_t r10;
    rt_uint32_t r11;

    struct exception_stack_frame exception_stack_frame;
};

3.2、初始化线程钩子函数

  • 初始化线程的钩子函数,调用的是代码1;
  • RT_OBJECT_HOOK_CALL是个宏,宏定义是代码2;
  • 假设func != NULL,将宏展开是代码3;
  • rt_thread_inited_hook是个函数指针,定义是代码4;
  • 这个函数指针rt_thread_inited_hook在函数rt_thread_inited_sethook中被赋值,如下代码5;
  • 最后,rt_thread_inited_sethook是给用户使用的,设置的钩子函数会在线程初始化的时候调用;
1、RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));
2、#define RT_OBJECT_HOOK_CALL(func, argv) \
      do { if ((func) != RT_NULL) func argv; } while (0)
3、rt_thread_inited_hook(thread);
4、static void (*rt_thread_inited_hook) (rt_thread_t thread);
5、 void rt_thread_inited_sethook(void (*hook)(rt_thread_t thread))
    {
        rt_thread_inited_hook = hook;
    }

4、创建完线程后,线程启动rt_thread_startup做了什么?

简而言之就是线程从初始态进入了就绪态,线程插入了就绪队列,然后rt_schedule内核调度,具体如下:

5、SysTick_Handler里有哪些操作?

SysTick_Handler里调用了rt_tick_increase,rt_tick_increase主要做了两件事:

  • 检查当前运行的线程,看看时间片有没有用完,用完了执行rt_schedule调度切换线程;
  • 检查定时器链表,看有没有超时溢出的,执行定时器回调函数;

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-23 11:02:21  更:2022-04-23 11:03:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:52:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码