osタスクスケジューリング実現原理

4887 ワード

文書ディレクトリ

  • なぜタスクスケジューリングを行うのか-why
  • タスクスケジューリングには何が必要ですか?-what
  • タスクスケジューリングはどのように実現しますか?-how

  • なぜタスクスケジュールを?


    オペレーティングシステムの中で最も顕著な特性はタスクスケジューリングであり、タスクスケジューリングは主に以下のいくつかのニーズから来ている.
  • プログラム同時(multiprogram)
  • タスク間同期、メッセージング
  • リアルタイム性能要求
  • のうちの第1のプログラムの同時性はよく理解され、一般的な意味のシングルコアハードウェアプラットフォームにとって、いかなる特定の時間にも実際には1つの機械命令しか実行できない(実際には現代のcpuに対して正確ではなく、例えばpipelineなどのハードウェア技術は実際にシングルコアcpuにある程度の並列命令を実行させることができる)ため、タスクスケジューリングを実現してこそマルチタスクの「一斉並行」の効果を実現することができる.各種タスクスケジューリングアルゴリズムは実際には,各タスクにユーザモードでcpuを独占させる「仮想」であり,cpuハードウェアの時間次元での抽象である.このようなタスクスケジューリングは一般的にタイムスライス形式として表現される.
  • タスク間の実行順序とタイミングは、2つのプロセスAB、AがBにイベントを書き込み、Bがイベントを読み出すなど、一定の論理規則に従って行う必要がある場合がある.論理的には、Aがイベントを書き込み済みである場合にのみ、Bは時間を読み取り、操作を実行することができるため、ジョブ呼び出し
  • を読み書きイベントの実装において明示的に実行する必要がある.
  • RTOSでは、リアルタイム性が要求されるため、外部イベントが到着すると、優先度に応じて即座に応答し、異なる外部イベントは一般に異なるタスクに対応するため、低優先度タスクの実行中に外部高優先度イベントが到着すると、直ちにタスクスケジューリング
  • を行う必要がある.

    タスクのスケジュールには何が必要ですか?what


    タスクスケジューリングが実際に行うのは、2つのタスクのコンテキスト切替(context)を実現することであり、あるいは、ある時点であるタスクのすべてのステータス情報を理解することができ、タスクスケジューリングが実際に行うのは、現在のタスクのステータスを保存し、切替するタスクのステータスを復元することであり、一般的に、1つのタスクのコンテキストには主に以下の部分がある.
  • stack、関数呼び出しスタック、ローカル変数など
  • heapは、動的に申請されたメモリであり、一般的にheap中のメモリがローカルポインタ変数によって指向されるため、実際のheapの情報もstackが
  • の保存に共同で参加する必要がある.
  • registerが具体的に実行する場合、関数中の各種変数間の演算、付与は、実際にはレジスタによって直接実現または中継され、タスク実行中に重要なレジスタの情報を中断すると保存する必要があり、一般的に通用する保存方法はレジスタをスタック中の
  • に最後に押し込むことである.
  • システム状態システム状態は、特定のハードウェアプラットフォームおよびos kernel実装に関連する、例えば、いくつかの特殊レジスタ、またはグローバルタスク制御ブロック(TCB)内の特定の情報などの
  • .
    タスクコンテキストに関する操作は、spポインタの切り替えなど、ハードウェアの特権モードで特定の命令を使用して実行する必要があるため、osは一般的にタスクスケジューリングのハンドルでアセンブリを使用して直接処理する.

    タスクのスケジュールをどのように実現しますか?-how


    異なるosのタスクスケジューリングの実現構想は基本的に一致しており、ここでは優れたタスク優先度プリエンプトをサポートするLiteosソースコードの例を用いて説明し、Liteosソースコードオープンソースはgithub上でダウンロードすることができる.
    タスクのスケジューリングを開始すると、主に4つのステップに分けられます.
  • 次のスケジュールが必要なタスク
  • を選択
    /* Find the highest task */
        g_stLosTask.pstNewTask = LOS_DL_LIST_ENTRY(osPriqueueTop(), LOS_TASK_CB, stPendList);
    
        /* In case that running is not highest then reschedule */
        if (g_stLosTask.pstRunTask != g_stLosTask.pstNewTask) {
        // do real schedual
        osTaskSchedule();
        //....
        }
    

    Liteosはタスクプリエンプトをサポートし、タスクキュー内の最も優先度の高いタスクを切り替え対象とします.
  • 特権モード及び特権モードハンドル
  • に入る.
    osTaskSchedule:
        LDR     R0, =OS_NVIC_INT_CTRL
        LDR     R1, =OS_NVIC_PENDSVSET
        STR     R1, [R0]
        BX      LR
    

    osTaskScheduleはアセンブリに入り始め、特権モードに入る方法は特定のハードウェアプラットフォームに依存し、ここでarm-cotex-Mアーキテクチャを例に、NVIC_INT_CTRLレジスタセット、pendSVハンドルへの特定の割り込みをトリガー
  • 現在のタスクコンテキスト
  • を保存
    TaskSwitch:
        /**
         * R0 = now stack pointer of the current running task.
         */
        MRS     R0, PSP
    
    
        STMFD   R0!, {R4-R12}          /* save the core registers and PRIMASK. */
    
        LDR     R5, =g_stLosTask
        MOV     R8, #OS_TASK_STATUS_RUNNING
    
        /**
         * Save the stack pointer of the current running task to TCB.
         * (g_stLosTask.pstRunTask->pStackPointer = R0)
         */
        LDR     R6, [R5]
        STR     R0, [R6]
    
        /**
         * Clear the RUNNING state of the current running task.
         * (g_stLosTask.pstRunTask->usTaskStatus &= ~OS_TASK_STATUS_RUNNING)
         */
        LDRH    R7, [R6, #4]
        BIC     R7, R7, R8
        STRH    R7, [R6, #4]
    
        /**
         * Switch the current running task to the next running task.
         * (g_stLosTask.pstRunTask = g_stLosTask.pstNewTask)
         */
        LDR     R0, [R5, #4]
        STR     R0, [R5]
    

    上は現在のタスクcontexを保存するコアコードであり、MRS R0, PSP命令は現在のタスクspポインタを取得し、次のSTMFD命令によりr 4~r 12を現在のタスクスタックに保存し、次にグローバルosタスク制御ブロック情報を修正し、現在のタスク状態をrunningからreadyに切り替え、現在のタスク制御ブロックを次のタスクに切り替える
    注意arm-mアーキテクチャでは、割り込みに入ると、ハードウェアはr 0~r 3,r 12,Lr,pc,xpsrの8つのコアレジスタを自動的にタスクスタックに押し込み、割り込みから戻っても自動的にスタックに対応します
  • 次のタスクコンテキストを復元し、
  • の実行を続行します.
        /**
         * Set the RUNNING state of the next running task.
         * (g_stLosTask.pstNewTask->usTaskStatus |= OS_TASK_STATUS_RUNNING)
         */
        LDRH    R7, [R0, #4]
        ORR     R7, R7, R8
        STRH    R7, [R0, #4]
    
        /**
         * Restore the stack pointer of the next running task from TCB.
         * (R1 = g_stLosTask.pstNewTask->pStackPointer)
         */
        LDR     R1, [R0]
    
     
        LDMFD   R1!, {R4-R12}          /* restore the core registers and PRIMASK. */
    
        /**
         * Set the stack pointer of the next running task to PSP.
         */
        MSR     PSP, R1
    
        /**
         * Restore the interruption state of the next running task.
         */
        MSR     PRIMASK, R12
        BX      LR
    

    古いタスクフィールドの保存と同様に、最後にMSR PSP, R1命令を使用してタスクスタックポインタspを新しいタスクのスタックトップに切り、ジャンプ命令BXを使用して割り込みから戻り、一旦戻ると、すべてのフィールド情報が新しいタスクの前回割り込みスケジューリングの状態に戻り、PCポインタを含むと、次のマシンサイクルは新しいタスクの前回割り込みの場所から実行され続け、タスクスケジューリングが実現される
    【REF】
    [ref 1]armアセンブリ命令セットマニュアル[ref 2]インラインアセンブリとアセンブリの2つの構文仕様(ATT/intel)