前言
- TCB 线程控制块
- RT-Thread中每个线程的信息用线程控制块(Thread Control-Block,缩写为TCB)表示,它是定义在rtdef.h中的struct结构体,用来描述一个线程所有必要信息;
- 线程的优先级别用非负整数(即无符号整数)表示。数值越小,优先级越高;
- 系统的线程优先级的数目固定,最多支持256级;
- 系统中的线程数目不做任何限制,线程的数目仅受限于系统RAM的大小。
- 线程控制块存储的数据结构为链表;
线程创建
- 在RT-Thread中分为静态创建和动态创建,二者实现的区别在于静态创建时所需的存储区由用户分配制定,而动态创建时所需的存储区由系统计算并调用rt_object_allocate进行申请。代码如下:
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);
...
}
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_object_init进行线程对象初始化,将新初始化的线程对象插入对象链表中,而动态创建,则会在静态创建的上述操作基础上增加线程对象内存分配申请及线程堆栈内存分配申请。
- 二者在最后一步实现为 _rt_thread_init,此函数主要完成对TCB的初始化,线程定时器的初始化,及线程堆栈初始化,具体实现如下:
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);
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(void *)((char *)thread->stack_addr),
(void *)rt_thread_exit);
#else
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(void *)((char *)thread->stack_addr + thread->stack_size - 4),
(void *)rt_thread_exit);
#endif
/* priority init */
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
thread->init_priority = priority;
thread->current_priority = priority;
thread->number_mask = 0;
#if RT_THREAD_PRIORITY_MAX > 32
thread->number = 0;
thread->high_mask = 0;
#endif
/* 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);
/* initialize signal */
#ifdef RT_USING_SIGNALS
thread->sig_mask = 0x00;
thread->sig_pending = 0x00;
thread->sig_ret = RT_NULL;
thread->sig_vectors = RT_NULL;
thread->si_list = RT_NULL;
#endif
#ifdef RT_USING_LWP
thread->lwp = RT_NULL;
#endif
RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));
return RT_EOK;
}
- rt_hw_stack_init用于初始化线程堆栈,这里会根据不同处理器架构做不同的实现,此处以Cortex-M4为例:
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;
};
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 */
#if USE_FPU
stack_frame->flag = 0;
#endif /* USE_FPU */
/* return task's current stack address */
return stk;
}
*. 以上在整个源码中仅在 _rt_thread_init中调用,上面在获取stk时,减去了4字节,这4字节表示的是栈指针的大小。而后进行了8字节向下对齐,向下对齐保证了不内存越界,而后就是对栈内存进行初始化,这里的0xdeadbeef表示已分配但未初始化的内存,具体可看各种内存初值描述。最后就是将线程入口函数传参放入R0,将线程退出函数地址放入LR,线程入口函数地址放入PC,这样在线程初始化完成,调用启动函数rt_thread_startup后,处理器就会从PC中获取入口函数地址,从而开始运行线程;在线程退出时,则会从LR中获取退出函数地址,从而停止线程,释放线程资源。
线程启动
- RT-Thread线程启动依赖于rt_thread_startup函数,该函数实现如下:
/**
* This function will start a thread and put it to system ready queue
*
* @param thread the thread to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_startup(rt_thread_t thread)
{
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
/* set current priority to init priority */
thread->current_priority = thread->init_priority;
/* calculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
thread->number = thread->current_priority >> 3; /* 5bit */
thread->number_mask = 1L << thread->number;
thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */
#else
thread->number_mask = 1L << thread->current_priority;
#endif
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n",
thread->name, thread->init_priority));
/* change thread stat */
thread->stat = RT_THREAD_SUSPEND;
/* then resume it */
rt_thread_resume(thread);
if (rt_thread_self() != RT_NULL)
{
/* do a scheduling */
rt_schedule();
}
return RT_EOK;
}
- 该函数主要功能:
- 更新线程状态。由初始态变更为挂起态;
- 更新了优先级位码。用于调度器获取已就绪最高优先级标志位线程索引,这里若设置的系统最大线程数大于32,由RT-Thread默认设定,将线程分为32组,每组8个线程,因此若最大线程数大于32,此处会将线程优先级先除8,即右移3位,之后将其取余,即与运算0x07,得到线程组中索引。
- 唤醒线程。调用rt_thread_resume函数,该函数此处不贴出。此函数将线程从挂起表中移除,暂停了线程定时器,并将线程移入调度器线程就绪表中,最后调用了线程唤醒钩子函数。
- 判断当前是否有线程占用处理器,若有,则进行一次调度切换;反之,则直接返回,等待下一次轮转调度。
线程分离/删除
- rt_thread_detach函数,将线程状态更新为关闭态,分离线程定时器,若线程为静态创建并且无清理函数,则直接将此线程对象分离;反之,则将其移入僵尸线程表中。具体实现如下:
/**
* This function will detach a thread. The thread object will be removed from
* thread queue and detached/deleted from system object management.
*
* @param thread the thread to be deleted
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_detach(rt_thread_t thread)
{
rt_base_t lock;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread));
if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT)
{
/* remove from schedule */
rt_schedule_remove_thread(thread);
}
/* release thread timer */
rt_timer_detach(&(thread->thread_timer));
/* change stat */
thread->stat = RT_THREAD_CLOSE;
if ((rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) &&
thread->cleanup == RT_NULL)
{
rt_object_detach((rt_object_t)thread);
}
else
{
/* disable interrupt */
lock = rt_hw_interrupt_disable();
/* insert to defunct thread list */
rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));
/* enable interrupt */
rt_hw_interrupt_enable(lock);
}
return RT_EOK;
}
- RT-Thread中类似功能函数还有rt_thread_delete以及rt_thread_exit,其中:
- rt_thread_delete与rt_thread_detach都能为用户主动调用,并且关于线程为动态创建的情况实现相同,不同之处在于rt_thread_detach能够操作系统线程(静态创建),而rt_thread_delete没有关于此的相关实现;
- rt_thread_exit只能为系统调用。此函数地址于rt_hw_stack_init中存储于栈中的LR中,当此线程出现异常崩溃时将触发此函数调用。
线程切换
- RT-Thread中提供了能为用户所调用的线程切换函数,该函数为rt_thread_yield。具体实现如下:
/**
* This function will let current thread yield processor, and scheduler will
* choose a highest thread to run. After yield processor, the current thread
* is still in READY state.
*
* @return RT_EOK
*/
rt_err_t rt_thread_yield(void)
{
register rt_base_t level;
struct rt_thread *thread;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* set to current thread */
thread = rt_current_thread;
/* if the thread stat is READY and on ready queue list */
if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY &&
thread->tlist.next != thread->tlist.prev)
{
/* remove thread from thread list */
rt_list_remove(&(thread->tlist));
/* put thread to end of ready queue */
rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
&(thread->tlist));
/* enable interrupt */
rt_hw_interrupt_enable(level);
rt_schedule();
return RT_EOK;
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
return RT_EOK;
}
- 该函数操作对象为当前运行线程,并且只在该线程处于就绪态并且线程表中线程数不为0时可执行。具体执行为将线程从线程挂起表中移除,头插入调度器就绪表中,之后进行一次调度(调用rt_shedule)。
线程休眠
- rt_thread_sleep函数用于线程休眠,此函数虽然不对用户开放提供,但封装了供用户使用的两个休眠函数rt_thread_delay以及rt_thread_mdelay,二者区别在于休眠时间单位的不同,rt_thread_delay单位为滴答数;rt_thread_mdelay单位为ms,这里通过rt_tick_from_millisecond函数将滴答数进行了单位转换。
- rt_thread_sleep具体实现如下:
/**
* This function will let current thread sleep for some ticks.
*
* @param tick the sleep ticks
*
* @return RT_EOK
*/
rt_err_t rt_thread_sleep(rt_tick_t tick)
{
register rt_base_t temp;
struct rt_thread *thread;
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* set to current thread */
thread = rt_current_thread;
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
/* suspend thread */
rt_thread_suspend(thread);
/* reset the timeout of thread timer and start it */
rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);
rt_timer_start(&(thread->thread_timer));
/* enable interrupt */
rt_hw_interrupt_enable(temp);
rt_schedule();
/* clear error number of this thread to RT_EOK */
if (thread->error == -RT_ETIMEOUT)
thread->error = RT_EOK;
return RT_EOK;
}
- 若线程执行函数调用此休眠函数,则将会被挂起,挂起时间则通过线程定时器计时,当计时时间到达时,则会调用定时器超时回调函数,即rt_thread_timeout。rt_thread_timeout同样能够为用户所调用,具体实现如下:
/**
* This function is the timeout function for thread, normally which is invoked
* when thread is timeout to wait some resource.
*
* @param parameter the parameter of thread timeout function
*/
void rt_thread_timeout(void *parameter)
{
struct rt_thread *thread;
thread = (struct rt_thread *)parameter;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
/* set error number */
thread->error = -RT_ETIMEOUT;
/* remove from suspend list */
rt_list_remove(&(thread->tlist));
/* insert to schedule ready list */
rt_schedule_insert_thread(thread);
/* do schedule */
rt_schedule();
}
- 此函数做的操作也是非常简单的,仅是将错误码设为超时,最后将线程从挂起表中移除,插入调度器就绪表中,最后进行一次调度。
线程挂起
- 当线程休眠时,就是处于挂起状态。当用户调用了rt_thread_sleep函数时,实际上调用了rt_thread_suspend函数,此函数仅对已就绪线程有效,具体实现如下:
/**
* This function will suspend the specified thread.
*
* @param thread the thread to be suspended
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*
* @note if suspend self thread, after this function call, the
* rt_schedule() must be invoked.
*/
rt_err_t rt_thread_suspend(rt_thread_t thread)
{
register rt_base_t temp;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: %s\n", thread->name));
if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY)
{
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n",
thread->stat));
return -RT_ERROR;
}
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* change thread stat */
thread->stat = RT_THREAD_SUSPEND | (thread->stat & ~RT_THREAD_STAT_MASK);
rt_schedule_remove_thread(thread);
/* stop thread timer anyway */
rt_timer_stop(&(thread->thread_timer));
/* enable interrupt */
rt_hw_interrupt_enable(temp);
RT_OBJECT_HOOK_CALL(rt_thread_suspend_hook, (thread));
return RT_EOK;
}
- 该函数作用与rt_thread_resume函数截然相反,将要挂起线程状态更新为挂起态,将其从就绪表中移除,暂停了线程定时器,最后调用了线程挂起钩子函数。
线程控制
/**
* This function will control thread behaviors according to control command.
*
* @param thread the specified thread to be controlled
* @param cmd the control command, which includes
* RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread;
* RT_THREAD_CTRL_STARTUP for starting a thread;
* RT_THREAD_CTRL_CLOSE for delete a thread.
* @param arg the argument of control command
*
* @return RT_EOK
*/
rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg)
{
register rt_base_t temp;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
switch (cmd)
{
case RT_THREAD_CTRL_CHANGE_PRIORITY:
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* for ready thread, change queue */
if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY)
{
/* remove thread from schedule queue first */
rt_schedule_remove_thread(thread);
/* change thread priority */
thread->current_priority = *(rt_uint8_t *)arg;
/* recalculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
thread->number = thread->current_priority >> 3; /* 5bit */
thread->number_mask = 1 << thread->number;
thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */
#else
thread->number_mask = 1 << thread->current_priority;
#endif
/* insert thread to schedule queue again */
rt_schedule_insert_thread(thread);
}
else
{
thread->current_priority = *(rt_uint8_t *)arg;
/* recalculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
thread->number = thread->current_priority >> 3; /* 5bit */
thread->number_mask = 1 << thread->number;
thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */
#else
thread->number_mask = 1 << thread->current_priority;
#endif
}
/* enable interrupt */
rt_hw_interrupt_enable(temp);
break;
case RT_THREAD_CTRL_STARTUP:
return rt_thread_startup(thread);
#ifdef RT_USING_HEAP
case RT_THREAD_CTRL_CLOSE:
return rt_thread_delete(thread);
#endif
default:
break;
}
return RT_EOK;
}
- 此函数提供了线程开启、线程关闭以及线程优先级改变的操作,此处不再赘述线程开启以及关闭的操作,这里仅是将优先级码改变为用户设定的那个优先级值,将传入参数设定为uint8_t类型指针,避免在执行位码调度算法时内存越界。
其余
- 线程搜索函数: rt_thread_find。此函数通过遍历线程表,并通过线程名对比,获取线程,具体实现如下:
/**
* This function will find the specified thread.
*
* @param name the name of thread finding
*
* @return the found thread
*
* @note please don't invoke this function in interrupt status.
*/
rt_thread_t rt_thread_find(char *name)
{
struct rt_object_information *information;
struct rt_object *object;
struct rt_list_node *node;
/* enter critical */
if (rt_thread_self() != RT_NULL)
rt_enter_critical();
/* try to find device object */
information = rt_object_get_information(RT_Object_Class_Thread);
RT_ASSERT(information != RT_NULL);
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
object = rt_list_entry(node, struct rt_object, list);
if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0)
{
/* leave critical */
if (rt_thread_self() != RT_NULL)
rt_exit_critical();
return (rt_thread_t)object;
}
}
/* leave critical */
if (rt_thread_self() != RT_NULL)
rt_exit_critical();
/* not found */
return RT_NULL;
}
- 线程钩子函数:包括线程挂起后、线程唤醒后以及线程创建后,这三个函数被定义为静态函数指针,通过rt_thread_xxxx_sethook函数进行设置,由用户实现钩子函数:
static void (*rt_thread_suspend_hook)(rt_thread_t thread);
static void (*rt_thread_resume_hook) (rt_thread_t thread);
static void (*rt_thread_inited_hook) (rt_thread_t thread);
/**
* @ingroup Hook
* This function sets a hook function when the system suspend a thread.
*
* @param hook the specified hook function
*
* @note the hook function must be simple and never be blocked or suspend.
*/
void rt_thread_suspend_sethook(void (*hook)(rt_thread_t thread))
{
rt_thread_suspend_hook = hook;
}
/**
* @ingroup Hook
* This function sets a hook function when the system resume a thread.
*
* @param hook the specified hook function
*
* @note the hook function must be simple and never be blocked or suspend.
*/
void rt_thread_resume_sethook(void (*hook)(rt_thread_t thread))
{
rt_thread_resume_hook = hook;
}
/**
* @ingroup Hook
* This function sets a hook function when a thread is initialized.
*
* @param hook the specified hook function
*/
void rt_thread_inited_sethook(void (*hook)(rt_thread_t thread))
{
rt_thread_inited_hook = hook;
}