其他分享
首页 > 其他分享> > uCOS-III 学习记录(5)——临界段

uCOS-III 学习记录(5)——临界段

作者:互联网

喜气洋洋过虎年!祝各位明年会有更大的进步!

参考内容:《[野火]uCOS-III内核实现与应用开发实战指南——基于STM32》第 10 章。

目录

1 临界段

临界段,又叫做临界区。对于多线程而言,它是一段不可分割、不可上下文切换的代码。对于 uCOS 而言,它是一段不可被中断的代码。临界段是不能被中断的,需要关中断或锁调度器(OSSched)以保护临界段。

什么情况下临界段代码会被打断?由刚才的描述可知,临界段被打断有两种情况:

因此,对临界段保护的实质就是控制中断的开启和关闭。

uCOS 定义了进入临界段的宏和退出临界段的宏,这两个宏分别实现了中断的开启和关闭。

还有一个宏,用于存储中断的状态:

2 临界段的保护

2.1 Cortex-M 内核的中断指令

在 CM 内核中,中断是通过 CPS 指令来控制的。

CPSID I     ;PRIMASK=1,关中断
CPSIE I     ;PRIMASK=0,开中断
CPSID F     ;FAULTMASK=1,关异常
CPSIE F     ;FAULTMASK=0,开异常

PRIMASK 和 FAULTMAST 是 CM 内核里面三个中断屏蔽寄存器中的两个,还有一个是 BASEPRI,最后一个用不到,就不进行介绍了。

因此,也可以通过 MSR 指令修改 PRIMASK(或 FAULTMASK)来开启或关闭中断:

MOVS   R0, #1
MSR    PRIMASK,  R0     ; 将 1 写入 PRIMASK 禁止所有中断

MOVS   R0, #0
MSR    PRIMASK,  R0     ; 将 0 写入 PRIMASK 使能中断

2.2 开中断和关中断

2.2.1 关中断 CPU_SR_Save()(cpu_a.asm)

该函数完成的事情:

; CPU_SR  CPU_SR_Save (void);  (临界段关中断,R0 为返回值)
CPU_SR_Save		
	MRS		R0, PRIMASK		; 将 PRIMASK 寄存器的值存入 R0 中
	CPSID 	I	; 关中断
	BX		LR

2.2.2 开中断 CPU_SR_Restore()(cpu_a.asm)

该函数完成的事情:

; void CPU_SR_Restore (CPU_SR  cpu_sr);   (临界段开中断,R0 为形参)
CPU_SR_Restore	
	MSR		PRIMASK, R0		; 将 R0 的值存入 PRIMASK 寄存器中
	BX		LR

为什么开中断不直接使用 CPS 指令呢?待会在应用那节(2.3.2 节)你就会明白了。

2.2.3 宏定义封装(cpu.h)

最后,在 cpu.h 中,将开中断和关中断的函数封装成一个宏,方便调用。

/*********************************CPU寄存器数据类型定义*********************************/
typedef volatile 		CPU_INT32U		CPU_REG32;
typedef 				CPU_REG32		CPU_SR;

/*********************************临界段定义*********************************/

#define CPU_SR_ALLOC()			CPU_SR	cpu_sr = (CPU_SR)0      // 用于存放中断状态
#define CPU_INT_DIS()			do { cpu_sr = CPU_SR_Save(); } while(0)     // 关闭中断,存储中断状态
#define CPU_INT_EN()			do { CPU_SR_Restore(cpu_sr); } while(0)     // 恢复中断状态
#define CPU_CRITICAL_ENTER()	do { CPU_INT_DIS(); } while(0)
#define CPU_CRITICAL_EXIT()		do { CPU_INT_EN();  } while(0)

/*********************************函数声明(cpu_a.asm)*********************************/
void 	CPU_IntDis 		(void);
void 	CPU_IntEn		(void);
CPU_SR 	CPU_SR_Save 	(void);
void 	CPU_SR_Restore 	(CPU_SR  cpu_sr);

2.3 临界段保护的应用

2.3.1 一层临界段的应用

如果有这么一段临界段代码:

/* 临界段代码保护 */
{
    /* 临界段开始 */
    {
        /* 执行临界段代码,不可中断 */
    }
    /* 临界段结束 */
}

那么使用以上宏定义的格式为:

/* 临界段代码保护 */
{
    CPU_SR_ALLOC();         /* cpu_sr = 0 */
    CPU_INT_DIS();          /* 关中断 */
    /* 临界段开始 */
    {
        /* 执行临界段代码,不可中断 */
    }
    /* 临界段结束 */
    CPU_INT_EN();           /* 开中断 */
}

若将其展开,则变成:

/* 临界段代码保护 */
{
    CPU_SR	cpu_sr = (CPU_SR)0;     /* (a) 定义一个变量,用于存放中断状态,初始化 cpu_sr = 0 */
    cpu_sr = CPU_SR_Save();         /* (b) cpu_sr 保存当前中断状态,然后关中断 */
    /* 临界段开始 */
    {
        /* 执行临界段代码,不可中断 */
    }
    /* 临界段结束 */
    CPU_SR_Restore(cpu_sr);         /* (c) cpu_sr 写入中断状态,恢复之前的中断状态 */
}

这个过程如下:

2.3.2 多层临界段的应用

如果是两层临界段代码的嵌套:

/* 临界段代码保护 */
{
    /* 临界段 1 开始 */
    {
        /* 临界段 2 开始 */
        {
            /* 执行临界段代码,不可中断 */
        }
        /* 临界段 2 结束 */
    }
    /* 临界段 1 结束 */
}

进入临界段 1 前,需要关闭中断;进入临界段 2 前,也需要关闭中断,那么使用宏定义的格式为:

/* 临界段代码保护 */
{
    CPU_SR_ALLOC();         /* cpu_sr = 0 */
    CPU_INT_DIS();          /* 关中断(临界段 1) */
    /* 临界段 1 开始 */
    {
        CPU_SR_ALLOC();         /* cpu_sr = 0 */
        CPU_INT_DIS();          /* 关中断(临界段 2) */
        /* 临界段 2 开始 */
        {
            /* 执行临界段代码,不可中断 */
        }
        /* 临界段 2 结束 */
        CPU_INT_EN();           /* 开中断(临界段 2) */
    }
    /* 临界段 1 结束 */
    CPU_INT_EN();           /* 开中断(临界段 1) */
}

展开宏定义,代码可等效为:

/* 临界段代码保护 */
{
    CPU_SR	cpu_sr1 = (CPU_SR)0;     /* (a) 定义一个变量,用于存放中断状态,初始化 cpu_sr1 = 0 */
    cpu_sr1 = CPU_SR_Save();         /* (b) cpu_sr1 保存当前中断状态,然后关中断 */
    /* 临界段 1 开始 */
    {
        CPU_SR	cpu_sr2 = (CPU_SR)0;     /* (c) 定义一个变量,用于存放中断状态,初始化 cpu_sr2 = 0 */
        cpu_sr2 = CPU_SR_Save();         /* (d) cpu_sr2 保存当前中断状态,然后关中断 */
        /* 临界段 2 开始 */
        {
            /* 执行临界段代码,不可中断 */
        }
        /* 临界段 2 结束 */
        CPU_SR_Restore(cpu_sr2);         /* (e) cpu_sr2 写入中断状态,恢复之前的中断状态 */
    }
    /* 临界段 1 结束 */
    CPU_SR_Restore(cpu_sr1);            /* (f) cpu_sr1 写入中断状态,恢复之前的中断状态 */
}

这个过程如下:

3 测量关中断时间

本部分先忽略,因为还不是我重点关注的部分。待有时间再来研究。

标签:uCOS,中断,SR,cpu,临界,PRIMASK,III,CPU
来源: https://www.cnblogs.com/Mount256/p/15859588.html