[
你可以任意转载本文,但转载请保留本段声明。
作者将尽量追求内容的正确,但不对正确性作出保证。
未经作者许可,不得用于商业目的。
作者:byeyear/告别年代
Email: byeyear@hotmail.com
]
首先纠正一下上一篇中的一个错误。对象类型(是否为系统对象)记录在rt_thread结构的type成员里,而不是flag成员里。 何谓线程 所谓“线程”,其实就是一段子程序。只是它和普通子程序的区别在于,这段子程序可能随时被操作系统所中断,并在将来的某个时间点恢复运行。为了达到这个目的,我们需要一些基础设施。例如,我们需要在中断一个线程的运行时保存当前的PC寄存器,这样我们才能在恢复其运行时知道从何处重新开始;我们还需要保存当前CPU的所有其他寄存器,因为这些寄存器可能被其他线程所破坏。 线程的创建 有两个函数可用于创建线程:rt_thread_init和rt_thread_create。这两者的区别在于,前者需要调用者自己分配线程对象和线程栈,而后者由操作系统分配线程对象和栈空间。换句话说,rt_thread_create比rt_thread_init多了资源分配的操作。这里,我们先看rt_thread_init。 rt_thread_init首先对调用者提供的线程对象进行初始化: rt_err_t rt_thread_init(...) { rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name); ... } rt_object_init的代码做以下几个工作: 1. 将对象类型设置为RT_Object_Class_Static 这个标记表明该对象是由用户分配而不是操作系统分配的。 一般来说,硬件资源的分配遵循“谁申请,谁释放”的原则。因此,当线程退出运行时,操作系统需要判断线程对象和线程栈占用的存储空间是用户分配的还是系统分配的。如果这些存储空间是用户分配的,那么操作系统不会对这些内存做什么事情;但如果这些存储空间是系统分配的(当你用rt_thread_create创建线程时),操作系统就要负责释放线程对象和线程栈所占用的存储空间。判断的依据就是线程对象类型。 2. 将name参数(对象名称)复制到线程对象中 3. 将该线程对象加入线程对象链表,方便后面的管理。 线程对象初始化完毕后,就调用_rt_thread_init进行实际的初始化工作: 1. 首先初始化该线程的tlist成员。 我们前面说过,tlist成员是用来将该线程挂入某个状态队列的。但是该线程目前还没有初始化完成,所以目前不将其挂入任何一个队列,仅将其prev和next字段指向自身。 2. 将entry、parameter、stack_start、stack_size四个参数保存到线程对象中。 entry和parameter是线程入口地址和线程参数;stack_start是线程栈起始地址。在后面将线程投入运行时,这些参数都是必须的。 stack_size并不是一个线程运行所必须的参数;但该变量可以帮助我们检查线程在运行过程中是否发生了堆栈溢出。RTT将线程栈空间全部初始化为'#'。这样,在线程运行过程中,通过检查堆栈内容,就可以判断该线程实际使用的最大堆栈空间,以及线程堆栈空间是否溢出。 3. 初始化线程栈 rt_hw_stack_init(...) 这个函数的作用是将线程栈模拟成这个线程刚刚被切换出去的样子。当调度器将该线程投入运行时,它不会知道这个线程到底是真的曾经被切换出去,还是被模拟成这个样子。这是一个体系结构相关的函数,比较关键的是这一句(以arm7为例): *(--stk) = (unsigned long)texit; /* lr */ texit是RTT提供的系统函数。我们结合一个最简单的线程作为例子来说明这一句的作用: void SampleThread(void *param) { } 这个线程什么也不做,投入运行后直接退出。函数返回时将执行bx lr指令,即将lr寄存器的内容从堆栈中弹入pc。而lr的值就是上面那条语句中的texit入口地址。因此,该线程退出后将执行texit()函数。texit()会做一些必要的清理工作,然后进行一次调度(因为当前线程已经退出运行了)。我们后面还要回到texit()函数中去。 从这里我们看到,RTT不像uc/OS那样要求线程一定要有一个while(1)循环。对于RTT来说,当一个线程不再需要时,可以像函数返回一样使用return语句令其退出运行。(插一句话,RTT关于texit的设计是激发我当初学习RTT的主要原因之一。) 4. 将优先级、初始tick都保存到线程对象。这里我们看到,RTT将init_tick和remaining_tick都设置为相同的值。 5. 初始化thread_timer成员。thread_timer的作用我们前面已经说过了,rt_timer_init函数我们留到定时器相关内容中分析。
|