freertos(第七课,semaphore, mutex, msg queue)

freertos是一个多进程操作系统。
多进程的一个重要控制,就是进程同步。
大多数的OS,都会基于PV操作完成进程同步。
基于EVENT的进程同步,
event在OS中,被表示为一个结构体对象,最简单的情况下,只有一个数据成员。
需要同步的进程,分别向OS申请P操作或者申请V操作,并告知OS,所锚定的event对象。
OS检查event的状态,并对发出申请的进程执行相应的状态切换。
申请P操作的进程,将被OS尝试阻塞,如果event状态允许进程继续执行,则不阻塞,而允许进程继续执行。如果event状态不允许进程继续执行,则进程被OS阻塞。
申请V操作的进程,将使OS尝试唤醒被阻塞到event上的进程。
可以看出,申请P操作的进程,会对自身的状态产生影响。申请V操作的进程,则会对其他的进程的状态产生影响。
在协作模型中,下游总是需要等待上游完成,所以,下游等待,上游唤醒。
在生产者消费者协作模型中,消费者总是需要等待生产者完成。所以,消费者等待,生产者和唤醒。

互斥是进程同步的一个特例。
在这个协作模型中,进程自己首先是消费者,然后自己又是生产者。
由于令牌模型更符合互斥的场景,所以通常用令牌模型来描述互斥。
进程首先要获得一个令牌,用完之后,再将令牌归还。其他进程如果没有获得令牌,则需要等待。当令牌被一个进程归还之后,OS会唤醒其他正在等待令牌的进程。

ISR和TASK的同步,是常见的场景。
ISR生产event,而TASK则消费event。

freertos提供了一系列的sem API。

SemaphoreHandle_t xSemaphoreCreateBinary(void);
SemaphoreHandle_t 
xSemaphoreCreateCounting(
		UBaseType_t uxMaxCount, 
		UBaseType_t uxInitialCount
		);

UBaseType_t uxSemaphoreGetCount(xSemaphore);


BaseType_t xSemaphoreGive(xSemaphore);
BaseType_t 
xSemaphoreGiveFromISR(
		xSemaphore, 
		BaseType_t* pxHigherPriorityTaskWoken
		);

BaseType_t xSemaphoreTake(xSemaphore, TickType_t xBlockTime);
BaseType_t
xSemaphoreTakeFromISR(
		xSemaphore, 
		BaseType_t* pxHigherPriorityTaskWoken
		);

二值信号量,一般用于进程同步,或者ISR和TASK的同步。
计数信号量,一般用于共享资源的缓冲区统计。
互斥量,一般用于共享资源的独立访问。

信号量在RTOS中,会引起优先级反转问题。
当低优先级任务获取信号量后,如果高优先级任务也需要这个信号量,则需要被阻塞,最终,实际上高优先级任务被降低到更低的级别了。
解决办法就是互斥量。
当任务获取一个互斥量时,它的优先级会被OS暂时提高到和所有等待该互斥量的任务中的最高优先级。从而保证了,任务在互斥访问时,不会被抢占。
当任务归还互斥量时,优先级会被OS恢复到之前的级别。
这被称为优先级继承。
优先级继承不能解决优先级反转,但是能够将危害降到最低。

freertos提供了一系列的API。

SemaphoreHandle_t xSemaphoreCreateMutex(void);
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);

BaseType_t xSemaphoreTake(xSemaphore, TickType_t xBlockTime);
BaseType_t xSemaphoreGive(xSemaphore);

BaseType_t xSemaphoreTakeRecursive(xSemaphore, TickType_t xBlockTime);
BaseType_t xSemaphoreGiveRecursive(xSemaphore);

信号量和互斥量,只能用于进程间的同步,并不能传递更多的数据。
freertos,提供了msgqueue,用来在实现进程同步的同时,传递数据。

进程间通信,或者ISR和TASK之间通信,如果没有msgqueue,则只能通过全局变量来进行,但是这有个问题,就是资源管理的问题。
freertos,提供了msgqueue来替代全局变量传递数据。
当消息被填充到msgqueue中时,是复制了一个副本的,这样做的代价是,数据复制的CPU时间消耗,但是带来的好处是,数据存放在专属位置,与原始缓冲区无关。如果向将原始缓冲区传递给其他进程,则只需要将缓冲区的指针发送到消息队列中即可。队列中只需要存放指针副本,节省了空间。
msgqueue是OS的公共资源,它不属于某个特定的进程,任何进程都可以向队列发送消息,或者提取消息。

以下,将消息队列简称为队列,freertos中,也是简称为Queue的。
进程向OS申请队列服务时,实际上也同时向OS申请了POV操作(P-Operating-V)。
即,首先是OS判断是否执行P操作,如果进程没有被阻塞,才会继续执行数据复制的操作。在数据复制完成后,OS会执行V操作,将阻塞的其他进程唤醒。
在发送方向上,
当进程向OS申请发送消息时,OS首先会尝试阻塞进程,如果队列缓冲区有空闲位置,则不阻塞进程,允许进程继续执行后续的数据复制操作。在执行完后,OS会唤醒WAITFORRECEIVELIST中的一个进程,让它能够取走队列中的消息。
如果队列缓冲区满,则阻塞进程,将进程插入WAITFORSENDLIST中。
在接收方向上,
当进程向OS申请接收消息时,OS首先会尝试阻塞进程,如果队列缓冲区中有消息,则不阻塞进程,允许进程继续执行后续的数据复制操作。在执行完成后,OS会唤醒WAITFORSENDLIST中的一个进程,让它能够填充队列中的空闲位置。
如果队列缓冲区空,则阻塞进程,将进程插入WAITFORRECEIVELIST中。

freertos提供了一系列API。

QueueHandle_t 
xQueueCreate(
		UBaseType_t uxQueueLength,
		UBaseType_t uxItemSize
		);
void vQueueDelete(QueueHandle_t xQueue);


BaseType_t 
xQueueSend(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		TickType_t xTicksToWait
		);

BaseType_t 
xQueueSendToFront(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		TickType_t xTicksToWait
		);

BaseType_t 
xQueueOverwrite(
		QueueHandle_t xQueue,
		const void* pvItemToQueue
		);
		
BaseType_t 
xQueueSendFromISR(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		BaseType_t * pxHigherPriorityTaskWoken
		);
		
BaseType_t 
xQueueSendToFrontFromISR(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		BaseType_t * pxHigherPriorityTaskWoken
		);
		
BaseType_t 
xQueueOverwriteFromISR(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		BaseType_t * pxHigherPriorityTaskWoken,
		);

BaseType_t 
xQueueReceive(
		QueueHandle_t xQueue,
		void* pvBuffer,
		TickType_t xTicksToWait
		);

BaseType_t 
xQueuePeek(
		QueueHandle_t xQueue,
		void* pvBuffer,
		TickType_t xTicksToWait
		);

BaseType_t 
xQueueReceiveFromISR(
		QueueHandle_t xQueue,
		void* pvBuffer,
		BaseType_t* pxTaskWoken
		);
		
BaseType_t 
xQueuePeekFromISR(
		QueueHandle_t xQueue,
		void* pvBuffer,
		BaseType_t* pxTaskWoken
		);

队列如果是FIFO方式,那么就使用send,如果是LIFO方式,那么就使用sendtofront。
通常在使用时,由多个进程或者ISR向同一个队列中发送消息,而只有一个进程从队列中接收消息,并依次处理。

在进行进程同步时,经常会遇到ISR和TASK同步的情况,这时候,在ISR中就需要使用fromisr版本的API。
这些API在唤醒TASK时,如果发现唤醒的TASK比当前TASK优先级更高,那么就需要触发任务抢占。这个API会给出一个HigherPriorityTaskWoken的status。当函数返回时,需要在ISR中检查status,并调用portYIELD_FROM_ISR,进行任务抢占。
通常的格式如下:

void KEY1_IRQHandler(void)
{
	BaseType_t pxHigherPriorityTaskWoken;
	...
	xQueueSendFromISR(
				my_queue,
				&send_buf,
				&pxHigherPriorityTaskWomen
				);
	portYIELD_FROM_ISR(pxHigherPriorityTaskWomen);
	...
}

FromISR和YIELD_FROM_ISR是配对使用的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值