启动顺序:
1 rtthread_startup进行对象初始化
2 rt_application_init进行线程对象初始化
rt_thread_create
创建线程
1 调用rt_object_allocate(RT_Object_Class_Thread,name);获取线程对象并创建线程
(a) rt_object_get_information(type),根据type获取线程的对象实例。查找方法是遍历rt_object_container并找到type能匹配上的。Rt-thread中所有的对象都包含在object.c中的静态变量rt_object_container中
static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] =
{
/* initialize object container - thread */
{RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread)},
#ifdef RT_USING_SEMAPHORE
/* initialize object container - semaphore */
{RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore)},
#endif
#ifdef RT_USING_MUTEX
/* initialize object container - mutex */
{RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex), sizeof(struct rt_mutex)},
#endif
#ifdef RT_USING_EVENT
/* initialize object container - event */
{RT_Object_Class_Event, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event), sizeof(struct rt_event)},
#endif
#ifdef RT_USING_MAILBOX
/* initialize object container - mailbox */
{RT_Object_Class_MailBox, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox), sizeof(struct rt_mailbox)},
#endif
#ifdef RT_USING_MESSAGEQUEUE
/* initialize object container - message queue */
{RT_Object_Class_MessageQueue, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue), sizeof(struct rt_messagequeue)},
#endif
#ifdef RT_USING_MEMHEAP
/* initialize object container - memory heap */
{RT_Object_Class_MemHeap, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap), sizeof(struct rt_memheap)},
#endif
#ifdef RT_USING_MEMPOOL
/* initialize object container - memory pool */
{RT_Object_Class_MemPool, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool), sizeof(struct rt_mempool)},
#endif
#ifdef RT_USING_DEVICE
/* initialize object container - device */
{RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device)},
#endif
/* initialize object container - timer */
{RT_Object_Class_Timer, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer), sizeof(struct rt_timer)},
#ifdef RT_USING_MODULE
/* initialize object container - module */
{RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), sizeof(struct rt_dlmodule)},
#endif
};
(b) object = (struct rt_object *)RT_KERNEL_MALLOC(information->object_size); 根据该对象的object_size大小生成线程对象。每个对象类的各个对象都是统一的大小
(c) rt_list_insert_after(&(information->object_list), &(object->list)); 将该对象插入到对象实例列表的表头
(d) 调用_rt_thread_init进行线程参数初始化,比如stack指针,回调函数,stack大小和优先级
(e) 调用rtthread_startup进行线程调度。
(e1): 调用rt_thread_resume,将创建的线程放入就绪列表(rt_schedule_insert_threa)。调用rt_list_remove(&(thread->tlist));将线程从挂起列表中删除
(e2): rt_schedule_insert_thread将线程插入到就绪列表。就绪列表是rt_thread_priority_table,每个优先级等级一个就绪列表
rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
&(thread->tlist));
(f) 调用rt_schedule进行调度。首先需要做的就是查找当前最高的优先级
RT-Thread内核中采用了基于位图(bitmap)的优先级算法(时间复杂度O(1),即与就绪线程的多少无关),通过位图的定位快速的获得优先级最高的线程。大致来说,就是每次调度的时间是恒定的:无论当前的系统中存在多少个线程,多少个优先级,rt-thread的调度函数总是可以在一个恒定的时间内选择出最高优先级的那个线程来执行。对不同优先级的线程,RT-Thread采用可抢占的方式:即高优先级的线程会“立刻”抢占低优先级的线程。
比如一个8字节的bitmap的十进制数字是6,对应的二进制数字是0000 0110。Bit0-Bit7分别代表优先级0-7。由于RT_thread中最高可支持256个优先级,最高优先级为0,最低优先级为256。因此bit为1的最低位代表当前最高优先级。所以bitmap等于6的时候,最高优先级是1。
RT-Thread内核中也允许创建相同优先级的线程。相同优先级的线程采用时间片轮转方式进行调度(也就是通常说的分时调度器),时间片轮转调度仅在当前系统中无更高优先级就绪线程存在的情况下才有效。每个线程的时间片大小都可以在初始化或创建这个线程时指定。在src/scheduler.c
首先来看优先级位图的定义,当优先级大于32个的时候,rt_thread_ready_table是32个字节的数组,也就是分别对应256个线程优先级。rt_thread_ready_priority_group是32位二级位图,用于查找一级位图中32个字节的最低非0字节。
当优先级个数小于32的时候,直接用rt_thread_ready_priority_group这个4字节的整型变量就可以表示了。
所谓二级位图,即我们首先确定32个字节中最低的非0的字节。为了实现这个效果,我们需要对这32个字节引入一个32个bit的位图变量,每一个bit位表示对应的字节是否为0。例如,这个32bit的位图变量的bit5为0,表示系统线程优先级256bit所分成的32个字节中的byte5为非0。为了区分,称这个32个bit的位图变量-字节位图变量
找到非0的字节位置后,在进行一次同样的操作,并对字节位置向左移三位。对应代码如下
rt_ffs的代码如下:
形成的bitmap图如下:
用Python就可以得出具体的位置
samples = 256
def getlowbit(byte):
c = 0
for i in range(0,8):
if(byte & 0x01):
return c
c = c+1
byte = byte >> 1
return 0
line =""
for i in range(0,samples):
print "%d," %getlowbit(i),
if((i+1)%16 == 0):
print "
"
同样的来看下Rt_thread中生成rt_thread_ready_priority_group的方法:
1 在_rt_thread_init中对thread中的init_priority,current_priority, number high_mask进行赋值
init_priority和current_priority中根据任务常见的priority进行赋值,number和high_mask暂时赋值为0。这些参数的在rt_thread_startup中会具体使用
2 在rt_thread_startup中的用法如下:
1) 如果RT_THREAD_PRIORITY_MAX大于32,证明有大于32个任务优先级存在。此时256个优先级=32*8,也就是32个字节。那么current_priority的高5bit代表优先级在哪一个字节。第3bit代表在字节中的位置。比如thread->current_priority =10=0000 1010。那么number=thread->current_priority >> 3=1,
thread->high_mask = 1L << (thread->current_priority & 0x07) high_mask赋值后=0100=4
(2) 如果RT_THREAD_PRIORITY_MAX小于32,则只有小于32个任务优先级存在。那么此时值需要对number_mask做一次运算赋值就可以了。比如当前优先级为7,则通过1L << thread->current_priority将第7个bit置为1。然后赋值给number_mask
3 在rt_thread_resume-> rt_schedule_insert_thread中,则对相应的bitmap参数进行赋值。任务优先级大于32的时候,使用rt_thread_ready_table数组,小于32的时候则使用32bit的rt_thread_ready_priority_group。在计算最高优先级的
在计算最高优先级的时候,则算出对应bitmap值最低位bit是第几位就可以知道当前最高的优先级了。
任务睡眠:
1 一般情况下调用rt_thread_mdelay进行当前任务延迟
rt_thread_mdelay(rt_int32_t ms),传入的参数为毫秒。调用rt_tick_from_millisecond将毫秒转换为tick值
2 调用rt_thread_sleep(tick)进行睡眠。主要是几个步骤 1:rt_thread_suspend 2: rt_timer_control 3: rt_timer_start 4: rt_schedule
(a) Rt_thread_suspend:
1 首先是将thread->stat设置成RT_THREAD_SUSPEND
2 然后调用rt_schedule_remove_thread 将当前thread从就绪列表列表中删除.并将rt_thread_ready_priority_group中对应的位置0
3 调用rt_thread_stop 将当前定时器停止。将定时器从属于自身等级的列表中删除。
(a) rt_timer_control:
1 根据传入的命令参数设置thread对应的参数,比如如果传入的命令是RT_TIMER_CTRL_SET_TIME则,对thread->timer->init_tick 设置成需要睡眠的tick数
(a) rt_timer_start开启定时器:开启定时器就是将该定时器任务加入到对应的队里中去。这里涉及到了跳表的操作来加速插入和排序。
4 rt_schedule:调用任务调度函数调度下一个优先级最高的任务。
原作者:一张红枫叶
|