この章の前は、RT-Threadはマルチ優先順位をサポートしていませんでした。最初に実行中のスレッドを手動で指定し、その後、3つのスレッド(アイドルスレッドを含む)が相互に切り替えました。この章では、優先順位関数を追加しました。実行される最初のプログラムは、実行可能リストで最も優先順位の高いプロセススレッドであり、スレッド切り替えも、実行可能スレッドの中で最も優先順位の高いスレッドに切り替えられます。
作動可能リストは、実際には、スレッド作動可能優先順位グループrt_thread_ready_priority_groupおよびスレッド優先順位テーブルrt_thread_priority_table [RT_THREAD_PRIORITY_MAX]で構成されます。この章の前に説明した作動可能リストは、スレッド優先順位テーブルrt_thread_priority_table [RT_THREAD_PRIORITY_MAX]を参照します。
スレッド対応優先順位グループは32ビット整数で、各ビットは優先順位に対応し、ビット0は優先順位0に対応し、ビット1は優先順位1に対応します。たとえば、優先度10のスレッドの準備ができたら、スレッド準備優先度グループのビット10を1に設定し、スレッドの準備ができていることを示します。次に、スレッド優先度テーブル10(rt_thread_priority_table [10]のインデックス値10に従って)この位置で糸に。
この章では、次の図に太字で示すように、スレッド制御ブロックに優先度関連のメンバーを追加します。また、エラーコードとスレッドステータスメンバーも追加します。
1.スレッド準備優先グループ
__rt_ffs関数を使用して、1に設定された32ビット整数の最初のビット番号(下位ビットから開始)を検索します。目的は、スレッド準備優先グループ内で最も優先度の高いスレッドを検索することです。コードは次のとおりです。
/**
* 该函数用于从一个32位的数中寻找第一个被置1的位(从低位开始),
* 然后返回该位的索引(即位号)
*
* @return 返回第一个置1位的索引号。如果全为0,则返回0。
*/
int __rt_ffs(int value)
{
/* 如果值为0,则直接返回0 */
if (value == 0) return 0;
/* 检查 bits [07:00]
这里加1的原因是避免当第一个置1的位是位0时
返回的索引号与值都为0时返回的索引号重复 */
if (value & 0xff)
return __lowest_bit_bitmap[value & 0xff] + 1;
/* 检查 bits [15:08] */
if (value & 0xff00)
return __lowest_bit_bitmap[(value & 0xff00) >> 8] + 9; //9==8+1
/* 检查 bits [23:16] */
if (value & 0xff0000)
return __lowest_bit_bitmap[(value & 0xff0000) >> 16] + 17; //17==16+1
/* 检查 bits [31:24] */
return __lowest_bit_bitmap[(value & 0xff000000) >> 24] + 25; //25==24+1
}
/*
* __lowest_bit_bitmap[] 数组的解析
* 将一个8位整形数的取值范围0~255作为数组的索引,索引值第一个出现1(从最低位开始)的位号作为该数组索引下的成员值。
* 举例:十进制数10的二进制为:0000 1010,从最低位开始,第一个出现1的位号为bit1,则有__lowest_bit_bitmap[10]=1
* 注意:只需要找到第一个出现1的位号即可
*/
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
};
2.スレッド1の優先順位表
スレッド優先度テーブルは、前述のレディリストです。各インデックス番号は、スレッドの優先度に対応します。双方向リンクリストは、このインデックスの下で維持されます。スレッドの準備ができると、スレッドは、優先度に従って、対応するインデックスのリンクリストに挿入されます。優先度のあるスレッドは同じリンクリストに挿入されます(同じ優先度の下に複数のスレッドがある場合、タイムスライスのサポートが必要です。これについては、後の章で詳しく説明します)。
①スレッドをスレッド優先順位テーブルに挿入し、2つの関数rt_schedule_insert_thread()とrt_schedule_remove_thread()によってそれぞれ削除します。それぞれの定義は次のとおりです。
void rt_schedule_insert_thread(struct rt_thread *thread)
{
register rt_base_t temp;
/* 关中断 */
temp = rt_hw_interrupt_disable();
/* 设置线程状态为就绪态 */
thread->stat = RT_THREAD_READY;
/* 根据线程的当前优先级将线程插入到就绪列表的优先级表对应的链表上 */
rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
&(thread->tlist));
/* 设置线程就绪优先级组中对应的位 */
rt_thread_ready_priority_group |= thread->number_mask;
/* 开中断 */
rt_hw_interrupt_enable(temp);
}
void rt_schedule_remove_thread(struct rt_thread *thread)
{
register rt_base_t temp;
/* 关中断 */
temp = rt_hw_interrupt_disable();
/* 将线程从就绪列表删除 */
rt_list_remove(&(thread->tlist));
if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority])))
{
rt_thread_ready_priority_group &= ~thread->number_mask;
}
/* 开中断 */
rt_hw_interrupt_enable(temp);
}
②スケジューラ初期化関数rt_system_scheduler_init()を変更して、現在の優先度をアイドルスレッドの優先度(つまり、最低)に設定します。
③スレッド初期化関数rt_thread_init()を修正
④現在の優先度マスクを設定し、rt_thread_resume関数を呼び出してスレッドを再開する最初の開始関数rt_thread_startup()を追加し、次にこの関数がrt_schedule_insert_thread関数を呼び出します(この関数は、以前に中断状態であった準備完了状態にスレッドを設定し、次にスレッドの現在の優先順位に従って対応する優先順位リストのリンクリストにスレッドを挿入し、対応する優先順位グループのビットを設定します)
idleアイドルスレッド初期化関数rt_thread_idle_init()を変更します
systemシステムスケジューラーの開始関数t_system_scheduler_start()を変更します。
systemシステムスケジューリング関数rt_schedule()を変更します。
blockingブロッキング遅延関数rt_thread_delay()を変更し、サスペンド時間を設定してスレッド状態をサスペンド状態に設定し、スレッド準備優先グループの対応するビットを0に設定します。
timeタイムベース更新関数rt_tick_increase()を変更します。一時停止時間が経過すると、スレッド準備優先度グループの対応するビットが1に設定されます(ただし、この章のコードは、スレッド状態を一時停止に変更し、準備完了に戻らないようですが、 P103で、スレッドは優先順位テーブルから削除されるべきであると述べられました。これはタイマーセクションで説明されます。削除されて再挿入されると、準備完了状態に設定されます)
3.主な機能
/************************************************************************
* @brief main函数
* @param 无
* @retval 无
*
* @attention
***********************************************************************
*/
int main(void)
{
/* 硬件初始化 */
/* 将硬件相关的初始化放在这里,如果是软件仿真则没有相关初始化代码 */
/* 关中断 */
rt_hw_interrupt_disable();
/* SysTick中断频率设置 */
SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND );
/* 调度器初始化 */
rt_system_scheduler_init();
/* 初始化空闲线程 */
rt_thread_idle_init();
/* 初始化线程 */
rt_thread_init( &rt_flag1_thread, /* 线程控制块 */
"rt_flag1_thread", /* 线程名字,字符串形式 */
flag1_thread_entry, /* 线程入口地址 */
RT_NULL, /* 线程形参 */
&rt_flag1_thread_stack[0], /* 线程栈起始地址 */
sizeof(rt_flag1_thread_stack), /* 线程栈大小,单位为字节 */
2); /* 优先级 */
/* 将线程插入到就绪列表 */
//rt_list_insert_before( &(rt_thread_priority_table[0]),&(rt_flag1_thread.tlist) );
rt_thread_startup(&rt_flag1_thread);
/* 初始化线程 */
rt_thread_init( &rt_flag2_thread, /* 线程控制块 */
"rt_flag2_thread", /* 线程名字,字符串形式 */
flag2_thread_entry, /* 线程入口地址 */
RT_NULL, /* 线程形参 */
&rt_flag2_thread_stack[0], /* 线程栈起始地址 */
sizeof(rt_flag2_thread_stack), /* 线程栈大小,单位为字节 */
3); /* 优先级 */
/* 将线程插入到就绪列表 */
//rt_list_insert_before( &(rt_thread_priority_table[1]),&(rt_flag2_thread.tlist) );
rt_thread_startup(&rt_flag2_thread);
/* 启动系统调度器 */
rt_system_scheduler_start();
}
/* 线程1 */
void flag1_thread_entry( void *p_arg )
{
for( ;; )
{
flag1 = 1;
rt_thread_delay(2);
flag1 = 0;
rt_thread_delay(2);
}
}
/* 线程2 */
void flag2_thread_entry( void *p_arg )
{
for( ;; )
{
flag2 = 1;
rt_thread_delay(2);
flag2 = 0;
rt_thread_delay(2);
}
}
void SysTick_Handler(void)
{
/* 进入中断 */
rt_interrupt_enter();
/* 更新时基 */
rt_tick_increase();
/* 离开中断 */
rt_interrupt_leave();
}
4.実験的現象
まとめ:この章では、優先度グループに対応する位置1までにスレッドが準備完了であることを示し、システムスケジューリング機能は、実行準備ができているスレッドの中で優先度が最も高いスレッドを選択して実行します。スレッドを一時停止すると、スレッドの状態が一時停止に変更され、優先グループ0の対応する位置に設定されます。一時停止時間が終了したら、相関ビットを1に設定して、スレッドの準備ができていることを示します(ただし、この章コードはスレッドの状態を一時停止に変更し、準備完了に戻されていないように見えますが、本のP103では、スレッドが優先順位テーブルから削除されているはずであると述べられています。削除後の再挿入は準備完了状態に設定され、スケジューラーの呼び出しを待ちます。この章の実験的な現象は前の章と同じですが、この章では、優先度が最も高いレディスレッドを最初に実行する点が異なります。スレッドが中断されている場合、優先度の低いスレッドが実行されます。
最後に、要約を作成します。ここで学習ポイントを要約します。この記事の知識のほとんどは、Wildfireによって発行された「STM32に基づくRTスレッドコアの実装とアプリケーション開発の戦闘」から得られます。 RT-Thread IoTオペレーティングシステムを使用している人は、それについて考えることができます。