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调度切换线程;
  • 检查定时器链表,看有没有超时溢出的,执行定时器回调函数;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值