關於嵌入式實時操作系統的實時性


嵌入式實時操作系統RTOS里實時的衡量指標到底是什么呢?1s肯定達不到實時,那需要多快呢?100ms,10ms,1ms,還是100us,10us?

還有這些指標是如何測量的呢?

一個關於1553B總線消息周期實時性指標的例子

一篇論文中關於1553B總線消息周期實時性的指標,從這個例子中可以看出,對於windows這種非實時操作系統而言,10ms的精度也很難保證。因此,實時性指標要求任務需要至少滿足10ms的指標,甚至更高。

韓春慧,王煜,黃書華,許權,張珅,魯月林. 基於BM3803的1553B總線通信軟件設計 [J]. 中國空間科學技術, 2019,39(234), 05 65-72.

論文中需要完成的1553總線測試終端的消息的周期數值偏差較為嚴格,

對於任務1廣播時間碼,周期為1s,周期偏差不能超過100us,

對於任務7系統同步,周期為2s,周期偏差不能超過10 000us=10ms,如下圖所示。

如果使用傳統的windows+1553B-PCI板卡方案的話,不能保證以上的精度;所以,論文使用了嵌入式實時操作系統的方案,BM3803+uCOS+61580,該系統可以滿足上圖的精度。

下表為實際測試結果,對於任務1,采用嵌入式實時操作系統方案,周期精確度偏差平均為8us,而采用windows方案則高達13ms,超過了100us=0.1ms的精度要求。

其它任務的周期精度要求均為10ms以內,對於嵌入式實時操作系統方案,周期精度平均1.5ms,而windows則為15ms,超過了精度要求。

論文

鏈接:https://pan.baidu.com/s/15P6VCZqdieAlSH9Mq8anmg
提取碼:o1vq

關於實時性都有哪些指標

expresslogic有一個文檔Measuring RTOS Real-Time Performance,其中描述了實時性的各種指標,最后,介紹了其RTOS實時性測量軟件。

https://rtos.com/wp-content/uploads/2017/10/EL_Measuring_RTOS_Real-Time_Performance.pdf

主要分為兩部分,

一是中斷處理實時性,主要包括以下步驟:

(1)中斷當前正在執行的任務,

(2)保存當前任務上下文,

(3)開始執行中斷服務程序ISR,

(4)ISR中進行一些處理,以確定需要采取的動作,

(5)保存一些中斷相關的關鍵數據,

(6)設置一些必須的輸出,

(7)確定該執行哪個任務(一般中斷到來之后,需要的處理會比較多,一般中斷中會處理必須的事情,剩下的處理由某個任務來處理)

(8)清除中斷狀態寄存器,

(9)將控制轉移到要執行的任務。

二是系統服務實時性,包括

(1)在某個事件發生時調度一個任務執行,

(2)任務之間傳遞消息(消息隊列),

(3)申明公共資源三方面(信號量等)。

 

TNKernel-RX/Thread-Metric/,某個操作系統使用了Thread Metric

源代碼:https://github.com/msalau/TNKernel-RX/tree/master/Thread-Metric

pdf鏈接:https://pan.baidu.com/s/1pJH2azMJb8QNmYZwXUQpFA
提取碼:t421

用戶程序需要做到什么

以上都是對於RTOS來說的,那么對於用戶程序需要做到什么呢?

RTOS內核里一般都會有一些關鍵區域critical section,在這些區域是需要關中斷的,中斷都屏蔽了,那么響應外界中斷自然會帶來延時,影響系統的實時性。

因此,對於用戶程序也可以這么說,用戶程序關中斷的時間不超過內核關中斷的時間,就能保證用戶程序不會使內核實時性變差。

這里有一個文檔Interrupts-II-Bringing Organization to our Code (the shared-data problem) Reference An Embedded Software Primer By David E. Simon,其中有描述內核關鍵區關中斷的時間會影響系統的實時性。

鏈接:https://pan.baidu.com/s/1M3f1rdl2DHsMMpLJ1Ptb0Q
提取碼:dbno

對於FreeRTOS而言,如何選擇保護關鍵區的方式 

https://forums.freertos.org/t/how-to-choose-a-critical-section-protecton-approach-taskenter-critical-or-vtasksuspendall-or-mutex/9500

How to choose a critical section protecton approach? taskENTER_CRITICAL or vTaskSuspendAll or mutex? 
Kernel


yanhc519
22h
The first question is how to choose between taskENTER_CRITICAL and vTaskSuspendAll?
I have read the manual and found that taskENTER_CRITICAL will disable interrupt while vTaskSuspendAll won’t disable interrupt but only suspend the scheduler. However, I still cannot understand the rational behind API xQueueGenericSend 2. In xQueueGenericSend, the code1 between line 761 and line 897 uses taskENTER_CRITICAL to protect itself, while the code2 between line 902 and line 941 uses vTaskSuspendAll to protect itself. What is the rational behind the protection approach chosen between the code1 and the code2?

The second question is how to choose between taskENTER_CRITICAL and mutex?
In this post, it said, "You should not be using taskENTER_CRITICAL to protect something that can take 6ms, let alone 80ms. A mutex is a much better option here, as that will only block the other tasks that try to use the file system. " As regarding to my platform, it is ARM Cotext-M4 processor running at 168 MHz and has a time slice as 1 ms. So, if the critical section lasts longer than 6*time slice, this is unacceptable. I think a reasonable critical section should last shorter than 1/n * time slice. So the rational behind how to choose between taskENTER_CRITICAL and mutex maybe that

If the critical section lasts shorter than 1/n * time slice then taskENTER_CRITICAL can be used.
If the critical section lasts longer than 1/n * time slice then mutex should be used.
Is what I am understood correct? If so, then which n should be chosen in practice?

Thanks in advance!

REPLY

created
22h
last reply
14h
2
replies
15
views
3
users
2
links


hs2
Hartmut Schaefer
20h
First when referring to line numbers of code you need to add the actual (FreeRTOS) version of that code :wink: But I can’t say much about the implementation of xQueueGenericSend anyway.
The concept of critical sections was recently very well explained by Richard Damon here 3 in the forum.
When using critical sections you must be aware that it affects your (worst case) interrupt response time. That means an ISR of a hardware interrupt might get delayed because all interrupts are disabled while executing code inside a critical section.
That’s the benefit of just suspending all tasks. Interrupts remain enabled and get handled by their corresponding ISRs.
There is no common rule how long ‘very shortis. It depends on the (real-time) requirements of your application.
Both protection methods are very light weight and fast, but affect the whole application i.e. all tasks regardless of their priorities.
In general you should narrow down the scope of any protection as much as possible. So when protecting the access to some data used by only 3 tasks out of 5 in your application, it’s almost always better to use a mutex used by these 3 tasks and all other tasks are not affected.
Also mutexes take the task priorities into account to decide which of the maybe blocked other tasks get’s the mutex next when it gets released by the task currently holding it.

SOLUTION

REPLY

richard-damon
14h
The basic characteristics that are used to determine between critical sections, suspending the scheduler and using a mutex are that critical sections are fast and cheap to perform, but have a wide impact as they disable interrupts. Critical sections affect interrupt latency, so I believe that one rule that FreeRTOS uses internally is that a critical section needs to have a strictly bounded execution time, and that time should be fairly short. I user code, since you know more about the requirements you may be able to relax some of these, but that is a good baseline. I personally limit critical sections to things that can be measured in small number of microseconds at most.

The distinction between stopping the scheduler and a mutex is perhas a bit more nuanced. Suspending the scheduler has wider impact, as it affects ALL tasks, and inside the section you can’t do anything that might block. It requires no ‘setup’ to create the locking agent like a mutex would. It doesn’t protect against ISR access (which is why when you suspend the schedule it sets a flag to tell the ISRs not to modify the main task lists). The disadvantage of suspending the scheduler is that it takes some time (vs just a couple of instructions for a critical section). FreeRTOS uses suspending the scheduler if the time period isn’t both strictly bounded and short.

A Mutex on the other hand only interacts with other tasks that use the same mutex, so has the least impact on the full system. I don’t think FreeRTOS uses muteness internally in the core, in part to avoid making them mandatory in a minimum configuration.
View Code

(1)taskENTER_CRITICAL,會關中斷,同時支持嵌套nesting,操作方便

(2)vTaskSuspendAll,不關中斷,會關調度器,操作復雜

(3)mutex,不關中斷,不關調度器

對於FreeRTOS內核來說,因為(3)Mutex是可選配置項,因此,內核是不能夠使用mutex的。

所以,對於FreeRTOS內核來說,只有選擇(1)和(2)。

而對於(1)和(2),按照richard-damon的回復,用(1)需要保證關鍵區執行時間在幾微秒以內(strictly bounded execution time,in small number of microseconds at most)。

而如果無法保證執行時間短且有界的,則需要使用(2)(FreeRTOS uses suspending the scheduler if the time period isn’t both strictly bounded and short.)。且在使用(2)時需要保證被保護的關鍵區不會阻塞(inside the section you can’t do anything that might block)。

 

關於這句話的進一步核實:when you suspend the schedule it sets a flag to tell the ISRs not to modify the main task lists

下面vTaskSuspendAll的時候,會對uxSchedulerSuspended+1。(這里的注釋比較有意思,就是因為變量的類型是基礎類型,因此不用設置關鍵區,為什么呢?因為是基礎類型,所以不用兩次move?但是++也會ldr,inc,str需要3條指令,也可能被打斷呢?

1 void vTaskSuspendAll( void )
2 {
3     /* A critical section is not required as the variable is of type 4  portBASE_TYPE. */
5     ++uxSchedulerSuspended;
6 }

uxSchedulerSuspended != 0的話,那么,上下文切換vTaskSwitchContext就不會執行。

1 void vTaskSwitchContext( void )
2 {
3     if( uxSchedulerSuspended != ( unsigned portBASE_TYPE ) pdFALSE )
4     {
5         /* The scheduler is currently suspended - do not allow a context
6         switch. */
7         xMissedYield = pdTRUE;
8     }
9     else

同時,注意到在xTaskResumeFromISR用到了這個調度器掛起變量,如果調度器掛起,那么,不能操作就緒任務列表(為什么呢?),因此,將任務放在即將就緒列表上。

 1 portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume )
 2     {
 3     portBASE_TYPE xYieldRequired = pdFALSE;
 4     tskTCB *pxTCB;
 5     unsigned portBASE_TYPE uxSavedInterruptStatus;
 6 
 7         configASSERT( pxTaskToResume );
 8 
 9         pxTCB = ( tskTCB * ) pxTaskToResume;
10 
11         uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
12         {
13             if( xTaskIsTaskSuspended( pxTCB ) == pdTRUE )
14             {
15                 traceTASK_RESUME_FROM_ISR( pxTCB );
16 
17                 if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE )
18                 {
19                     xYieldRequired = ( pxTCB->uxPriority >= pxCurrentTCB->uxPriority );
20                     uxListRemove(  &( pxTCB->xGenericListItem ) );
21                     prvAddTaskToReadyQueue( pxTCB );
22                 }
23                 else
24                 {
25                     /* We cannot access the delayed or ready lists, so will hold this 26  task pending until the scheduler is resumed, at which point a 27  yield will be performed if necessary. */
28                     vListInsertEnd( ( xList * ) &( xPendingReadyList ), &( pxTCB->xEventListItem ) );
29                 }
30             }
31         }
32         portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
33 
34         return xYieldRequired;
35     }

同時,在vTaskIncrementTick時,也會判斷調度器是否掛起,若掛起,則記錄丟失的tick數,在調取器繼續時,補償丟失的tick數。

 1 void vTaskIncrementTick( void )
 2 {
 3 tskTCB * pxTCB;
 4 
 5     /* Called by the portable layer each time a tick interrupt occurs.
 6     Increments the tick then checks to see if the new tick value will cause any
 7     tasks to be unblocked. */
 8     traceTASK_INCREMENT_TICK( xTickCount );
 9     if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE )
10     {
11         ++xTickCount;
12 
13     }
14     else
15     {
16         ++uxMissedTicks;
17 
18         /* The tick hook gets called at regular intervals, even if the
19         scheduler is locked. */
20         #if ( configUSE_TICK_HOOK == 1 )
21         {
22             vApplicationTickHook();
23         }
24         #endif
25     }
26 }

在xTaskResumeAll時,補償丟失的tick數。

 1 signed portBASE_TYPE xTaskResumeAll( void )
 2 {
 3 
 4     /* It is possible that an ISR caused a task(只有ISR會引起) to be removed from an event  5  list while the scheduler was suspended. If this was the case then the  6  removed task will have been added to the xPendingReadyList.  Once the
 7     scheduler has been resumed it is safe(?) to move all the pending ready
 8     tasks from this list into their appropriate ready list. */
 9     taskENTER_CRITICAL();
10     {
11         --uxSchedulerSuspended;
12 
13         if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE )
14         {
15             if( uxCurrentNumberOfTasks > ( unsigned portBASE_TYPE ) 0U )
16             {
17                 portBASE_TYPE xYieldRequired = pdFALSE;
18 
19                 /* If any ticks occurred while the scheduler was suspended then 20  they should be processed now. This ensures the tick count does not 21  slip, and that any delayed tasks are resumed at the correct time. */
22                 if( uxMissedTicks > ( unsigned portBASE_TYPE ) 0U )
23                 {
24                     while( uxMissedTicks > ( unsigned portBASE_TYPE ) 0U )
25                     {
26  vTaskIncrementTick(); 27                         --uxMissedTicks; 28                     }
29                 }
31             }
32         }
33     }
34     taskEXIT_CRITICAL();
35 
36     return xAlreadyYielded;
37 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM