继续按照程序流程前进,前面提到的时钟初始化,堆初始化都还未跳出板级初始化rt_hw_board_init函数
然后看看rt_hw_board_init函数中剩下的代码
/* Pin driver initialization is open by default */
#ifdef RT_USING_PIN
rt_hw_pin_init();
#endif
/* USART driver initialization is open by default */
#ifdef RT_USING_SERIAL
rt_hw_usart_init();
#endif
/* Set the shell console output device */
#ifdef RT_USING_CONSOLE
rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
/* Board underlying hardware initialization */
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
也就是分别对引脚、串口、shell进行了初始化,最后rt_components_board_init是对执行添加进自启动的函数,自启动相关前篇文章描述,这里也就不谈了。
这样又回到了componts.c的rtthread_startup函数中,板级初始化之后需要执行三个函数
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
其中rt_show_version是打印一些系统信息,rt_system_timer_init则是基于系统时钟的定时器初始化。最后就是操作系统的重头线程调度初始化了rt_system_scheduler_init,但这里先不谈调度器,而是看看本文主题--线程,调度器相关下篇再分析。
先从线程结构体入手,便能了解到每个线程所包含的信息,该定义在文件rtdef.h中
/**
* Thread structure
*/
struct rt_thread
{
/* rt object */
char name[RT_NAME_MAX]; /**< the name of thread */
rt_uint8_t type; /**< type of object 对象类型*/
rt_uint8_t flags; /**< thread's flags */
#ifdef RT_USING_MODULE
void *module_id; /**< id of application module */
#endif
rt_list_t list; /**< the object list */
rt_list_t tlist; /**< the thread list */
/* stack point and entry */
void *sp; /**< stack point */
void *entry; /**< entry */
void *parameter; /**< parameter */
void *stack_addr; /**< stack address */
rt_uint32_t stack_size; /**< stack size */
/* error code */
rt_err_t error; /**< error code */
rt_uint8_t stat; /**< thread status */
#ifdef RT_USING_SMP
rt_uint8_t bind_cpu; /**< thread is bind to cpu */
rt_uint8_t oncpu; /**< process on cpu` */
rt_uint16_t scheduler_lock_nest; /**< scheduler lock count */
rt_uint16_t cpus_lock_nest; /**< cpus lock count */
#endif /*RT_USING_SMP*/
/* priority */
rt_uint8_t current_priority; /**< current priority 当前优先级*/
rt_uint8_t init_priority; /**< initialized priority 初始化优先级*/
#if RT_THREAD_PRIORITY_MAX > 32
rt_uint8_t number;
rt_uint8_t high_mask;
#endif
rt_uint32_t number_mask;
#if defined(RT_USING_EVENT)
/* thread event */
rt_uint32_t event_set;
rt_uint8_t event_info;
#endif
#if defined(RT_USING_SIGNALS)
rt_sigset_t sig_pending; /**< the pending signals 等待信号*/
rt_sigset_t sig_mask; /**< the mask bits of signal 掩码*/
#ifndef RT_USING_SMP
void *sig_ret; /**< the return stack pointer from signal */
#endif
rt_sighandler_t *sig_vectors; /**< vectors of signal handler */
void *si_list; /**< the signal infor list */
#endif
rt_ubase_t init_tick; /**< thread's initialized tick能占用的tick */
rt_ubase_t remaining_tick; /**< remaining tick 剩余可运行的tick*/
struct rt_timer thread_timer; /**< built-in thread timer */
void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit */
/* light weight process if present */
#ifdef RT_USING_LWP
void *lwp;
#endif
rt_uint32_t user_data; /**< private user data beyond this thread */
};
typedef struct rt_thread *rt_thread_t;
如果看了内核对象管理那篇,前几个成员看着肯定熟悉吧,其实就和内核对象结构体struct rt_object成员是一样,最后多了一个线程链表rt_list_t tlist。里面其他成员单独拿出来是没啥说的,在具体使用时就清楚了。
线程相关函数分析
静态创建线程
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);
}
RTM_EXPORT(rt_thread_init);
英文单词能直接表达其意思就不打备注了,其中RTM_EXPORT宏定义是将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);
//给堆栈指针赋值
#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 - sizeof(rt_ubase_t)),
(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;
#ifdef RT_USING_SMP//多核
/* not bind on any cpu */
thread->bind_cpu = RT_CPUS_NR;
thread->oncpu = RT_CPU_DETACHED;
/* lock init */
thread->scheduler_lock_nest = 0;
thread->cpus_lock_nest = 0;
#endif /*RT_USING_SMP*/
/* 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_thread_timeout,他实际上就是将对于的线程踢出挂起链表,加入到就绪链表,并且手动执行调度器切换,在看调度器部分的时候应该会配合则这个函数说明,这里就不多言了。
移除进程函数
该函数与静态创建线程配对,其实静态创建于动态创建区域就在于是在运行之前就分配好了内存,还是在运行中使用malloc来分配,其他部分都是一样的,这里就不再分析动态创建删除等相关的函数了。
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) //线程被创建但未启动则处于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;
/* detach object */
rt_object_detach((rt_object_t)thread); //将线程从管理器中移除
if (thread->cleanup != RT_NULL) //如果有绑定清除回调
{
/* 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;
}
RTM_EXPORT(rt_thread_detach);
启动线程
rt_thread_resume函数是将挂起线程切换成就绪状态并且放入线程链表中去,在线程启动函数rt_thread_startup中被调用,所以先拿出来说说。
其实线程状态实际就四种,初始、就绪、挂起、关闭。初始就是线程刚刚被创建时候的状态,就绪表示该线程已经准备好被执行,就等调度器乱转到它了,挂起表示该线程在获得某种条件前不被执行。关闭和初始对应,就是线程被删除。
rt_err_t rt_thread_resume(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 resume: %s\n", thread->name));
//当线程状态不是SUSPEND时,错误退出
if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_SUSPEND)
{
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: thread disorder, %d\n",
thread->stat));
return -RT_ERROR;
}
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* remove from suspend list */
//这里是将该线程链表指针清空
rt_list_remove(&(thread->tlist));
rt_timer_stop(&thread->thread_timer);
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* insert to schedule ready list */
//这里是将状态切换成READY,并且插入到线程链表中去
rt_schedule_insert_thread(thread);
RT_OBJECT_HOOK_CALL(rt_thread_resume_hook, (thread));
return RT_EOK;
}
RTM_EXPORT(rt_thread_resume);
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; //状态由INIT 变成了SUSPEND
/* then resume it */
rt_thread_resume(thread);//切换成就绪态,加入线程链表
if (rt_thread_self() != RT_NULL)
{
/* do a scheduling */
rt_schedule();//该函数是手动切换一次调度器轮转,选择一个最高优先级的线程执行
}
return RT_EOK;
}
RTM_EXPORT(rt_thread_startup);
线程挂起
与函数rt_thread_resume相对于的是rt_thread_suspend,前者是将挂起转为就绪,后者是将就绪转为挂起。
rt_err_t rt_thread_suspend(rt_thread_t thread)
{
register rt_base_t stat;
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));
stat = thread->stat & RT_THREAD_STAT_MASK;
if ((stat != RT_THREAD_READY) && (stat != RT_THREAD_RUNNING))
{
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();
if (stat == RT_THREAD_RUNNING)
{
/* not suspend running status thread on other core */
//当执行挂起正在运行的程序则会报错,正在运行其实意思就是自己,也就是自己不能挂起自己
RT_ASSERT(thread == rt_thread_self());
}
/* change thread stat */
//踢出链表,状态切换为suspend
rt_schedule_remove_thread(thread);
thread->stat = RT_THREAD_SUSPEND | (thread->stat & ~RT_THREAD_STAT_MASK);
/* 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;
}
RTM_EXPORT(rt_thread_suspend);
rt_thread_sleep函数实际上是基于rt_thread_suspend和定时器做了一个功能性封装,先将对于线程挂起,然后开启定时器,由于定时器超时函数被设定成了将对于线程加入就绪链表中去,就达到了类似让线程挂起特定时间的目的。
rt_err_t rt_thread_sleep(rt_tick_t tick)
{
register rt_base_t temp;
struct rt_thread *thread;
/* set to current thread */
thread = rt_thread_self();
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* 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_hw_stack_init,它实际上是根据不同内核进行的栈信息填充,我们分配给线程的栈在这里被切成了两部分,第一部分是CPU寄存器,主要是用来保存上下文,第二部分才是线程可使用的栈空间。
网友评论