UCOSII System Concepts - ianchen0119/AwesomeCS GitHub Wiki
Concept
Multitasking
Schedular 會切換 CPU 的注意力給多個任務:
- Task 為 logically concurrent(透過來回切換處理多個任務做到的 concurrency)
Task
- Tasks, threads, processes are used interchangeably
- Task 由以下幾項組成:
- priority
- register set,用於存放每一個 register 的狀態
- 獨立的 stack 空間
- TCB 或是類似的 housekeeping information
Task State
uC/OS-2 定義了五個 task 狀態:
1. Dormant(休眠)
當 OSTaskDel()
被呼叫,Task 的狀態會從 Waiting 進入到 Dormant,這裡可以理解成任務已經不再需要被排程,但是相關的 memory space 被保留下來供之後新建立的任務使用。
2. Ready(準備完成)
當 OSTaskCreate()
、OSTaskCreateExt()
被呼叫,Task 會進入 Ready 狀態(也就是還沒被分配到 CPU 執行,但是可被排程)。
OSTaskCreateExt()
為OSTaskCreate()
的擴展函式,可以做更細節的參數設定。
3. Running(執行中)
當 OSStart()
、OSIntExit()
與 OS_TASK_SW()
被呼叫時,Task 會被分配到 CPU 時間,而這三個時機點分別會出現在:
OSStart()
出現在 OS 完成 Init work 並開始處理 User Space APP 時。OSIntExit()
用於離開中斷,UCOSII 使用 Timer Interrupt 去檢測 Task 是否需要 preempted,如果檢測後 scheduler 認為這個 task 可以繼續執行,那麼OSIntExit()
會讓 CPU 回復到 Task 進入 ISR 之前的狀態繼續做原本的工作。OS_TASK_SW()
出現在當 Scheduler 認為 interrupted task 要被替換時,OS_TASK_SW()
會載入 scheduler 指定的 task 的 context 並開始執行。
4. Waiting(等待)
一般來說,Task 會因為無法取得它們想要的資源進入 Waiting 狀態,這些資源可能是 Semaphore
、Message Box
,或是因為 Task 呼叫了 OSTimeDly()
、OSTaskSuspend()
。
5. Interrupted(被中斷)
Context Switch
- Context Switch 會帶來額外的 time overheads 密集的 Context-switch 應該要被避免,因為現代處理器普遍有很深的 pipeline 以及很大的 register files,如果頻繁的切換 context 會讓 pipeline 裡面的內容反覆的被清除。
- The overhead of a context switch is part of the specification of an RTOS
Kernels
- Kernel 負責:
- 任務管理
- Inter-task communication
- Kernel 會帶來額外的 time/space overheads:
- Kernel 服務時需要花費時間(Semaphores, message queues, mailboxes, timing controls, and etc...)。
- Kernel 存放在 RAM 或是/以及 ROM 上面。
Schedulers
- Scheduler 是 kernel 的一部分,它負責選擇下一個分配到 CPU 時間的任務:
- 又分為 Preemptive 以及 non-preemptive scheduling
- Priority-driven 或是 deadline-driven
- 對於 Priority-driven 的排程演算法,擁有最高優先權的任務將會持續的取得 CPU 資源。
Non-Preemptive Kernels
- Context-switch 只會在任務主動讓出 CPU 資源時發生。
- Interrupts 會被 ISR 處理,而 ISR 處理完畢後一定會回到被中斷的任務繼續處理。
- 因為上述特性,這樣的排程方式不會有 task 與 task 之間的資源競爭(因為大家都是用到開心才會交出殺生大權)。
- 但是 ISR 與 Task 可能會彼此競爭資源。
- 優點:簡單並且可控制。
- 缺點:schedulability 很差。
- A task is executing but gets interrupted.
- If interrupts are enabled, the CPU vectors (i.e. jumps) to the ISR.
- The ISR handles the event and makes a higher priority task ready-to- run.
- Upon completion of the ISR, a Return From Interrupt instruction is executed and the CPU returns to the interrupted task.
- The task code resumes at the instruction following the interrupted instruction.
- When the task code completes, it calls a service provided by the kernel to relinquish the CPU to another task.
- The new higher priority task, then executes to handle the event signaled by the ISR.
Preemptive Kernels
- Preemptive kernel 具有更靈敏的反應
- A ready, high-priority task gains control of the CPU instantly
- ISR 處理完畢後不一定會返回到 interrupted task。
- task-task 與 task-ISR race 都存在。
- A task is executing but interrupted.
- If interrupts are enabled, the CPU vectors (jumps) to the ISR.
- The ISR handles the event and makes a higher priority task ready to run. Upon completion of the ISR, a service provided by the kernel is invoked. (i.e., a function that the kernel provides is called).
- This function knows that a more important task has been made ready to run, and thus, instead of returning to the interrupted task, the kernel performs a context switch and executes the code of the more important task. When the more important task is done, another function that the kernel provides is called to put the task to sleep waiting for the event (i.e., the ISR) to occur.
- The kernel then sees that a lower priority task needs to execute, and another context switch is done to resume execution of the interrupted task.
Interrupts
- 一般來說,Interrupt 是用來通知 CPU 的 hardware asynchronous event:
- clock tick(時間中斷)
- I/O events(可能是 Keyboard、USB Device 或是網卡等等)
- hardware errors
- 進入 ISR 之前需要先保存 Interrupted task 的 context。
- ISR 用於處理這些 event,並且在處理完畢後回到:
- Interrupted task (in non-preemptive kernel)
- The highest-priority ready task (in preemptive kernel)
Nested Interrupts
已在前面的文章探討過,不詳述:
Interrupt Latency
interrupt latency =
the time length of interrupt disabling +
Time to start executing the first instruction in the ISR
ISR Processing Time
- 在多數狀態下,ISR 必須要:
- 儲存當前 Task 的 context。
- 識別 Interrupt 的種類。
- 獲得 Interrupting device 的資訊。
- 恢復 task execution。
- ISR 應該越短越好:
- ISR 會帶來非同步的、額外的 time overhead。
- 不要在 ISR 做 big job,應該讓 worker task 去做 follow-ups。
Clock Tick
- 定期的 hardware event(由 timer 引發的中斷)。
- 可以看成一個系統的心跳。
- 更高的 tick rate 會帶來:
- 更高的靈敏度。
- 更高的 ctxsw 頻率。
- UCOSII 提供
OSTimeDly()
讓一個 Task 等待 n 個 ticks 後回到 ready 狀態。
Memory Requirements
- RAM usage = code segment + data (global variables) + stack
- 我們必須確保 Task 有足夠的空間:
- 將 Large arrays 以及 structures 設置成 local variables。
- Recursive function calls 會佔用大量的 stack 空間,如果 overflow 會產生無法預期的執行結果。
- ISR nesting 也會消耗大量的 stack 空間。
- Function calls 帶有太多 arguments:如果暫存器不夠放,剩下的參數會放在 stack 上。