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向同一个队列中发送消息,而只有一个进程从队列中接收消息,并依次处理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章