高給の秘訣は、AliOS Thingsに従って、オペレーティングシステムを簡単に開始することです。セマフォ

セマフォは、オペレーティングシステムで非常に一般的に使用される同期メカニズムです。この記事では、AliOS Thingsのコアソースコードを分析することにより、セマフォメカニズムについて学習します。

AliOSThingsのセマフォのソースパスは次のとおりです

セマフォのソースコードの場所:core / rhino / k_sem.c

セマフォヘッダーファイルの場所:core / rhino / include / k_sem.h

 

1.セマフォ構造ksem_t

k_sem.hヘッダーファイルは、セマフォ構造ksem_tを定義します。セマフォ関連の機能はこの構造に基づいているため、最初に構造を分析します。具体的な定義は次のとおりです。

 

typedef struct sem_s {

  blk_obj_t blk_obj;

  sem_count_tカウント;

  sem_count_t peak_count;

#if(RHINO_CONFIG_KOBJ_LIST> 0)

  klist_t sem_item; / ** <統計用のkobjリスト* /

#endif

  uint8_t mm_alloc_flag; / ** <内部mallocまたは呼び出し元入力からのバッファー* /

} ksem_t;

 

メンバーの説明:

(1)blk_objカーネルの基本構造であり、カーネル構造の基本情報を管理するために使用されます。オブジェクト指向の観点からは、ksem_tの親クラスと同等です。その主なドメインは次のとおりです。blk_listブロッキングキュー、nameオブジェクト名、blk_policyブロッキングキュー待機戦略(主に優先度(PRI)および先入れ先出し(FIO))、obj_type構造タイプ。

(2)countはシグナルの数を記録します。

(3)peak_countは、システム動作中のセマフォの信号の最大数を記録する統計値です。

(4)sem_itemはリンクリストノードであり、主にデバッグと統計のために、セマフォをグローバルリンクリストに挿入するために使用されます。

(5)mm_alloc_flagは、構造体のメモリが静的に割り当てられているか動的に割り当てられているかを示すために使用されるメモリフラグです。

 

2.セマフォ関数sem_createを作成します

セマフォを作成するためのコア関数はsem_createであり、そのプロトタイプは次のとおりです。

static kstat_t sem_create(ksem_t * sem、const name_t * name、sem_count_t count、

                         uint8_t mm_alloc_flag);

パラメータの意味:

sem:セマフォ構造体ポインター。

name:セマフォの名前。ユーザーは、デバッグと区別を容易にするために、セマフォの名前を指定できます。

count:初期信号の数。

mm_alloc_flag:メモリタイプ。つまり、semが指すメモリが静的に割り当てられているか動的に割り当てられているか。動的に割り当てられる場合は、セマフォが削除されたときに、semが指す構造体メモリを解放する必要があります。

 

この関数では:

(1)最初にステートメントCPSR_ALLOC()を使用して、クリティカルセクションステートメントRHINO_CRITICAL_ENTER()/ RHINO_CRITICAL_EXIT()によって使用される内部変数を定義します。シングルコアでは、クリティカルセクションはオフ割り込み保護を使用するため、CPSR_ALLOC()は実際には割り込み状態を保存する変数を定義します。RHINO_CRITICAL_ENTER()は、現在の割り込みステータスを読み取って保存してから、割り込みを閉じます。RHINO_CRITICAL_EXIT()は、中断された状態を復元します。したがって、次のようにクリティカルセクションに出入りします。

CPSR_ALLOC();

RHINO_CRITICAL_ENTER(); //クリティカルセクションに入る

……//クリティカルセクション

RHINO_CRITICAL_EXIT(); //クリティカルセクションを終了します

 

sem_create関数では、グローバルリンクリストへのアクセスを保護するために使用されます。

klist_insert(&(g_kobj_list.sem_head)、&sem-> sem_item); //セマフォ構造をグローバルリンクリストに挿入します

(2)NULL_PARA_CHK()マクロは、null以外のポインターをチェックするために使用されます。渡されたsemまたはnameポインタがNULLであることが判明した場合、直接返されます。

(3)次に、セマフォ構造を初期化します。ブロッキング戦略はBLK_POLICY_PRIに初期化されます。つまり、セマフォで複数のタスクがブロックされると、優先度の高いタスクが最初にセマフォを取得します。もう1つの戦略は、BLK_POLICY_FIFOです。つまり、最初にブロックされたタスクが最初にセマフォを取得します。ksem_tの初期化されたタイプはRHINO_SEM_OBJ_TYPEです。

 

関数krhino_sem_create()およびkrhino_sem_dyn_create()は、セマフォを作成するための外部インターフェイスです。2つの違いは、前者が静的作成(K_OBJ_STATIC_ALLOC)である、つまり、ksem_t構造のメモリが外部からインポートされることです。後者は動的に作成されます(K_OBJ_DYN_ALLOC)。この関数はkrhino_mm_allocを呼び出して、ksem_t構造体のメモリを動的に割り当て、作成された構造体オブジェクトを入力パラメーターsemを介して呼び出し元に渡すため、入力パラメーターsemのタイプはksem_t *です。 *。

 

セマフォ作成関数のソースコードを分析すると、セマフォの最初の特性を取得できます。つまり、セマフォを作成するときに初期セマフォの数を指定できます。

 

3.セマフォ関数krhino_sem_takeを要求します

関数krhino_sem_takeは、セマフォを要求するために使用されます。関数プロトタイプは次のとおりです。

kstat_t krhino_sem_take(ksem_t * sem、tick_tティック);

パラメータの説明:

(1)semは、セマフォ構造へのポインターを指します。

(2)ティックブロッキング時間。セマフォがない場合、タスクは最大でティックシステムクロックをブロックします。2つの特別な値は次のとおりです:(a)RHINO_NO_WAIT、セマフォがない場合は直接返されます;(2)RHINO_WAIT_FOREVERは、セマフォが取得されるまでタスクをブロックします。

 

この関数では:

(1)NULL_PARA_CHK(sem);入力パラメーターsemがNULLかどうかを確認し、NULLの場合は、関数を直接終了します。

(2)RHINO_CRITICAL_ENTER();複数のタスクが同時にsem構造にアクセスする可能性があるため、クリティカルセクションに入るのに使用されます。クリティカルセクションの保護が必要です。

(3)cpu_cur_get()を呼び出して、マルチコアアーキテクチャをサポートする現在のコア番号を取得します。シングルコアプロセッサでは、この関数は0を返します。

(4)TASK_CANCEL_CHK(sem)は、現在のタスクが終了したかどうかを確認するために使用されます。INTRPT_NESTED_LEVEL_CHK()は、割り込みコンテキストにあるかどうかを確認するために使用されます。割り込み処理関数をブロックすることは許可されていないため、krhino_sem_take()を呼び出すことはできません。

(5)条件付き判定ステートメントif(sem-> blk_obj.obj_type!= RHINO_SEM_OBJ_TYPE)を使用して、semタイプがRHINO_SEM_OBJ_TYPEであるかどうかを確認します。これにより、セマフォ構造が削除された後の誤用を防ぐことができます。

(6)ここで、セマフォを申請するロジックを正式に入力します。sem-> countが0より大きい場合は、セマフォがあることを意味します。その後、sem-> countが1減少し、アプリケーションは成功します。もちろん、ステートメントRHINO_CRITICAL_EXIT();を使用して、戻る前にクリティカルセクションを終了します。

(7)現在セマフォがなく、待機時間がRHINO_NO_WAITの場合、直接戻り、アプリケーションは失敗します。

(8)残りの状況は次のとおりです。現在セマフォがなく、発信者はタイムアウトするかセマフォを取得するまで一定期間待機したいと考えています。条件付き判定g_sched_lock [cur_cpu_num]> 0uは、スケジューリングが現在許可されているかどうかを確認するために使用されます。後続のコードは現在のタスクを一時停止し、他のタスクにスケジュールするためです。したがって、g_sched_lock [cur_cpu_num]> 0u(スケジューリングは許可されていません)の場合、次のコードは実行できず、クリティカルセクションは直接終了して返されます。

(9)文pend_to_blk_objは、現在のタスクを準備完了状態に設定します。

(10)ステートメントRHINO_CRITICAL_EXIT_SCHED();は、クリティカルセクションを終了し、スケジューリングをトリガーします。ここでは、タイムアウトまたはタスクがセマフォを解放して現在のタスクをウェイクアップするまで、他のタスクに切り替えます。

(11)文pend_state_end_procは、主に覚醒の理由を判別するために、覚醒後の処理に使用されます:セマフォが取得される、タイムアウトが期限切れになる、またはセマフォが削除されるなど。krhino_sem_takeが呼び出される場所は、戻り値に基づいて後続の操作を決定できます。

 

4.セマフォ関数sem_giveを解放します

このインターフェースは、送信セマフォとも呼ばれます。関数プロトタイプは次のとおりです。

static kstat_t sem_give(ksem_t * sem、uint8_t opt_wake_all)

パラメータの説明:

(1)semは、セマフォ構造へのポインターを指します。

(2)opt_wake_allは、待機中のタスクをウェイクアップする(WAKE_ONE_SEM)か、すべてのタスクをウェイクアップします(WAKE_ALL_SEM)。

 

この関数では:

(1)RHINO_CRITICAL_ENTER();複数のタスクが同時にsem構造にアクセスする可能性があるため、クリティカルセクションを入力するために使用されます。クリティカルセクションの保護が必要です。

(2)条件付き判定ステートメントif(sem-> blk_obj.obj_type!= RHINO_SEM_OBJ_TYPE)を使用して、semタイプがRHINO_SEM_OBJ_TYPEであるかどうかを確認します。これにより、セマフォ構造が削除された後の誤用を防ぐことができます。

(3)関数cpu_cur_get()は、マルチコアアーキテクチャをサポートするための現在のコア番号を取得するために使用されます。シングルコアプロセッサでは、この関数は0を返します。

(4)条件ステートメントif(is_klist_empty(blk_list_head))を使用して、ブロッキングキューが空であるかどうか、つまり、セマフォで現在ブロックされているタスクがないかどうかを判別します。この場合、処理は比較的単純で、セマフォの数に1を追加するだけです。

最初にエラーチェックを行います。sem-> count ==(sem_count_t)-1の場合、システムに問題があります。問題がない場合は、セマフォの数を1つ増やします。

sem-> count ++;

sem-> countがsem-> peak_countより大きい場合、sem-> peak_countが更新されて履歴内のセマフォの最大数が記録されます。

(5)セマフォを待機しているタスクがある場合、sem-> countに1を追加する必要はありません。タスクを直接ウェイクアップして、セマフォを消費するだけです。2つの状況があります。opt_wake_allが0(WAKE_ALL_SEM)でない場合は、pend_task_wakeupを呼び出してすべてのタスクをウェイクアップします。それ以外の場合は、1つのタスクのみが起動されます。

 

外部インターフェース関数krhino_sem_give / krhino_sem_give_allはすべてsem_giveを呼び出してセマフォを解放します。2つの違いは、前者は1つのブロックされたタスクのみをウェイクアップし、後者はすべてのブロックされたタスクをウェイクアップすることです。

 

セマフォリリース関数のソースコードを分析すると、セマフォの他の2つの特性を取得できます。

2番目の機能:セマフォの要求と解放をペアで表示する必要はなく、セマフォを取得せずにセマフォを解放できます。そのため、割り込み処理機能は、割り込みとタスク間の同期でよく使用されるセマフォを解放できます。

3番目の機能:セマフォでブロックされたすべてのタスクをウェイクアップできます。

 

5.セマフォ関数krhino_sem_del / krhino_sem_dyn_delを削除します

krhino_sem_dyn_delは、krhino_sem_dyn_createによって作成されたセマフォを削除するために使用されます。krhino_sem_delは、krhino_sem_createによって作成されたセマフォを削除するために使用されます。これらの2つの機能セットを一緒に使用する必要があります。そうしないと、重大な問題が発生します。

krhino_sem_delと比較して、krhino_sem_dyn_delには、セマフォ構造を解放するためのもう1つのステップがあります。ここでは、krhino_sem_dyn_del関数を分析します。

(1)関数エントリは、主にいくつかの正確性チェックを行うために、前の関数処理と同様です。

(2)sem-> blk_obj.obj_type = RHINO_OBJ_TYPE_NONEは、構造のタイプをNONEに設定します。このステートメントの利点は、リリース後にセマフォが誤用された場合に検出できることです。

(3)loopステートメントは、pend_task_rm関数を呼び出して、セマフォでブロックされているすべてのタスクをウェイクアップします。この手順がないと、セマフォが削除されると、セマフォを待機しているタスクがウェイクアップされることはありません。

(4)ステートメントklist_rm(&sem-> sem_item);は、グローバルg_kobj_list.sem_headリンクリストからセマフォ構造を削除するために使用されます。

(5)最後に、クリティカルセクションを終了し、krhino_mm_freeを呼び出して、セマフォ構造のメモリスペースを解放します。

 

セマフォ削除機能のソースコードを分析すると、セマフォの使用に関するメモが表示されます。

krhino_sem_take()を呼び出すタスクがブロックされている場合、セマフォが削除されるとタスクがウェイクアップされるため、krhino_sem_take()が戻っても、セマフォを取得する必要があるわけではありません。戻り値がRHINO_SUCCESSかどうかを判断する必要があります。

 

6.例

この例では、タスク2は1秒ごとにセマフォを送信します。タスク1はセマフォを要求し、セマフォを受信した後に1行を印刷します。

/* 定义信号量结构体*/

ksem_t sem_test;  



/* 定义任务相关资源*/

ktask_t     test_task1_tcb;

cpu_stack_t test_task1_stack[TEST_TASK_STACKSIZE];

ktask_t     test_task2_tcb;

cpu_stack_t test_task2_stack[TEST_TASK_STACKSIZE];



/* 前向声明任务入口函数*/

static void test_task1(void *arg);

static void test_task2(void *arg);



/* 主入口 */

int application_start(int argc, char *argv[])

{

   /* 静态创建信号量,初始个数为0 */

   krhino_sem_create(&sem_test, "sem_test", 0);

   /* 创建两个测试任务 */

   krhino_task_create(&test_task1_tcb, TEST_TASK1_NAME, 0, TEST_TASK1_PRI, 50,

                      test_task1_stack, TEST_TASK_STACKSIZE, test_task1, 0);

   krhino_task_create(&test_task2_tcb, TEST_TASK2_NAME, 0, TEST_TASK2_PRI, 50,

                      test_task2_stack, TEST_TASK_STACKSIZE, test_task2, 0);



}



/* 任务1的入口 */

static void test_task1_entry(void *arg)

{

   kstat_t stat;

   while (1) {

       /* 请求信号量*/

       stat = krhino_sem_take(&sem_test, RHINO_WAIT_FOREVER);

       if (stat == RHINO_SUCCESS) {

           printf("revc sem\r\n");

       }

   }

}



/* 任务2的入口 */

static void test_task2(void *arg) {

   while(1) {

       /* 睡眠1s */

       aos_msleep(1000);

       /* 释放信号量*/

       krhino_sem_give(&sem_test);

   }

}

 

7.まとめ

この記事では、AliOS Thingsセマフォのソースコードを分析し、3つの特徴と1つの使用上の注意を要約します。krhino_sem_count_getとkrhino_sem_count_setの2つのインターフェースは分析されないままです。上記の分析基準を使用すると、読者は自分で分析を試みることができます。また、この記事をAliOS Thingsカーネルソースコードを読み始める機会として利用し、貢献を歓迎することを願っています。

 

8.開発者のテクニカルサポート

さらにテクニカルサポートが必要な場合は、DingTalk開発者グループに参加するか、WeChatパブリックアカウントをフォローしてください。

テクノロジーとソリューションの詳細については、AliyunAIoTホームページhttps://iot.aliyun.com/をご覧ください。

おすすめ

転載: blog.csdn.net/HaaSTech/article/details/113919776