在RT-Thread 操作系统的线程当中,一共有两种,一是静态线程,二是动态线程
接下来通过RT-Thread 源码来讲解线程是如何创建的,以及这两种线程的区别:
首先贴出RT-Thread 有关静态线程创建的源码:
/**
* This function will initialize a thread, normally it's used to initialize a
* static thread object.
*
* @param thread the static thread object
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_start the start address of thread stack
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(stack_start != RT_NULL);
/* init thread object */
rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);
return _rt_thread_init(thread,
name,
entry,
parameter,
stack_start,
stack_size,
priority,
tick);
}
我们可以看到,代码中第30 行的语句是初始化一个线程,这个初始化函数rt_object_init
作用是通过指定的对象名称(第三个形参)将该名称赋给线程对象,然后通过指定的对象类型(第二个形参)将该线程添加进对象系统管理(信息列表)当中。
rt_object_init
源码(删去无关成分):/**
* This function will initialize an object and add it to object system
* management.
*
* @param object the specified object to be initialized.
* @param type the object type.
* @param name the object name. In system, the object's name must be unique.
*/
void rt_object_init(struct rt_object *object,
enum rt_object_class_type type,
const char *name)
{
register rt_base_t temp;
struct rt_object_information *information;
/* get object information */
information = rt_object_get_information(type);
RT_ASSERT(information != RT_NULL);
/* initialize object's parameters */
/* set object type to static */
object->type = type | RT_Object_Class_Static;
/* copy name */
rt_strncpy(object->name, name, RT_NAME_MAX);
RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));
/* lock interrupt */
temp = rt_hw_interrupt_disable();
{
/* insert object into information object list */
rt_list_insert_after(&(information->object_list), &(object->list));
}
/* unlock interrupt */
rt_hw_interrupt_enable(temp);
}
通过rt_object_init
初始化线程之后,在第32 行就返回一个_rt_thread_init
函数的值,请注意这个函数和静态线程创建函数rt_thread_init
十分相似。其实静态线程创建函数rt_thread_init
就是个壳子,其核心代码才是这个_rt_thread_init
函数。
通过上一个函数rt_object_init
,已将线程赋予了名字,并且加入了对象系统管理当中,现在就需要分析_rt_thread_init
函数:
_rt_thread_init
函数源码(删去无关成分):static rt_err_t _rt_thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
/* init thread list */
rt_list_init(&(thread->tlist));
thread->entry = (void *)entry;
thread->parameter = parameter;
/* stack init */
thread->stack_addr = stack_start;
thread->stack_size = stack_size;
/* init thread stack */
rt_memset(thread->stack_addr, '#', thread->stack_size);
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(void *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),
(void *)rt_thread_exit);
/* priority init */
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
thread->init_priority = priority;
thread->current_priority = priority;
thread->number_mask = 0;
/* tick init */
thread->init_tick = tick;
thread->remaining_tick = tick;
/* error and flags */
thread->error = RT_EOK;
thread->stat = RT_THREAD_INIT;
/* initialize cleanup function and user data */
thread->cleanup = 0;
thread->user_data = 0;
/* init thread timer */
rt_timer_init(&(thread->thread_timer),
thread->name,
rt_thread_timeout,
thread,
0,
RT_TIMER_FLAG_ONE_SHOT);
RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));
return RT_EOK;
}
_rt_thread_init
函数源码中第23 行的rt_hw_stack_init
函数作用是从控制块开始定义一个结构体用来存放和线程有关的信息(即线程堆),源码如下:
rt_hw_stack_init
函数源码(删去无关成分):rt_uint8_t *rt_hw_stack_init(void *tentry,
void *parameter,
rt_uint8_t *stack_addr,
void *texit)
{
struct stack_frame *stack_frame;
rt_uint8_t *stk;
unsigned long i;
stk = stack_addr + sizeof(rt_uint32_t);
stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
stk -= sizeof(struct stack_frame);
stack_frame = (struct stack_frame *)stk;
/* init all register */
for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
{
((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
}
stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */
stack_frame->exception_stack_frame.r1 = 0; /* r1 */
stack_frame->exception_stack_frame.r2 = 0; /* r2 */
stack_frame->exception_stack_frame.r3 = 0; /* r3 */
stack_frame->exception_stack_frame.r12 = 0; /* r12 */
stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */
stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */
stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR */
/* return task's current stack address */
return stk;
}
rt_hw_stack_init
函数源码第10 行相当于从堆的起始地址加上32位设为末地址;第11 行的RT_ALIGN_DOWN
函数作用是返回指定宽度对齐的向下数,例如RT_ALIGN_DOWN(13, 4)
将返回12;
第12 行即是在将堆格式化对齐后,减去堆栈结构所占用空间,即此时的stk 等于线程堆栈的起始地址了,然后返回stk。
此时再回到_rt_thread_init
函数第23 行,通过上述讲解,我们可知此时线程的堆指针sp 指向了rt_hw_stack_init
函数返回的stk,即stack_frame 的起始地址。
第48 行的rt_timer_init
函数作用是初始化一个定时器,通常初始化一个静态的定时器对象。该定时器作用就是作为时间片计数
除此之外,其余代码都是起配置控制块作用。
最后的RT_OBJECT_HOOK_CALL
是一个内核对象钩子函数。
#ifdef RT_USING_HEAP
/**
* This function will create a thread object and allocate thread object memory
* and stack.
*
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the created thread object
*/
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
struct rt_thread *thread;
void *stack_start;
thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,
name);
if (thread == RT_NULL)
return RT_NULL;
stack_start = (void *)RT_KERNEL_MALLOC(stack_size);
if (stack_start == RT_NULL)
{
/* allocate stack failure */
rt_object_delete((rt_object_t)thread);
return RT_NULL;
}
_rt_thread_init(thread,
name,
entry,
parameter,
stack_start,
stack_size,
priority,
tick);
return thread;
}
理解了静态线程的创建过程,对于动态线程的创建过程也就容易明白了。
动态线程的创建与静态线程的创建之不同就在于,动态线程不需要手动定义线程控制块和线程堆,但是一个线程能够运行总需要有线程控制块和线程堆栈吧,于是动态线程的创建就是利用系统帮助我们创建动态内存作为线程控制块和线程栈
从以上代码的第22 行至37 行可以很容易地看出,这部分代码的作用就是先定义一个控制块指针和一个栈指针,然后通过rt_object_allocate
函数从系统资源中分配一个对象用以存放线程控制块信息,再然后通过RT_KERNEL_MALLOC
函数向内核索取动态内存用以存放线程运行资源。
随后代码便与静态线程创建过程相同。
分析了静态线程和动态线程各自的创建过程,可知:其实静态线程和动态线程本质上是一样的,只不过静态线程的线程控制块和线程堆存放于用户自定义的静态内存空间中,而动态线程的线程控制块和线程栈是由系统分配的动态内存空间罢了。
从创建线程的过程当中也可以得知,创建一个线程,主要的任务就是将线程堆栈指针指向我们所定义的堆、开启定时器、配置其他的控制块参数。