日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

临界区保护

發(fā)布時(shí)間:2025/4/5 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 临界区保护 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1 臨界區(qū)保護(hù)

1.1 問(wèn)題引入

首先看一下如下問(wèn)題:

原因分析:

  • 根本原因在于讀-改-寫過(guò)程中隨時(shí)會(huì)被打斷,再恢復(fù)運(yùn)行時(shí)寫,導(dǎo)致打斷過(guò)程中其它寫的效果被覆蓋。

1.2 臨界區(qū)概念

臨界區(qū)的概念如下:

  • 臨界區(qū)指的是訪問(wèn)多個(gè)任務(wù)共享資源的一段代碼。當(dāng)有任務(wù)進(jìn)入臨界區(qū)時(shí),其它任務(wù)必須等待直至該任務(wù)離開(kāi)臨界區(qū),以確定共享資源的訪問(wèn)不會(huì)沖突。

    由于共享資源的訪問(wèn)存在于任務(wù)與任務(wù)之間、任務(wù)與中斷ISR之間;那么,只需要防止任務(wù)在訪問(wèn)共享資源時(shí),切換至其它任務(wù)或防止中斷發(fā)生即可。

1.3 使用關(guān)中斷保護(hù)臨界區(qū)

我們可以使用關(guān)中斷來(lái)保護(hù)臨界區(qū),如下:

但是當(dāng)中斷發(fā)生嵌套的時(shí)候就會(huì)出現(xiàn)問(wèn)題:

所以我們需要采用如下的解決方案:

1.4 設(shè)計(jì)實(shí)現(xiàn)

中斷控制寄存器PRIMASK:

進(jìn)入臨界區(qū):

退出臨界區(qū):

這里只貼一下main.c文件中的內(nèi)容:

/*************************************** Copyright (c)****************************************************** ** File name : main.c ** Latest modified Date : 2016-06-01 ** Latest Version : 0.1 ** Descriptions : 主文件,包含應(yīng)用代碼 ** **-------------------------------------------------------------------------------------------------------- ** Created by : 01課堂 lishutong ** Created date : 2016-06-01 ** Version : 1.0 ** Descriptions : The original version ** **-------------------------------------------------------------------------------------------------------- ** Copyright : 版權(quán)所有,禁止用于商業(yè)用途 ** Author Blog : http://ilishutong.com **********************************************************************************************************/ #include "tinyOS.h" #include "ARMCM3.h"// 當(dāng)前任務(wù):記錄當(dāng)前是哪個(gè)任務(wù)正在運(yùn)行 tTask * currentTask;// 下一個(gè)將即運(yùn)行的任務(wù):在進(jìn)行任務(wù)切換前,先設(shè)置好該值,然后任務(wù)切換過(guò)程中會(huì)從中讀取下一任務(wù)信息 tTask * nextTask;// 空閑任務(wù) tTask * idleTask;// 所有任務(wù)的指針數(shù)組:簡(jiǎn)單起見(jiàn),只使用兩個(gè)任務(wù) tTask * taskTable[2];// 用于臨界區(qū)測(cè)試的計(jì)數(shù)器 uint32_t tickCounter;/********************************************************************************************************** ** Function name : tTaskInit ** Descriptions : 初始化任務(wù)結(jié)構(gòu) ** parameters : task 要初始化的任務(wù)結(jié)構(gòu) ** parameters : entry 任務(wù)的入口函數(shù) ** parameters : param 傳遞給任務(wù)的運(yùn)行參數(shù) ** Returned value : 無(wú) ***********************************************************************************************************/ void tTaskInit (tTask * task, void (*entry)(void *), void *param, uint32_t * stack) {// 為了簡(jiǎn)化代碼,tinyOS無(wú)論是在啟動(dòng)時(shí)切換至第一個(gè)任務(wù),還是在運(yùn)行過(guò)程中在不同間任務(wù)切換// 所執(zhí)行的操作都是先保存當(dāng)前任務(wù)的運(yùn)行環(huán)境參數(shù)(CPU寄存器值)的堆棧中(如果已經(jīng)運(yùn)行運(yùn)行起來(lái)的話),然后再// 取出從下一個(gè)任務(wù)的堆棧中取出之前的運(yùn)行環(huán)境參數(shù),然后恢復(fù)到CPU寄存器// 對(duì)于切換至之前從沒(méi)有運(yùn)行過(guò)的任務(wù),我們?yōu)樗渲靡粋€(gè)“虛假的”保存現(xiàn)場(chǎng),然后使用該現(xiàn)場(chǎng)恢復(fù)。// 注意以下兩點(diǎn):// 1、不需要用到的寄存器,直接填了寄存器號(hào),方便在IDE調(diào)試時(shí)查看效果;// 2、順序不能變,要結(jié)合PendSV_Handler以及CPU對(duì)異常的處理流程來(lái)理解*(--stack) = (unsigned long)(1<<24); // XPSR, 設(shè)置了Thumb模式,恢復(fù)到Thumb狀態(tài)而非ARM狀態(tài)運(yùn)行*(--stack) = (unsigned long)entry; // 程序的入口地址*(--stack) = (unsigned long)0x14; // R14(LR), 任務(wù)不會(huì)通過(guò)return xxx結(jié)束自己,所以未用*(--stack) = (unsigned long)0x12; // R12, 未用*(--stack) = (unsigned long)0x3; // R3, 未用*(--stack) = (unsigned long)0x2; // R2, 未用*(--stack) = (unsigned long)0x1; // R1, 未用*(--stack) = (unsigned long)param; // R0 = param, 傳給任務(wù)的入口函數(shù)*(--stack) = (unsigned long)0x11; // R11, 未用*(--stack) = (unsigned long)0x10; // R10, 未用*(--stack) = (unsigned long)0x9; // R9, 未用*(--stack) = (unsigned long)0x8; // R8, 未用*(--stack) = (unsigned long)0x7; // R7, 未用*(--stack) = (unsigned long)0x6; // R6, 未用*(--stack) = (unsigned long)0x5; // R5, 未用*(--stack) = (unsigned long)0x4; // R4, 未用task->stack = stack; // 保存最終的值task->delayTicks = 0; }/********************************************************************************************************** ** Function name : tTaskSched ** Descriptions : 任務(wù)調(diào)度接口。tinyOS通過(guò)它來(lái)選擇下一個(gè)具體的任務(wù),然后切換至該任務(wù)運(yùn)行。 ** parameters : 無(wú) ** Returned value : 無(wú) ***********************************************************************************************************/ void tTaskSched () { // 進(jìn)入臨界區(qū),以保護(hù)在整個(gè)任務(wù)調(diào)度與切換期間,不會(huì)因?yàn)榘l(fā)生中斷導(dǎo)致currentTask和nextTask可能更改uint32_t status = tTaskEnterCritical();// 空閑任務(wù)只有在所有其它任務(wù)都不是延時(shí)狀態(tài)時(shí)才執(zhí)行// 所以,我們先檢查下當(dāng)前任務(wù)是否是空閑任務(wù)if (currentTask == idleTask) {// 如果是的話,那么去執(zhí)行task1或者task2中的任意一個(gè)// 當(dāng)然,如果某個(gè)任務(wù)還在延時(shí)狀態(tài),那么就不應(yīng)該切換到他。// 如果所有任務(wù)都在延時(shí),那么就繼續(xù)運(yùn)行空閑任務(wù),不進(jìn)行任何切換了if (taskTable[0]->delayTicks == 0) {nextTask = taskTable[0];} else if (taskTable[1]->delayTicks == 0) {nextTask = taskTable[1];} else {tTaskExitCritical(status);return;}} else {// 如果是task1或者task2的話,檢查下另外一個(gè)任務(wù)// 如果另外的任務(wù)不在延時(shí)中,就切換到該任務(wù)// 否則,判斷下當(dāng)前任務(wù)是否應(yīng)該進(jìn)入延時(shí)狀態(tài),如果是的話,就切換到空閑任務(wù)。否則就不進(jìn)行任何切換if (currentTask == taskTable[0]) {if (taskTable[1]->delayTicks == 0) {nextTask = taskTable[1];}else if (currentTask->delayTicks != 0) {nextTask = idleTask;} else {tTaskExitCritical(status);return;}}else if (currentTask == taskTable[1]) {if (taskTable[0]->delayTicks == 0) {nextTask = taskTable[0];}else if (currentTask->delayTicks != 0) {nextTask = idleTask;}else {tTaskExitCritical(status);return;}}}tTaskSwitch(); // 退出臨界區(qū)tTaskExitCritical(status); }/********************************************************************************************************** ** Function name : tTaskSystemTickHandler ** Descriptions : 系統(tǒng)時(shí)鐘節(jié)拍處理。 ** parameters : 無(wú) ** Returned value : 無(wú) ***********************************************************************************************************/ void tTaskSystemTickHandler () {// 檢查所有任務(wù)的delayTicks數(shù),如果不0的話,減1。int i; uint32_t status = tTaskEnterCritical();for (i = 0; i < 2; i++) {if (taskTable[i]->delayTicks > 0){taskTable[i]->delayTicks--;}}// 在中斷中也有訪問(wèn)資源tickCounter++;tTaskExitCritical(status);// 這個(gè)過(guò)程中可能有任務(wù)延時(shí)完畢(delayTicks = 0),進(jìn)行一次調(diào)度。tTaskSched(); }/********************************************************************************************************** ** Function name : taskDelay ** Descriptions : 使當(dāng)前任務(wù)進(jìn)入延時(shí)狀態(tài)。 ** parameters : delay 延時(shí)多少個(gè)ticks ** Returned value : 無(wú) ***********************************************************************************************************/ void taskDelay (uint32_t delay) {// 配置好當(dāng)前要延時(shí)的ticks數(shù)uint32_t status = tTaskEnterCritical();currentTask->delayTicks = delay;tTaskExitCritical(status);// 然后進(jìn)行任務(wù)切換,切換至另一個(gè)任務(wù),或者空閑任務(wù)// delayTikcs會(huì)在時(shí)鐘中斷中自動(dòng)減1.當(dāng)減至0時(shí),會(huì)切換回來(lái)繼續(xù)運(yùn)行。tTaskSched(); }/********************************************************************************************************* ** 系統(tǒng)時(shí)鐘節(jié)拍定時(shí)器System Tick配置 ** 在我們目前的環(huán)境(模擬器)中,系統(tǒng)時(shí)鐘節(jié)拍為12MHz ** 請(qǐng)務(wù)必按照本教程推薦配置,否則systemTick的值就會(huì)有變化,需要查看數(shù)據(jù)手冊(cè)才了解 **********************************************************************************************************/ void tSetSysTickPeriod(uint32_t ms) {SysTick->LOAD = ms * SystemCoreClock / 1000 - 1;NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);SysTick->VAL = 0;SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |SysTick_CTRL_TICKINT_Msk |SysTick_CTRL_ENABLE_Msk; }/********************************************************************************************************** ** Function name : SysTick_Handler ** Descriptions : SystemTick的中斷處理函數(shù)。 ** parameters : 無(wú) ** Returned value : 無(wú) ***********************************************************************************************************/ void SysTick_Handler () {tTaskSystemTickHandler(); }/********************************************************************************************************** ** 應(yīng)用示例 ** 有兩個(gè)任務(wù),分別執(zhí)行task1Entry和task2Entry。功能是分別對(duì)相應(yīng)的變量進(jìn)行周期性置0置1. ** 每個(gè)任務(wù)都可以占用一段時(shí)間的CPU,一旦用完了,就會(huì)被強(qiáng)制暫停,切換到另一個(gè)任務(wù)中去。 ** 本例中有全局共享的變量criticalCounter,同時(shí)在taskDelay和tTaskSystemTickHandler中修改。 ** 如果不加臨界區(qū)保護(hù),則在tTaskSystemTickHandler中對(duì)criticalCounter的修改將會(huì)丟失 **********************************************************************************************************/ int task1Flag; void task1Entry (void * param) {tSetSysTickPeriod(10);for (;;) {task1Flag = 1;taskDelay(1);task1Flag = 0;taskDelay(1);} }int task2Flag; void task2Entry (void * param) {for (;;) {// 臨界區(qū)演示的計(jì)數(shù)器uint32_t i;// 由于有臨界區(qū)的保護(hù),所以即便是這中間中斷產(chǎn)生,也不會(huì)立即響應(yīng)中斷。// criticalCounter的值會(huì)被正確修改。等待退出臨界區(qū)時(shí),才會(huì)響應(yīng)中斷,在中斷中修改criticalCounter// 最終不會(huì)導(dǎo)致沖突// 進(jìn)入臨界區(qū)uint32_t status = tTaskEnterCritical();uint32_t counter = tickCounter;for (i = 0; i < 0xFFFF; i++) {} // 故意產(chǎn)生長(zhǎng)的延時(shí),以便在此期間發(fā)生中斷tickCounter = counter + 1;// 退出臨界區(qū)tTaskExitCritical(status);task2Flag = 1;taskDelay(1);task2Flag = 0;taskDelay(1);} }// 任務(wù)1和任務(wù)2的任務(wù)結(jié)構(gòu),以及用于堆棧空間 tTask tTask1; tTask tTask2; tTaskStack task1Env[1024]; tTaskStack task2Env[1024];// 用于空閑任務(wù)的任務(wù)結(jié)構(gòu)和堆棧空間 tTask tTaskIdle; tTaskStack idleTaskEnv[1024];void idleTaskEntry (void * param) {for (;;){// 空閑任務(wù)什么都不做} }int main () {// 初始化任務(wù)1和任務(wù)2結(jié)構(gòu),傳遞運(yùn)行的起始地址,想要給任意參數(shù),以及運(yùn)行堆棧空間tTaskInit(&tTask1, task1Entry, (void *)0x11111111, &task1Env[1024]);tTaskInit(&tTask2, task2Entry, (void *)0x22222222, &task2Env[1024]);// 接著,將任務(wù)加入到任務(wù)表中taskTable[0] = &tTask1;taskTable[1] = &tTask2;// 創(chuàng)建空閑任務(wù)tTaskInit(&tTaskIdle, idleTaskEntry, (void *)0, &idleTaskEnv[1024]);idleTask = &tTaskIdle;// 我們期望先運(yùn)行tTask1, 也就是void task1Entry (void * param) nextTask = taskTable[0];// 切換到nextTask, 這個(gè)函數(shù)永遠(yuǎn)不會(huì)返回tTaskRunFirst();return 0; }

參考資料:

  • 【李述銅】從0到1自己動(dòng)手寫嵌入式操作系統(tǒng)
  • 《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結(jié)

    以上是生活随笔為你收集整理的临界区保护的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。