临界区保护_临界地带

临界区保护_临界地带1临界区保护1.1问题引入首先看一下如下问题:原因分析:根本原因在于读-改-写过程中随时会被打断,再恢复运行时写,导致打断过程中其它写的效果被覆盖。1.2临界区概念临界区的概念如下:临界区指的是访问多个任务共享资源的一段代码。当有任务进入临界区时,其它任务必须等待直至该任务离开临界区,以确定共享资源的访问不会冲突。由于共享资源的访问存在于任务与任务之间、任务与中断I…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

1 临界区保护

1.1 问题引入

首先看一下如下问题:
在这里插入图片描述
原因分析:

  • 根本原因在于读-改-写过程中随时会被打断,再恢复运行时写,导致打断过程中其它写的效果被覆盖。在这里插入图片描述

1.2 临界区概念

临界区的概念如下:

  • 临界区指的是访问多个任务共享资源的一段代码。当有任务进入临界区时,其它任务必须等待直至该任务离开临界区,以确定共享资源的访问不会冲突。
    在这里插入图片描述
    由于共享资源的访问存在于任务与任务之间、任务与中断ISR之间;那么,只需要防止任务在访问共享资源时,切换至其它任务或防止中断发生即可。
    在这里插入图片描述

1.3 使用关中断保护临界区

我们可以使用关中断来保护临界区,如下:
在这里插入图片描述
但是当中断发生嵌套的时候就会出现问题:
在这里插入图片描述
所以我们需要采用如下的解决方案:
在这里插入图片描述

1.4 设计实现

中断控制寄存器PRIMASK:
在这里插入图片描述
进入临界区:
在这里插入图片描述
退出临界区:
在这里插入图片描述
这里只贴一下main.c文件中的内容:

/*************************************** Copyright (c)****************************************************** ** File name : main.c ** Latest modified Date : 2016-06-01 ** Latest Version : 0.1 ** Descriptions : 主文件,包含应用代码 ** **-------------------------------------------------------------------------------------------------------- ** Created by : 01课堂 lishutong ** Created date : 2016-06-01 ** Version : 1.0 ** Descriptions : The original version ** **-------------------------------------------------------------------------------------------------------- ** Copyright : 版权所有,禁止用于商业用途 ** Author Blog : http://ilishutong.com **********************************************************************************************************/
#include "tinyOS.h"
#include "ARMCM3.h"
// 当前任务:记录当前是哪个任务正在运行
tTask * currentTask;
// 下一个将即运行的任务:在进行任务切换前,先设置好该值,然后任务切换过程中会从中读取下一任务信息
tTask * nextTask;
// 空闲任务
tTask * idleTask;
// 所有任务的指针数组:简单起见,只使用两个任务
tTask * taskTable[2];
// 用于临界区测试的计数器
uint32_t tickCounter;
/********************************************************************************************************** ** Function name : tTaskInit ** Descriptions : 初始化任务结构 ** parameters : task 要初始化的任务结构 ** parameters : entry 任务的入口函数 ** parameters : param 传递给任务的运行参数 ** Returned value : 无 ***********************************************************************************************************/
void tTaskInit (tTask * task, void (*entry)(void *), void *param, uint32_t * stack)
{ 

// 为了简化代码,tinyOS无论是在启动时切换至第一个任务,还是在运行过程中在不同间任务切换
// 所执行的操作都是先保存当前任务的运行环境参数(CPU寄存器值)的堆栈中(如果已经运行运行起来的话),然后再
// 取出从下一个任务的堆栈中取出之前的运行环境参数,然后恢复到CPU寄存器
// 对于切换至之前从没有运行过的任务,我们为它配置一个“虚假的”保存现场,然后使用该现场恢复。
// 注意以下两点:
// 1、不需要用到的寄存器,直接填了寄存器号,方便在IDE调试时查看效果;
// 2、顺序不能变,要结合PendSV_Handler以及CPU对异常的处理流程来理解
*(--stack) = (unsigned long)(1<<24);                // XPSR, 设置了Thumb模式,恢复到Thumb状态而非ARM状态运行
*(--stack) = (unsigned long)entry;                  // 程序的入口地址
*(--stack) = (unsigned long)0x14;                   // R14(LR), 任务不会通过return xxx结束自己,所以未用
*(--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, 传给任务的入口函数
*(--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 : 任务调度接口。tinyOS通过它来选择下一个具体的任务,然后切换至该任务运行。 ** parameters : 无 ** Returned value : 无 ***********************************************************************************************************/
void tTaskSched () 
{ 
   
// 进入临界区,以保护在整个任务调度与切换期间,不会因为发生中断导致currentTask和nextTask可能更改
uint32_t status = tTaskEnterCritical();
// 空闲任务只有在所有其它任务都不是延时状态时才执行
// 所以,我们先检查下当前任务是否是空闲任务
if (currentTask == idleTask) 
{ 

// 如果是的话,那么去执行task1或者task2中的任意一个
// 当然,如果某个任务还在延时状态,那么就不应该切换到他。
// 如果所有任务都在延时,那么就继续运行空闲任务,不进行任何切换了
if (taskTable[0]->delayTicks == 0) 
{ 

nextTask = taskTable[0];
}           
else if (taskTable[1]->delayTicks == 0) 
{ 

nextTask = taskTable[1];
} 
else 
{ 

tTaskExitCritical(status);
return;
}
} 
else 
{ 

// 如果是task1或者task2的话,检查下另外一个任务
// 如果另外的任务不在延时中,就切换到该任务
// 否则,判断下当前任务是否应该进入延时状态,如果是的话,就切换到空闲任务。否则就不进行任何切换
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();   
// 退出临界区
tTaskExitCritical(status); 
}
/********************************************************************************************************** ** Function name : tTaskSystemTickHandler ** Descriptions : 系统时钟节拍处理。 ** parameters : 无 ** Returned value : 无 ***********************************************************************************************************/
void tTaskSystemTickHandler () 
{ 

// 检查所有任务的delayTicks数,如果不0的话,减1。
int i;   
uint32_t status = tTaskEnterCritical();
for (i = 0; i < 2; i++) 
{ 

if (taskTable[i]->delayTicks > 0)
{ 

taskTable[i]->delayTicks--;
}
}
// 在中断中也有访问资源
tickCounter++;
tTaskExitCritical(status);
// 这个过程中可能有任务延时完毕(delayTicks = 0),进行一次调度。
tTaskSched();
}
/********************************************************************************************************** ** Function name : taskDelay ** Descriptions : 使当前任务进入延时状态。 ** parameters : delay 延时多少个ticks ** Returned value : 无 ***********************************************************************************************************/
void taskDelay (uint32_t delay) { 

// 配置好当前要延时的ticks数
uint32_t status = tTaskEnterCritical();
currentTask->delayTicks = delay;
tTaskExitCritical(status);
// 然后进行任务切换,切换至另一个任务,或者空闲任务
// delayTikcs会在时钟中断中自动减1.当减至0时,会切换回来继续运行。
tTaskSched();
}
/********************************************************************************************************* ** 系统时钟节拍定时器System Tick配置 ** 在我们目前的环境(模拟器)中,系统时钟节拍为12MHz ** 请务必按照本教程推荐配置,否则systemTick的值就会有变化,需要查看数据手册才了解 **********************************************************************************************************/
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的中断处理函数。 ** parameters : 无 ** Returned value : 无 ***********************************************************************************************************/
void SysTick_Handler () 
{ 

tTaskSystemTickHandler();
}
/********************************************************************************************************** ** 应用示例 ** 有两个任务,分别执行task1Entry和task2Entry。功能是分别对相应的变量进行周期性置0置1. ** 每个任务都可以占用一段时间的CPU,一旦用完了,就会被强制暂停,切换到另一个任务中去。 ** 本例中有全局共享的变量criticalCounter,同时在taskDelay和tTaskSystemTickHandler中修改。 ** 如果不加临界区保护,则在tTaskSystemTickHandler中对criticalCounter的修改将会丢失 **********************************************************************************************************/
int task1Flag;
void task1Entry (void * param) 
{ 

tSetSysTickPeriod(10);
for (;;) 
{ 

task1Flag = 1;
taskDelay(1);
task1Flag = 0;
taskDelay(1);
}
}
int task2Flag;
void task2Entry (void * param) 
{ 

for (;;) 
{ 

// 临界区演示的计数器
uint32_t i;
// 由于有临界区的保护,所以即便是这中间中断产生,也不会立即响应中断。
// criticalCounter的值会被正确修改。等待退出临界区时,才会响应中断,在中断中修改criticalCounter
// 最终不会导致冲突
// 进入临界区
uint32_t status = tTaskEnterCritical();
uint32_t counter = tickCounter;
for (i = 0; i < 0xFFFF; i++) { 
}         // 故意产生长的延时,以便在此期间发生中断
tickCounter = counter + 1;
// 退出临界区
tTaskExitCritical(status);
task2Flag = 1;
taskDelay(1);
task2Flag = 0;
taskDelay(1);
}
}
// 任务1和任务2的任务结构,以及用于堆栈空间
tTask tTask1;
tTask tTask2;
tTaskStack task1Env[1024];
tTaskStack task2Env[1024];
// 用于空闲任务的任务结构和堆栈空间
tTask tTaskIdle;
tTaskStack idleTaskEnv[1024];
void idleTaskEntry (void * param) { 

for (;;)
{ 

// 空闲任务什么都不做
}
}
int main () 
{ 

// 初始化任务1和任务2结构,传递运行的起始地址,想要给任意参数,以及运行堆栈空间
tTaskInit(&tTask1, task1Entry, (void *)0x11111111, &task1Env[1024]);
tTaskInit(&tTask2, task2Entry, (void *)0x22222222, &task2Env[1024]);
// 接着,将任务加入到任务表中
taskTable[0] = &tTask1;
taskTable[1] = &tTask2;
// 创建空闲任务
tTaskInit(&tTaskIdle, idleTaskEntry, (void *)0, &idleTaskEnv[1024]);
idleTask = &tTaskIdle;
// 我们期望先运行tTask1, 也就是void task1Entry (void * param) 
nextTask = taskTable[0];
// 切换到nextTask, 这个函数永远不会返回
tTaskRunFirst();
return 0;
}

Jetbrains全家桶1年46,售后保障稳定


参考资料:

  1. 【李述铜】从0到1自己动手写嵌入式操作系统
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/226923.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 如何将a4排版成a3双面打印_A4如何双面打印

    如何将a4排版成a3双面打印_A4如何双面打印A4排成A3双面打印怎么操作?A3纸张的尺寸是297mm×420mm,其大小相当于两张A4的大小,A4是大家工作及生活中使用较多的纸张尺寸,A3纸张不常用,但是遇到一些比较重要的画报、图纸等之类的资料,A3纸张就比较突出了。在城市周边打印店,打印资料时多以使用A4纸张居多,所以如果您到打印店打印A3纸张,很有可能会被打印店告知:无法打印。有时候可能不是打印店员工不会帮您排版,而是打印店的设备不支持为大家打印A3大小的纸张资料。今天小编给大家介绍一个比较专业的网上在线打印平台——易桌面打印室,这是一个网

  • @RequestBody出现请求415问题

    @RequestBody出现请求415问题首先之前也遇到过这样的问题,但是稀里糊涂的最后虽然bug修复了,但是不知道是啥原因,今天又遇到了同样的问题。问题:在前端向后台发送一个json对象(字符串),后端用@RequestBody匹配接受的时候就会出现415,如果不用@RequestBody,那么前端发送来的数据是不能被正确解析成对象里的属性的。因为用公司的框架写项目的时候,也遇见了这个问题。请教了学长,学长让我用公司自己封装的p…

  • COleVariant 的使用

    COleVariant 的使用使用COleVariant传递基本数据类型COleVariant vTrue(TRUE,VT_BOOL);COleVariant vFalse(FALSE,VT_BOOL);COleVariant vFileName((LPCTSTR)pszFileP

  • C++中int转string方法

    C++中int转string方法一点睛需要用到snprintf()。函数原型intsnprintf(char*str,size_tsize,constchar*format,…)功能:将可变参数“…”按照format的格式格式化为字符串,然后再将其拷贝至str中。(1)如果格式化后的字符串长度<size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符(…

  • java.lang.verifyerror_java.lang.reflect.undeclared

    java.lang.verifyerror_java.lang.reflect.undeclared以前写代码,总免不了编写登陆部分。在获取user的时候,只可能返回一个user实例,或者为null。以前使用以下方法实现。publicUserget(Stringid){Sessionsession=HibernateUtil.getSessionFactory().openSession();Stringhql=”fromUseruwhereu.id=?”;Listlis…

  • 【转载】一分钟了解两阶段提交2PC(运营MM也懂了)

    【转载】一分钟了解两阶段提交2PC(运营MM也懂了)

    2021年11月20日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号