女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

RT-Thread記錄(五、RT-Thread 臨界區保護)

矜辰所致 ? 來源:矜辰所致 ? 作者:矜辰所致 ? 2022-06-20 16:06 ? 次閱讀

前言

為什么要聊臨界區?

因為在 RT-Thread 中臨界區關系到線程的順序執行,也就是線程同步的問題。

在使用RTOS的時候,多個運行的線程往往都需要訪問臨界資源,比如一些全局變量,那么如果不進行一定的保護措施,程序運行就可能出現意想不到的結果。

RT-Thread 提供了多種途徑來保護臨界區,本文主要說明的是:關閉系統調度和禁止中斷的方式 。

本 RT-Thread 專欄記錄的開發環境:
RT-Thread記錄(一、RT-Thread 版本、RT-Thread Studio開發環境 及 配合CubeMX開發快速上手)
RT-Thread記錄(二、RT-Thread內核啟動流程 — 啟動文件和源碼分析
RT-Thread 內核篇系列博文鏈接:
RT-Thread記錄(三、RT-Thread 線程操作函數及線程管理與FreeRTOS的比較)
RT-Thread記錄(四、RT-Thread 時鐘節拍和軟件定時器

一、臨界區

經常會聽到臨界區,臨界資源之類的名詞,那么什么叫臨界區,臨界資源?

1.1 什么是臨界區

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

簡單的概括就是圖中兩句話:

  • 臨界資源
    一次僅允許一個進程使用的共享資源
  • 臨界區
    每個進程中訪問臨界資源的那段代碼稱為 臨界區

1.2 RTOS中的臨界區

對于我們的多任務的RTOS而言,除了外部中斷,自身的多線程和系統調度機制,多個線程可能會對共享資源進行訪問,為了保證數據的可靠性和完整性,那么就需要對臨界區進行保護,共享資源要互斥的訪問(比如全局變量)。

首先是最基礎的示例,外部中斷!這個不僅在RTOS存在,前后臺系統也存在:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

上面的例子中,如果在線程函數中,加入臨界區保護,使得線程對臨界資源 a 的操作沒有結束以前不響應中斷,就不會發生問題。

再來看一個線程間對臨界資源訪問的例子:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

在上圖的示例中 (可能delay(1)和時鐘節拍一樣可能有點問題,可能需要多一點延時,這里意思到了就行,不糾結了= =!),我已經分析了如果沒有臨界區保護會出現的問題(有問題請指出),實際程序結果可能不會是程序本來想要的結果,這種錯誤是需要避免的!

本小結以下內容包括后面臨界區的保護源碼分析是擴展說明,懂與不懂不影響學會使用 RT-Thread 臨界區保護,因為涉及的 RTOS的調度原理,PendSV異常等知識,需要一定的基礎,這里建議想學習RTOS的小伙伴務必好好看看《Cortex-M3與Cortex-M4權威指南》這個文檔。

理解上面示例關系到RTOS的調度原理,上面解釋中用到的中斷打斷線程后現場保存,現場恢復,線程調度。得對RTOS的調度原理有一定的理解,在RTOS中除了外部中斷會打斷線程的執行,還有Systick中斷和一個重要的 PendSV 異常。

PendSV 也稱為可懸起的系統調用,它是一種異常,可以像普通的中斷一樣被掛起,它是專門用來輔助操作系統進行上下文切換的。PendSV 異常會被初始化為最低優先級的異常。每次需要進行上下文切換的時候,會手動觸發 PendSV 異常,在 PendSV 異常處理函數中進行上下文切換。

詳細理解請參考我另一篇博文:
FreeRTOS記錄(三、RTOS任務調度原理解析_Systick、PendSV、SVC)

這里用文中截圖稍微解釋一下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

總之,對于RTOS而言,在訪問臨界資源的時候,需要特別注意,做好臨界區的保護。

為了避免出現上面我們所說的問題,RTOS對臨界區采取了一些對應的保護方法,一般來說有:
關閉系統調度,關中斷,利用信號量,互斥量。

RT-Thread 信號量,互斥量我們會在下篇博文來說明,本文主要來了解下關閉中斷和系統調度的操作。

二、RT-Thread臨界區保護

2.1 禁止調度

RT-Thread 調度器上鎖 和 調度器解鎖的函數如下:

void rt_enter_critical(void);//調度器上鎖,進入調度臨界區,不再切換線程
void rt_exit_critical(void);//調度器解鎖,退出調度臨界區

注意,調度鎖不會阻止系統的響應中斷,只不過是中斷處理完成退出后,繼續執行被鎖住的線程。如果中斷中有訪問臨界資源的情況,此方式不適用!!

調度器上鎖和調度器解鎖函數,是成對使用的,切記!

使用示例:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16

禁止調度源碼簡析

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

但是上面的函數只對rt_scheduler_lock_nest變量進行了自增,并沒有別的操作,那么這個變量是如何影響調度器的呢?
我們查到使用到變量rt_scheduler_lock_nest的地方,找到如下代碼:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

那么同樣的,在rt_exit_critical函數中,當然就是變量自減了:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_17,color_FFFFFF,t_70,g_se,x_16

仔細看了這段代碼還能發現一個細節,就是這個關閉調度和打開調度是支持嵌套的! 調度器上鎖一次,就要解鎖一次,上鎖2次,就得解鎖2次。

通過這個也告訴我們,有些時候多看看源碼,會比直接看說明對邏輯的理解更直觀!

2.2 屏蔽中斷

RTOS所有的線程調度都是建立在中斷基礎上的,關閉中斷,不僅可以屏蔽,外部中斷,也可以禁止調度,他比上面的禁止調度“更能夠保護”臨界區。

RT-Thread 屏蔽中斷 和 使能中斷的函數如下:

/*
返回值:
中斷狀態 	rt_hw_interrupt_disable 函數運行前的中斷狀態
*/
rt_base_t rt_hw_interrupt_disable(void);//屏蔽中斷
/*
參數:
level 	前一次 rt_hw_interrupt_disable 返回的中斷狀態
*/
void rt_hw_interrupt_enable(rt_base_t level);//中斷使能

注意,上面的終端所中斷鎖是最強大的和最高效的同步方法,這個方法最主要的問題在于,中斷響應延時會拉長,對于實時性特別極端的場合需要注意,所以實際使用要根據應用場合,合理的使用。

中斷屏蔽和中斷使能函數也是是成對使用的,切記!

使用示例:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_18,color_FFFFFF,t_70,g_se,x_16

中斷鎖源碼簡析

上面的函數找到申明,但是跳轉不到函數原型:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_13,color_FFFFFF,t_70,g_se,x_16

那么函數的實現在什么地方呢?如下圖:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

因為使用的是 gcc 編譯器,所以context_gcc.S文件中的函數體前后語句會與 MDK下有一定的區別,但函數實現的匯編語言都是一樣的:

/*
 * rt_base_t rt_hw_interrupt_disable();
 */
 /*
 .global關鍵字用來讓一個符號對鏈接器可見,可以供其他鏈接對象模塊使用
 前面兩句意思就類似于定義了一個全局可調用的函數rt_hw_interrupt_disable 
 */
    .global rt_hw_interrupt_disable //告訴編譯器rt_hw_interrupt_disable 是一個全局可見的
    .type rt_hw_interrupt_disable, %function//告訴編譯器rt_hw_interrupt_disable是一個函數
rt_hw_interrupt_disable:
    MRS     R0, PRIMASK   //讀取PRIMASK寄存器的值到r0寄存器
    CPSID   I             //關閉全局中斷,具體原因見博文后續說明
    BX      LR       	//函數返回,通過LR 連接寄存器 返回

/*
 * void rt_hw_interrupt_enable(rt_base_t level);
 */
    .global rt_hw_interrupt_enable   //與上面類似
    .type rt_hw_interrupt_enable, %function
rt_hw_interrupt_enable:
    MSR     PRIMASK, R0  //將 r0 的值寄存器寫入到 PRIMASK 寄存器
    BX      LR   		//函數返回,通過LR 連接寄存器 返回

即便上面的代碼我寫了注釋,告訴了意思,但是還是會有問題,為什么 CPSID I就是關閉全局中斷?

如果好好看了《Cortex-M3與Cortex-M4權威指南》這個文檔,所有東西都能明白了。

PRIMSK:中斷屏蔽特殊寄存器。利用 PRIMSK,可以禁止除HardFault 和 NMI外的所有異常。在上面推薦文檔中有說明:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

CPSID I就是禁止中斷,CPSIE I就是使能中斷。

一個細節,為什么 rt_hw_interrupt_enable 函數,不用 CPSIE I恢復中斷?

答案就是,如果使用CPSIE I使能中斷,那么中斷鎖就無法嵌套。使用R0寄存器將當前的PRIMASK的狀態保存起來,這樣子就必須要關多少次中斷就得開多少次中斷。

poYBAGKwKpuAU3vuAABFVBO4L08300.png

另外值得一說的是, 在上面的示例中R0寄存器中保存的值,就是 rt_base_t level這個變量!

通過上述分析,我們應該完全明白了,RT-Thread 的中斷鎖是如何實現的,那么其他的RTOS是不是都是這個樣子呢? 我們來看看 FreeRTOS 對于中斷鎖是如何實現的。

與FreeRTOS區別

FreeRTOS的臨界區,在我的博文介紹過:FreeRTOS記錄(四、FreeRTOS任務堆棧溢出問題和臨界區)

這里我們就只看一下他的實現代碼來和 RT-Thread 比較一下(同樣是以M3為例,M0與M3又是不同的):

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

這里我們分析就用在任務中屏蔽中斷的函數來分析,在中斷中屏蔽分析類似,只不過稍微復雜一點。

屏蔽中斷:

#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()
/*----------------------------------------------*/
/*只需要注意操作的寄存器為 basepri*/
/*----------------------------------------------*/

portFORCE_INLINE static void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI;

	__asm volatile
	(
		"	mov %0, %1												\n" \
		"	msr basepri, %0											\n" \  
		"	isb														\n" \
		"	dsb														\n" \
		:"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory"
	);
}

使能中斷:

//...
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI(0)
/*只需要注意操作的寄存器為 basepri*/
portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue )
{
	__asm volatile
	(
		"	msr basepri, %0	" :: "r" ( ulNewMaskValue ) : "memory"
	);
}
————————————————
版權聲明:本文為CSDN博主「矜辰所致」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_42328389/article/details/123593592

這里我們通過 FreeRTOS 中斷鎖的代碼可以看出,它操作的是basepri寄存器,而不是PRIMSK寄存器,那么basepri寄存器又是什么呢? 答案還是從《Cortex-M3與Cortex-M4權威指南》文檔中可以找到:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

FreeRTOS 在中斷鎖的操作上面,是利用 basepri 寄存器屏蔽特定優先級的中斷。 這個優先級的設置是用戶可以自行設置的。這給非常緊急的中斷留了一條后路。

但是不管怎樣,在任何時候,臨界區處理的代碼當然是時間越短越好!!

2.3 實際應用場合

簡單總結一下,臨界區的保護實際應用中可能需要的場合:

調用公共函數的代碼(不可重入函數)
讀取或者修改變量(全局變量)
使用硬件資源(在操作內存或者flash的時候)
對時序有精準要求的操作(I2C通訊,但是得注意在通訊中不能使用利用了systick的延時函數,用干等的延時)
某些用戶不想被打斷的代碼(比如 printf 打印)

在一般的場合,普通臨界區的保護使用禁止調度的方式就可以滿足需求了,除非你中斷中有對臨界資源的訪問。
當然事無絕對,有些時候中斷的發生對某些普通任務(比如ADC采樣)也可能產品影響,所以還是需要根據實際情況,合理的使用 臨界區保護。

結語

本文的內容從學會 RT-Thread 臨界區保護的使用來說是比較簡單,只需要掌握幾個函數的調用就可以。但對于了解實現原理來說相對復雜些,需要對內核,對操作系統基本原理有一定的理解。

我們通過對這幾個函數源碼的簡單分析,讓我們對其原理的實現有了更直觀的理解,養成看源碼是對我們學習有幫助的一個好習慣!

下一篇 RT-Thread 記錄,就要來學習 RT-Thread 的線程間同步相關的信號量,互斥量,這也是 RT-Thread 對臨界區的另一種保護方式。

謝謝!

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • FreeRTOS
    +關注

    關注

    12

    文章

    488

    瀏覽量

    63738
  • Studio
    +關注

    關注

    2

    文章

    204

    瀏覽量

    29506
  • 線程
    +關注

    關注

    0

    文章

    507

    瀏覽量

    20073
  • RT-Thread
    +關注

    關注

    32

    文章

    1369

    瀏覽量

    41507
收藏 人收藏

    評論

    相關推薦
    熱點推薦

    RT-Thread記錄(一、版本開發環境及配合CubeMX)

    RT-Thread 學習記錄的第一篇文章,RT-Thread記錄(一、RT-Thread 版本、RT-T
    的頭像 發表于 06-20 00:28 ?5782次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(一、版本開發環境及配合CubeMX)

    RT-Thread記錄(二、RT-Thread內核啟動流程)

    在前面我們RT-Thread Studio工程基礎之上講一講RT-Thread內核啟動流程.
    的頭像 發表于 06-20 00:30 ?5477次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(二、<b class='flag-5'>RT-Thread</b>內核啟動流程)

    【原創精選】RT-Thread征文精選技術文章合集

    RT-Thread記錄RT-Thread 臨界保護
    發表于 07-26 14:56

    RT-Thread編程指南

    RT-Thread編程指南——RT-Thread開發組(2015-03-31)。RT-Thread做為國內有較大影響力的開源實時操作系統,本文是RT-Thread實時操作系統的編程指南
    發表于 11-26 16:06 ?0次下載

    RT-Thread用戶手冊

    RT-Thread用戶手冊——本書是RT-Thread的編程手冊,用于指導在RT-Thread實時操作系統環境下如何進行編 程。
    發表于 11-26 16:16 ?0次下載

    RT-Thread開發,如何有效學習RT-Thread個步驟

    RT-Thread推出RT-Thread Inside戰略開放RT-Thread開發平臺授權合作,與硬件十萬個為什么合作首次推出第一款RT-Inside的開發板——iBox物聯網開發套
    的頭像 發表于 09-25 09:55 ?3.5w次閱讀
    <b class='flag-5'>RT-Thread</b>開發,如何有效學習<b class='flag-5'>RT-Thread</b>的<b class='flag-5'>五</b>個步驟

    RT-Thread全球技術大會:螢石研發團隊使用RT-Thread的技術挑戰

    RT-Thread全球技術大會:研發團隊使用RT-Thread的技術挑戰 ? ? ? ? 審核編輯:彭靜
    的頭像 發表于 05-27 11:36 ?1480次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術大會:螢石研發團隊使用<b class='flag-5'>RT-Thread</b>的技術挑戰

    RT-Thread全球技術大會:Kconfig在RT-Thread中的工作機制

    RT-Thread全球技術大會:Kconfig在RT-Thread中的工作機制 ? ? ? ? ? ? ? 審核編輯:彭靜
    的頭像 發表于 05-27 14:49 ?1755次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術大會:Kconfig在<b class='flag-5'>RT-Thread</b>中的工作機制

    RT-Thread全球技術大會:在RT-Thread上編寫測試用例

    RT-Thread全球技術大會:在RT-Thread上編寫測試用例 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發表于 05-27 16:28 ?1676次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術大會:在<b class='flag-5'>RT-Thread</b>上編寫測試用例

    RT-Thread全球技術大會:RT-Thread測試用例集合案例

    RT-Thread全球技術大會:RT-Thread測試用例集合案例 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發表于 05-27 16:34 ?2301次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術大會:<b class='flag-5'>RT-Thread</b>測試用例集合案例

    RT-Thread學習筆記 RT-Thread的架構概述

    RT-Thread 簡介 作為一名 RTOS 的初學者,也許你對 RT-Thread 還比較陌生。然而,隨著你的深入接觸,你會逐漸發現 RT-Thread 的魅力和它相較于其他同類型 RTOS
    的頭像 發表于 07-09 11:27 ?4899次閱讀
    <b class='flag-5'>RT-Thread</b>學習筆記 <b class='flag-5'>RT-Thread</b>的架構概述

    RT-Thread文檔_RT-Thread 簡介

    RT-Thread文檔_RT-Thread 簡介
    發表于 02-22 18:22 ?5次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> 簡介

    RT-Thread文檔_RT-Thread 潘多拉 STM32L475 上手指南

    RT-Thread文檔_RT-Thread 潘多拉 STM32L475 上手指南
    發表于 02-22 18:23 ?10次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> 潘多拉 STM32L475 上手指南

    RT-Thread文檔_RT-Thread SMP 介紹與移植

    RT-Thread文檔_RT-Thread SMP 介紹與移植
    發表于 02-22 18:31 ?9次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> SMP 介紹與移植

    基于RT-Thread Studio學習

    前期準備:從官網下載 RT-Thread Studio,弄個賬號登陸,開啟rt-thread學習之旅。
    的頭像 發表于 05-15 11:00 ?5009次閱讀
    基于<b class='flag-5'>RT-Thread</b> Studio學習