知識回顧——RTThread中的多優先順序
多優先順序的使用:
RTThread支援可定義的多優先順序,在RTThread中,優先順序的數字越小,其邏輯優先順序就越高。
在深入瞭解它的優先順序排程機制之前,我們看一下RTT系統的優先順序的使用方法:
開啟RTT Studio,建立一個工程,開啟它的rtconfig.h檔案,可以看到一個巨集定義:
#define RT_THREAD_PRIORITY_MAX 32
這句話定義了我們的最大優先順序。接下來進行執行緒的優先順序定義(這裡直接引用一個led1執行緒初始化定義):
rt_thread_init(&led1_thread, //執行緒控制塊地址 "led1", //執行緒名稱 led1_thread_entry, //執行緒入口函式 RT_NULL, //入口函式引數 &rt_led1_thread_stack[0], //棧起始地址 sizeof(rt_led1_thread_stack), //執行緒棧大小 3, //優先順序 20); //執行緒時間片
我們獎優先順序定義為3,這樣我們就可以使這個執行緒按照我們所需的優先度運行了。
優先順序表的實現:
我們發現,使用優先順序是非常簡單的,但它的實現需要靠兩個變數來支援:執行緒優先順序組和執行緒優先順序表。
開啟rtt核心檔案中的scheduler.c檔案,在開頭的一段就可以看到兩個全域性變數的定義:
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
rt_uint32_t rt_thread_ready_priority_group;
第一句話是一個rt_list_t 型別的定義,它是一個執行緒優先順序表(也叫就緒列表)。本質上它是一個連結串列陣列,每個連結串列的索引就是執行緒的優先順序。假設一個執行緒被創立,它的節點就會被掛載到相應的連結串列中。
同一個優先順序表的執行緒都會被插入到同一個連結串列中,而同優先順序的執行緒執行需要RTT系統的時間片支援。
當執行緒需要插入或者移出時,就需要排程器出馬了,在scheduler.c中,有排程器插入執行緒和移除執行緒兩個函式,排程器通過這兩個函式將執行緒插入就緒列表或將執行緒從就緒列表中移除:
/*插入函式*/ void rt_schedule_insert_thread(struct rt_thread *thread) { register rt_base_t temp; RT_ASSERT(thread != RT_NULL); /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* it's current thread, it should be RUNNING thread */ if (thread == rt_current_thread) { thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK); goto __exit; } /* READY thread, insert to ready queue */ thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK); /* insert thread to ready list */ rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), &(thread->tlist)); RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n", RT_NAME_MAX, thread->name, thread->current_priority)); /* set priority mask */ #if RT_THREAD_PRIORITY_MAX > 32 rt_thread_ready_table[thread->number] |= thread->high_mask; #endif rt_thread_ready_priority_group |= thread->number_mask; __exit: /* enable interrupt */ rt_hw_interrupt_enable(temp); }
/*刪除函式*/ void rt_schedule_remove_thread(struct rt_thread *thread) { register rt_base_t level; RT_ASSERT(thread != RT_NULL); /* disable interrupt */ level = rt_hw_interrupt_disable(); RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("remove thread[%.*s], the priority: %d\n", RT_NAME_MAX, thread->name, thread->current_priority)); /* remove thread from ready list */ rt_list_remove(&(thread->tlist)); if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority]))) { #if RT_THREAD_PRIORITY_MAX > 32 rt_thread_ready_table[thread->number] &= ~thread->high_mask; if (rt_thread_ready_table[thread->number] == 0) { rt_thread_ready_priority_group &= ~thread->number_mask; } #else rt_thread_ready_priority_group &= ~thread->number_mask; #endif } /* enable interrupt */ rt_hw_interrupt_enable(level); }
優先順序組的實現:
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
rt_uint32_t rt_thread_ready_priority_group;、
然後我們看一下全域性變數定義的第二句話,這是執行緒就緒優先順序組的定義。實際上,它就是一個32位的整型數,每一個位對應著一個優先順序組。如果我們的優先順序大於32個,那麼RTT系統便會定義一個數組成員,每個成員都表示32個優先順序。
那它是怎樣運作的呢?這裡做一個假設:一個優先順序為5的執行緒已經準備好了,這時優先順序組的第5個位就會置1。
之後下一個系統週期開始,排程器開始從最高位開始掃描,當它掃描到第5個位時,就會發現第五個位為1,排程器就會從優先順序表中尋找下標為5的陣列,取出相應執行緒控制塊,從而跳轉到相應的執行緒。
具體的程式是RTT中的kservice.c的__rt_ffs()函式:
int __rt_ffs(int value) { if (value == 0) return 0; if (value & 0xff) return __lowest_bit_bitmap[value & 0xff] + 1; if (value & 0xff00) return __lowest_bit_bitmap[(value & 0xff00) >> 8] + 9; if (value & 0xff0000) return __lowest_bit_bitmap[(value & 0xff0000) >> 16] + 17; return __lowest_bit_bitmap[(value & 0xff000000) >> 24] + 25; }
我們可以看到,value值經過運算後呼叫_lowest_bit_bitmap這個陣列:
const rt_uint8_t __lowest_bit_bitmap[] = { /* 00 */ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 10 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 20 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 30 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 40 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 50 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 60 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 70 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 80 */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 90 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* A0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* B0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* C0 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* D0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* E0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* F0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 };
通過此表,就會返回第一個(邏輯優先順序從高到低的第一個)置1的位號。
通過優先順序表,優先順序組,執行緒排程器就能實現多優先順序的排程功能。