互斥信号量的等待与通知

目录

等待互斥信号量

信号量未被占用

信号量被自己占用

信号量被高优先级任务占用

信号量被低优先级任务占用

释放互斥信号量

未发生优先级继承

发生优先级继承


等待互斥信号量

信号量未被占用
  • 标记为已经被占用
  • 锁定计数+1
信号量 自己 占用
  • 锁定计数+1
信号量被高优先级任务占用
  • 低优先级任务插入事件控制块的等待队列中
信号量 低优先级 任务 占用
  • 高优先级任务插入到等待队列中
  • 低优先级任务设置成高优先级任务一样的优先级

释放互斥信号量

未发生优先级继承
  • 释放信号量
  • 从等待队列中唤醒一个任务占用信号量
发生优先级继承
  • 低优先级任务从信号量中释放,不再占用信号量,同时低优先级任务优先级改为原有的优先级
  • 从等待队列中唤醒一个任务占用信号量

tMutex.c

cpp 复制代码
#include "tinyOS.h"

/* 互斥信号量初始化函数 */
void tMutexInit(tMutex *mutex)
{
	tEventInit(&mutex->event, tEventTypeMutex);
	
	mutex->lockedCount = 0;
	mutex->owner = (tTask *)0;
	mutex->ownerOriginalPrio = TINYOS_PRO_COUNT;//初始设为无效值
}

/* 等待互斥信号量函数 */
//参数:互斥信号量,超时时间
uint32_t tMutexWait(tMutex *mutex, uint32_t waitTicks)
{
	uint32_t status = tTaskEnterCritical();
	
	//互斥信号量是否被锁定
	if(mutex->lockedCount <= 0)
	{
		//未锁定:当前任务可以占用互斥信号量
		mutex->owner = currentTask;
		mutex->ownerOriginalPrio = currentTask->prio;
		mutex->lockedCount++;
		
		tTaskExitCritical(status);
		return tErrorNoError;
	}
	else
	{
		//已锁定:
		//判断是否是当前任务锁定的
		if(mutex->owner == currentTask)
		{
			mutex->lockedCount++;
			tTaskExitCritical(status);
			return tErrorNoError;
		}
		else
		{
			//不是当前任务锁定:
			//判断当前任务优先级和互斥信号量占有者优先级哪个高
			if(currentTask->prio < mutex->owner->prio)
			{
				//当前任务优先级高:
				//任务优先级继承机制
				tTask *owner = mutex->owner;
				//判断当前任务是否为就绪状态
				if(owner->state == TINYOS_TASK_STATE_RDY)
				{
					//当前任务为就绪状态:
					tTaskSchedUnRdy(owner);//从原有就绪队列中移出
					owner->prio = currentTask->prio;//更改所有者优先级
					tTaskSchedRdy(owner);//插入新的队列
				}
				else
				{
					owner->prio = currentTask->prio;
				}
			}
			tEventWait(&mutex->event, currentTask, (void *)0, tEventTypeMutex, waitTicks);//当前任务插入事件控制块中
			tTaskExitCritical(status);
			
			tTaskSched();
			return currentTask->waitEventResult;
		}
	}
}

/* 无等待获取互斥信号量函数 */
//仅需检查互斥信号量能否被当前任务获取到
uint32_t tMutexNoWaitGet(tMutex *mutex)
{
	uint32_t status = tTaskEnterCritical();
	
	//判断互斥信号量是否被锁定
	if(mutex->lockedCount <= 0)
	{
		//没有被锁定:由当前任务锁定
		mutex->owner = currentTask;
		mutex->ownerOriginalPrio = currentTask->prio;
		mutex->lockedCount++;
		
		tTaskExitCritical(status);
		return tErrorNoError;
	}
	else
	{
		//被锁定:
		//判断互斥信号量所有者是否是当前任务
		if(mutex->owner == currentTask)
		{
			mutex->lockedCount++;
			tTaskExitCritical(status);
			return tErrorNoError;
		}
		tTaskExitCritical(status);
		return tErrorResourceUnavailable;
	}
}

/* 释放互斥信号量函数 */
uint32_t tMutexNotify(tMutex *mutex)
{
	uint32_t status = tTaskEnterCritical();
	
	//判断信号量是否被锁定
	if(mutex->lockedCount <= 0)
	{
		tTaskExitCritical(status);
		return tErrorNoError;
	}
	
	//判断信号量所有者
	if(mutex->owner != currentTask)
	{
		tTaskExitCritical(status);
		return tErrorOwner;
	}
	
	//对锁定计数--仍大于0:没有到最终释放任务的过程
	if(--mutex->lockedCount > 0)
	{
		tTaskExitCritical(status);
		return tErrorNoError;
	}
	
	//判断是否发生优先级继承
	if(mutex->ownerOriginalPrio != mutex->owner->prio)
	{
		//发生优先级继承:
		//判断任务是否在就绪状态
		if(mutex->owner->state == TINYOS_TASK_STATE_RDY)
		{
			//更改任务所在就绪列表位置及优先级
			tTaskSchedUnRdy(mutex->owner);
			currentTask->prio = mutex->ownerOriginalPrio;
			tTaskSchedUnRdy(mutex->owner);
		}
		else
		{
			currentTask->prio = mutex->ownerOriginalPrio;
		}
	}
	
	//判断当前等待队列中是否有任务
	if(tEventWaitCount(&mutex->event) > 0)
	{
		tTask *task = tEventWakeUp(&mutex->event, (void *)0, tErrorNoError);//取出一个任务
		
		//信号量的所有者设置为新任务
		mutex->owner = task;
		mutex->ownerOriginalPrio = task->prio;
		mutex->lockedCount++;
		
		//判断任务的优先级是否比当前任务的优先级高
		if(task->prio < currentTask->prio)
		{
			tTaskSched();
		}
	}
	tTaskExitCritical(status);
	return tErrorNoError;
}

tMutex.h

cpp 复制代码
#ifndef __TMUTEX_H
#define __TMUTEX_H

#include "tEvent.h"

/* 互斥信号量结构 */
typedef struct _tMutex
{
	tEvent event;								//事件控制块
	uint32_t lockedCount;				//锁定计数器
	tTask *owner;								//当前互斥信号量所有者
	uint32_t ownerOriginalPrio;	//所有者原始优先级
}tMutex;

void tMutexInit(tMutex *mutex);
uint32_t tMutexWait(tMutex *mutex, uint32_t waitTicks);
uint32_t tMutexNoWaitGet(tMutex *mutex);
uint32_t tMutexNotify(tMutex *mutex);

#endif

tintOS.h

cpp 复制代码
#ifndef __TINYOS_H
#define __TINYOS_H

#include <stdint.h>
#include "tLib.h"
#include "tConfig.h"
#include "tTask.h"
#include "tEvent.h"
#include "tSem.h"
#include "tMbox.h"
#include "tMemBlock.h"
#include "tFlagGroup.h"
#include "tMutex.h"

/* 错误码 */
typedef enum _tError{
	tErrorNoError = 0,				//没有错误发生
	tErrorTimeout,						//超时
	tErrorResourceUnavailable,//资源不可用
	tErrorDel,								//被删除
	tErrorResourceFull,				//资源已满
	tErrorOwner,							//拥有者错误
}tError;

extern tTask *currentTask;			
extern tTask *nextTask;				

uint32_t tTaskEnterCritical(void);
void tTaskExitCritical(uint32_t status);

void tTaskSwitch(void);		//和CPU相关,写在switch.c
void tTaskRunFirst(void);

void tTaskSchedInit(void);
void tTaskSchedDisable(void);
void tTaskSchedEnable(void);
void tTaskSchedRdy(tTask *task);
void tTaskSchedUnRdy(tTask *task);
void tTaskSchedRemove(tTask *task);
void tTaskSched(void);
void tTimeTaskWait(tTask *task, uint32_t ticks);
void tTimeTaskWakeUp(tTask *task);
void tTimeTaskRemove(tTask *task);
void tTaskSystemTickHandler(void);
void tTaskDelay(uint32_t delay);


void tSetSysTickPeriod(uint32_t ms);
void tInitApp(void);

	
#endif

tEvent.c

cpp 复制代码
#include "tinyOS.h"

/* 事件控制块初始化函数 */
void tEventInit(tEvent *event, tEventType type)
{
	event->type = tEventTypeUnknow;
	tListInit(&event->waitList);
}

/* 事件控制块等待函数 */
//参数:事件控制块,任务,消息(传入消息来源,在事件发生以后存放具体的消息),等待的状态,超时时间
void tEventWait(tEvent *event, tTask *task, void *msg, uint32_t state, uint32_t timeout)
{
	uint32_t status = tTaskEnterCritical();
		
	task->state |= state << 16;
	task->waitEvent = event;
	task->eventMsg = msg;
	task->waitEventResult = tErrorNoError;
	
	tTaskSchedUnRdy(task);//移出就绪队列
	
	tListAddLast(&event->waitList, &task->linkNode);//插入事件控制块等待队列的尾部
	
	if(timeout)
	{
		tTimeTaskWait(task, timeout);//设置了超时事件,插入延时队列
	}
	
	tTaskExitCritical(status);
}


/* 事件控制块通知函数(将任务从事件控制块中唤醒,唤醒队列首部任务) */
//参数:事件控制块,消息,唤醒结果
tTask *tEventWakeUp(tEvent *event, void *msg, uint32_t result)
{
	tNode *node;
	tTask *task = (tTask *)0;
	
	uint32_t status = tTaskEnterCritical();
		
	if((node = tListRemoveFirst(&event->waitList)) != (tNode *)0)
	{
		task = (tTask *)tNodeParent(node, tTask, linkNode);//插入到事件控制块是用linknode
		task->waitEvent = (tEvent *)0;//不等待任何事件
		task->eventMsg = msg;
		task->waitEventResult = result;
		task->state &= ~TINYOS_TASK_WAIT_MASK;
		
		if(task->delayTicks != 0)//有延时
		{
			tTimeTaskWakeUp(task);//强制将任务从延时队列中移除
		}
		tTaskSchedRdy(task);//插入就绪队列
	}
	
	tTaskExitCritical(status);
	
	return task;
}

/* 事件控制块通知函数(将任务从事件控制块中唤醒,唤醒队列中的指定任务) */
//参数:事件控制块,指定唤醒的任务,消息,唤醒结果
tTask *tEventWakeUpTask(tEvent *event, tTask *task, void *msg, uint32_t result)
{
	uint32_t status = tTaskEnterCritical();
	
	tListRemove(&event->waitList, &task->linkNode);//直接将任务移出队列
	task->waitEvent = (tEvent *)0;//不等待任何事件
	task->eventMsg = msg;
	task->waitEventResult = result;
	task->state &= ~TINYOS_TASK_WAIT_MASK;
		
	if(task->delayTicks != 0)//有延时
	{
		tTimeTaskWakeUp(task);//强制将任务从延时队列中移除
	}
	tTaskSchedRdy(task);//插入就绪队列
	
	tTaskExitCritical(status);
	
	return task;
}

/* 事件控制块移除函数 */
void tEventRemoveTask(tTask *task, void *msg, uint32_t result)
{
	uint32_t status = tTaskEnterCritical();
	
	tListRemove(&task->waitEvent->waitList, &task->linkNode);
	task->waitEvent = (tEvent *)0;
	task->eventMsg = msg;
	task->waitEventResult = result;
	task->state &= ~TINYOS_TASK_WAIT_MASK;

	tTaskExitCritical(status);
}

/* 事件控制块清空函数 */
//返回值:事件任务块被清空时,它的等待队列中有多少任务
uint32_t tEventRemoveAll(tEvent *event, void *msg, uint32_t result)
{
	tNode *node;
	uint32_t count = 0;
	
	uint32_t status = tTaskEnterCritical();
	
	count = tListCount(&event->waitList);//等待队列中有多少任务
	
	while((node = tListRemoveFirst(&event->waitList)) != (tNode *)0)//移除等待队列头部任务
	{
		tTask *task = (tTask *)tNodeParent(node, tTask, linkNode);//获取task结构
		task->waitEvent = (tEvent *)0;//不再等待事件
		task->eventMsg = msg;
		task->waitEventResult = result;
		task->state &= ~TINYOS_TASK_WAIT_MASK;
		
		if(task->delayTicks != 0)//任务有延时
		{
			tTimeTaskWakeUp(task);//移出延时队列
		}
		
		tTaskSchedRdy(task);
	}
	
	tTaskExitCritical(status);
	
	return count;
}

/* 获取事件控制块中等待任务函数 */
uint32_t tEventWaitCount(tEvent *event)
{
	uint32_t count = 0;
	
	uint32_t status = tTaskEnterCritical();
	
	count = tListCount(&event->waitList);
	
	tTaskExitCritical(status);
	
	return count;
}

app.c

cpp 复制代码
#include "tinyOS.h"
#include "string.h"

//定义任务,分别为它们配备独立的堆栈空间
tTask tTask1;
tTask tTask2;
tTask tTask3;
tTask tTask4;
tTaskStack task1Env[1024];
tTaskStack task2Env[1024];
tTaskStack task3Env[1024];
tTaskStack task4Env[1024];

tMutex mutex;

//定义任务要执行的功能
int task1Flag;
void task1Entry(void *param)
{
	tSetSysTickPeriod(10);//初始化
	
	tMutexInit(&mutex);
	
	for(;;)//任务里是for的死循环
	{
		//嵌套的申请互斥信号量再释放
		tMutexWait(&mutex, 0);
		tMutexWait(&mutex, 0);
		
		task1Flag = 0; 
		tTaskDelay(1);
		task1Flag = 1;
		tTaskDelay(1);
		
		tMutexNotify(&mutex);
		tMutexNotify(&mutex);
	}
}

int task2Flag;
void task2Entry(void *param)
{
	for(;;)
	{
		tMutexWait(&mutex, 0);
		tMutexWait(&mutex, 0);
		
 		task2Flag = 0;
		tTaskDelay(1);
		task2Flag = 1;
		tTaskDelay(1);
		
		tMutexNotify(&mutex);
		tMutexNotify(&mutex);
	}
}
int task3Flag;
void task3Entry(void *param)
{
	for(;;)
	{
		task3Flag = 0;
		tTaskDelay(1);
		task3Flag = 1;
		tTaskDelay(1);
	}
}
int task4Flag;
void task4Entry(void *param)
{
	for(;;)
	{
		task4Flag = 0;
		tTaskDelay(1);
		task4Flag = 1;
		tTaskDelay(1);
	}
}

/* 应用任务初始化函数 */
void tInitApp(void)
{
	//最后一个参数:传堆栈末端地址,因为堆栈是向下生长的,初始堆栈地址是堆栈空间最后一个单元地址的末端
	tTaskInit(&tTask1, task1Entry, (void *)0x11111111, 0, &task1Env[1024]);
	tTaskInit(&tTask2, task2Entry, (void *)0x22222222, 1, &task2Env[1024]);
	tTaskInit(&tTask3, task3Entry, (void *)0x22222222, 1, &task3Env[1024]);
	tTaskInit(&tTask4, task4Entry, (void *)0x22222222, 1, &task4Env[1024]);
}
相关推荐
智者知已应修善业2 小时前
【51单片机用数码管显示流水灯的种类是按钮控制数码管加一和流水灯】2022-6-14
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
智商偏低8 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen9 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森11 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白11 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D12 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术15 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt15 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘16 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang16 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c