啟動順序:
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 "\n"
同樣的來看下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:調用任務調度函數調度下一個優先級最高的任務。