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