实时操作系统的一些分析

工程目录

├─Project
│  ├─Listings
│  ├─Objects
│  └─RTE
│      ├─Device
│      │  └─ARMCM3
│      └─_Target_1
├─RTOS
│  ├─include
│  └─source
└─user

明确几个状态:
运行态running、就绪态ready、阻塞态blocked、暂停态suspend
几个链表:
就绪链表Ready List、延时链表Delayed List、挂起列表Pending List

一、汇编的实现

在port.c中实现这三个函数
1.实现StartFirstTask: 设置MSP,并触发svc中断
2.实现SVCHadnler:加载第一个任务的现场并执行
3.实现PendSVHandler:1.保存现场 2.找到就绪列表中优先级最高的任务 3.恢复下一个任务的现场

二、双向链表的实现

list.h

/* 节点结构体定义 */
struct xLIST_ITEM
{
	TickType_t xItemValue;             /* 辅助值,用于帮助节点做顺序排列 */			
	struct xLIST_ITEM *  pxNext;       /* 指向链表下一个节点 */		
	struct xLIST_ITEM *  pxPrevious;   /* 指向链表前一个节点 */	
	void * pvOwner;					   /* 指向拥有该节点的内核对象,通常是TCB */
	void *  pvContainer;		       /* 指向该节点所在的链表 */
};
typedef struct xLIST_ITEM ListItem_t;  /* 节点数据类型重定义 */

/* mini节点结构体定义,作为双向链表的结尾
   因为双向链表是首尾相连的,头即是尾,尾即是头 */
struct xMINI_LIST_ITEM
{
	TickType_t xItemValue;                      /* 辅助值,用于帮助节点做升序排列 */
	struct xLIST_ITEM *  pxNext;                /* 指向链表下一个节点 */
	struct xLIST_ITEM *  pxPrevious;            /* 指向链表前一个节点 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;  /* 最小节点数据类型重定义 */

/* 链表结构体定义 */
typedef struct xLIST
{
	UBaseType_t uxNumberOfItems;    /* 链表节点计数器 */
	ListItem_t *  pxIndex;			/* 链表节点索引指针 */
	MiniListItem_t xListEnd;		/* 链表最后一个节点 */
} List_t;

list.c

/*节点初始化*/
void vListInitialiseItem(ListItem_t* const pxItem){
		pxItem->pvContainer =NULL;
}

/* 链表根节点初始化:
	1.索引指向最后一个节点
	2.将最后一个节点辅助排序值设为最大,确保该节点是最后节点
	3.将尾节点的下一个指针和上一个指针都指向自己
	4.节点计数器初始化为0
*/
void vListInitialise( List_t * const pxList ){
		pxList->pxIndex = (ListItem_t*)&(pxList->xListEnd);
		pxList->xListEnd.xItemValue = portMAX_DELAY;
		pxList->xListEnd.pxNext = (ListItem_t*)&(pxList->xListEnd);
		pxList->xListEnd.pxPrevious = (ListItem_t*)&(pxList->xListEnd);
		pxList->uxNumberOfItems = 0U;
}

/* 将节点插入到链表的尾部 */
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
	ListItem_t * const pxIndex = pxList->pxIndex;

	pxNewListItem->pxNext = pxIndex;
	pxNewListItem->pxPrevious = pxIndex->pxPrevious;
	pxIndex->pxPrevious->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;

	/* 记住该节点所在的链表 */
	pxNewListItem->pvContainer = ( void * ) pxList;

	/* 链表节点计数器++ */
	( pxList->uxNumberOfItems )++;
}


/* 将节点按照升序排列插入到链表 */
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
	ListItem_t *pxIterator;
	
	/* 获取节点的排序辅助值 */
	const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

	/* 寻找节点要插入的位置 */
	if( xValueOfInsertion == portMAX_DELAY )
	{
		pxIterator = pxList->xListEnd.pxPrevious;
	}
	else
	{
		for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );
		     pxIterator->pxNext->xItemValue <= xValueOfInsertion; 
			 pxIterator = pxIterator->pxNext )
		{
			/* 没有事情可做,不断迭代只为了找到节点要插入的位置 */			
		}
	}

	pxNewListItem->pxNext = pxIterator->pxNext;
	pxNewListItem->pxNext->pxPrevious = pxNewListItem;
	pxNewListItem->pxPrevious = pxIterator;
	pxIterator->pxNext = pxNewListItem;

	/* 记住该节点所在的链表 */
	pxNewListItem->pvContainer = ( void * ) pxList;

	/* 链表节点计数器++ */
	( pxList->uxNumberOfItems )++;
}

/* 将节点从链表中删除 */
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
	/* 获取节点所在的链表 */
	List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;

	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

	/* Make sure the index is left pointing to a valid item. */
	if( pxList->pxIndex == pxItemToRemove )
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious;
	}

	/* 初始化该节点所在的链表为空,表示节点还没有插入任何链表 */
	pxItemToRemove->pvContainer = NULL;
	
	/* 链表节点计数器-- */
	( pxList->uxNumberOfItems )--;

	/* 返回链表中剩余节点的个数 */
	return pxList->uxNumberOfItems;
}

main.c验证该数据结构

#include "list.h"
/* 定义链表根节点 */
struct xLIST       List_Test;

/* 定义节点 */
struct xLIST_ITEM  List_Item1;
struct xLIST_ITEM  List_Item2;
struct xLIST_ITEM  List_Item3;
int main(){
    /* 链表根节点初始化 */
    vListInitialise( &List_Test );
    
    /* 节点1初始化 */
    vListInitialiseItem( &List_Item1 );
    List_Item1.xItemValue = 1;
    
    /* 节点2初始化 */    
    vListInitialiseItem( &List_Item2 );
    List_Item2.xItemValue = 2;
    
    /* 节点3初始化 */
    vListInitialiseItem( &List_Item3 );
    List_Item3.xItemValue = 3;
    
    /* 将节点插入链表,按照升序排列 */
    vListInsert( &List_Test, &List_Item2 );    
    vListInsert( &List_Test, &List_Item1 );
    vListInsert( &List_Test, &List_Item3 );    
	while(1);
}

结果如图:
在这里插入图片描述

二、系统调度实现

三、锁机制的实现

调度锁:处于调度锁开和关之间的代码在执行期间是不会被高优先级的任务抢占的,即任务调度被禁止。但和临界段作用不同,中断还是正常执行的。临界段进行了开关中断操作。

void LED1_Task(void const * argument)
{
  for(;;)
  {
   	    HAL_Delay(2000);
		//调度器锁
		vTaskSuspendAll();
		HAL_Delay(5000);
		if(xTaskResumeAll() == pdTRUE)
		{
			taskYIELD(); //立即任务切换
		}
  }
}
void LED2_Task(void const * argument)
{
  for(;;)
  {
        osDelay(100);
		LED.LED_Flip(LED2);
  }
}

没有使用调度锁时,任务2间隔100ms连续快闪,但是低优先级的任务1调用了调度锁开关函数,延时5s,期间任务2不运行。
在task.c中 1.添加函数vTaskSuspendAll 2.修改函数vTaskSwitchContext

void vTaskSuspendAll( void ) //肯定不会是只有一个全局变量++这么简单
{
	++uxSchedulerSuspended; 
}
void vTaskSwitchContext( void )
{
	if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
	{
		/* The scheduler is currently suspended - do not allow a context  switch. */
		xYieldPending = pdTRUE;
	}
	else
	{
		xYieldPending = pdFALSE;
		traceTASK_SWITCHED_OUT();
	...
	}
	...
}

实验测试:

void Task1_Entry( void *p_arg )
{
	for( ;; )
	{
		flag1 = 1;
        delay (100);	
		vTaskSuspendAll();
		delay (500);
		if(xTaskResumeAll() == pdTRUE)
		{
			taskYIELD(); //立即任务切换
		}
		flag1 = 0;
        delay (100);    
	}
}
void Task3_Entry( void *p_arg )
{
	for( ;; )
	{
		flag3 = 1;
	    vTaskDelay( 1 );
		flag3 = 0;
	    vTaskDelay( 1 );
	}
}

不使用调度锁时:
在这里插入图片描述
使用调度锁后,待解决Bug:调度锁关闭不起作用。
在这里插入图片描述
分析发现停止调度不难,难的是怎么恢复调度,需要用到xPendingReadyList和xEventListItem,过于复杂,遂暂时放一放。
定位分析:需要理解时间片调度是怎么引起任务切换的,在debug过程钟发现在任务1里始终无法引起任务切换。找一个完全移植过的程序对比debug.
2024-4-4
经过debug发现,是在xTaskResumeAll中对uxSchedulerSuspended减一时,需要在临界区中操作,在临界区中uxCriticalNesting的值默认为0xAAAAAAAA,而不是0,因此一直无法使能中断,在任务1中死循环。去掉临界区的保护可以暂时解决,至于为什么临界区会出问题,需要继续研究。

所以做个总结,调度锁机制简单的实现方法,就是在挂起任务函数中设置一个自增的变量uxSchedulerSuspended,在 恢复函数中自减这个uxSchedulerSuspended变量,在每次触发PendSV中断进行任务切换时,在切换上下文函数中没有开锁的时候就是找到优先级最高的就绪任务的TCB,而使能了锁时就什么也不干。(倒也不是真的什么也不干,设置xYieldPending = pdTRUE;在滴答中断中判断xYieldPending 为True就出发PendSV中断,上下文切换后又进入到了任务上下文函数,再设置xYieldPending = pdTRUE,感觉像是脱了裤子放屁,咱时没发现他的用处)。

四、二值信号量实现(要实现二值信号量,先实现队列)

初步看源码要用到队列。需要实现四个函数:

xSemaphoreCreateBinary()
xSemaphoreGive()
xSemaphoreTake()
xSemaphoreDelete()

实现流程:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif
其中#define semSEMAPHORE_QUEUE_ITEM_LENGTH		( ( uint8_t ) 0U )
	#define queueQUEUE_TYPE_BINARY_SEMAPHORE	( ( uint8_t ) 3U )
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )

五、内存管理heap4.c

5.1内存初始化

static void prvHeapInit( void )

1.检查首地址是否8字节对齐,如果地址未对齐,将 uxAddress 增加到最接近的下一个对齐边界,并重新计算 xTotalHeapSize 以调整堆的大小,以确保对齐后的地址范围内完全包含原始堆的大小。

	uxAddress = ( size_t ) ucHeap;
	if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
	{
		uxAddress += ( portBYTE_ALIGNMENT - 1 );
		uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
		xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
	}
	pucAlignedHeap = ( uint8_t * ) uxAddress;

2.初始化xstart和pxEnd

	/* xStart is used to hold a pointer to the first item in the list of free
	blocks.  The void cast is used to prevent compiler warnings. */
	xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
	xStart.xBlockSize = ( size_t ) 0;

	/* pxEnd is used to mark the end of the list of free blocks and is inserted
	at the end of the heap space. */
	uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
	uxAddress -= xHeapStructSize;
	uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
	pxEnd = ( void * ) uxAddress;
	pxEnd->xBlockSize = 0;
	pxEnd->pxNextFreeBlock = NULL;

3.初始化堆空间的第一个空闲块

	/* To start with there is a single free block that is sized to take up the
	entire heap space, minus the space taken by pxEnd. */
	pxFirstFreeBlock = ( void * ) pucAlignedHeap;
	pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
	pxFirstFreeBlock->pxNextFreeBlock = pxEnd;

	/* Only one block exists - and it covers the entire usable heap space. */
	xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
	xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;

5.2 内存分配

void *pvPortMalloc( size_t xWantedSize )

1.分配过程开启调度锁,禁止任务调度。
2.利用xBlockAllocatedBit 判断要分配的内存是否太大,满足要求的话就找到第一个合适大小的空闲块

	pxPreviousBlock = &xStart;
	pxBlock = xStart.pxNextFreeBlock;
	while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
	{
		pxPreviousBlock = pxBlock;
		pxBlock = pxBlock->pxNextFreeBlock;
	}

3.找到首地址,看空闲块是否大于heapMINIMUM_BLOCK_SIZE,大的话就一分为二,提高内存块的利用率。

		/* If the block is larger than required it can be split into
		two. */
		if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
		{
			/* This block is to be split into two.  Create a new
			block following the number of bytes requested. The void
			cast is used to prevent byte alignment warnings from the
			compiler. */
			pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
			configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );

			/* Calculate the sizes of two blocks split from the
			single block. */
			pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
			pxBlock->xBlockSize = xWantedSize;

			/* Insert the new block into the list of free blocks. */
			prvInsertBlockIntoFreeList( pxNewBlockLink );
		}

5.3内存释放

1.检验内存是否已经被分配。
2.更新该内存的状态,并禁止调度。
3.将该内存插入到空闲块列表,并恢复调度。

	if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
	{
		if( pxLink->pxNextFreeBlock == NULL )
		{
			/* The block is being returned to the heap - it is no longer
			allocated. */
			pxLink->xBlockSize &= ~xBlockAllocatedBit;

			vTaskSuspendAll();
			{
				/* Add this block to the list of free blocks. */
				xFreeBytesRemaining += pxLink->xBlockSize;
				traceFREE( pv, pxLink->xBlockSize );
				prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
			}
			( void ) xTaskResumeAll();
		}

5.4相邻内存合并

1.找到新空闲块要插入的位置。
2.判断新空闲块的地址是否等于上一个空闲块的尾地址,相等的话就扩大上一个空闲块的大小。

	puc = ( uint8_t * ) pxIterator;
	if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
	{
		pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
		pxBlockToInsert = pxIterator;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

3.判断新空闲块的地址是否等于下一个空闲块的首地址,相等的话就合并。

	/* Do the block being inserted, and the block it is being inserted before
	make a contiguous block of memory? */
	puc = ( uint8_t * ) pxBlockToInsert;
	if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
	{
		if( pxIterator->pxNextFreeBlock != pxEnd )
		{
			/* Form one big block from the two blocks. */
			pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
			pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
		}
		else
		{
			pxBlockToInsert->pxNextFreeBlock = pxEnd;
		}
	}
	else
	{
		pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
	}

六、队列

动态创建队列(即使用自动内存分配heap4.c)

QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )

6.1 队列创建

计算要分配的消息空间大小

	if( uxItemSize == ( UBaseType_t ) 0 )
	{
		/* There is not going to be a queue storage area. */
		xQueueSizeInBytes = ( size_t ) 0;
	}
	else
	{
		/* Allocate enough space to hold the maximum number of items that
		can be in the queue at any time. */
		xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
	}

	pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );

a. 计算消息存储空间起始地址,即跳过结构体Queue_t的大小,再调用prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
b. 设置pxNewQueue->pcHead,pxNewQueue->uxLength,pxNewQueue->uxItemSize 后调用( void ) xQueueGenericReset( pxNewQueue, pdTRUE );初始化队列的状态。
c. xQueueGenericReset在临界段执行:

设置队列的 pcTail 指针,指向队列的尾部。
将队列中等待的消息数量 uxMessagesWaiting 设置为0。
将写指针 pcWriteTo 设置为队列的头部。
将读指针 pcReadFrom 设置为队列的尾部减去一个项目大小的位置。
将接收锁 cRxLock 和发送锁 cTxLock 初始化为 queueUNLOCKED。

6.2 队列接收消息

xQueueReceive

如果队列钟消息数大于0,拷贝数据到buffer。然后判断是否有任务在等待向队列发送消息,有的话就环形优先级最高的任务。

			if( uxMessagesWaiting > ( UBaseType_t ) 0 )
			{
				/* Data available, remove one item. */
				prvCopyDataFromQueue( pxQueue, pvBuffer );
				traceQUEUE_RECEIVE( pxQueue );
				pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1;

				/* There is now space in the queue, were any tasks waiting to
				post to the queue?  If so, unblock the highest priority waiting
				task. */
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
				{
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
					{
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				taskEXIT_CRITICAL();
				return pdPASS;
			}

没有消息就阻塞,超时时间未过队列为空就把当前任务放入xTasksWaitingToReceive链表开始阻塞,写函数恢复此任务后下一轮循环继续尝试读出,直到超时。再解锁对了,恢复调度器。
如果超时时间已过,返回errQUEUE_EMPTY。

6.3 队列发送消息

xQueueGenericSend

如果队列未满或者是覆盖方式,拷贝数据到队列。判断是否有任务在等待队列钟的数据,接触其阻塞状态。
队列已满时,无阻塞时间直接返回,有阻塞时间需要进行设置,然后挂起调度器,队列上锁。
超时时间未过,队列满的话将当前任务放入xTasksWaitingToSend链表和延时链表开始阻塞,接收函数恢复此任务后下一轮循环继续尝试加入队列,直到超时。
超时时间过了就返回errQUEUE_FULL。

BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

	configASSERT( pxQueue );
	configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
	configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
	#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
	{
		configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
	}
	#endif


	/* This function relaxes the coding standard somewhat to allow return
	statements within the function itself.  This is done in the interest
	of execution time efficiency. */
	for( ;; )
	{
		taskENTER_CRITICAL();
		{
			/* Is there room on the queue now?  The running task must be the
			highest priority task wanting to access the queue.  If the head item
			in the queue is to be overwritten then it does not matter if the
			queue is full. */
			if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
			{
				traceQUEUE_SEND( pxQueue );
				xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

				#if ( configUSE_QUEUE_SETS == 1 )
				{
					if( pxQueue->pxQueueSetContainer != NULL )
					{
						if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE )
						{
							/* The queue is a member of a queue set, and posting
							to the queue set caused a higher priority task to
							unblock. A context switch is required. */
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						/* If there was a task waiting for data to arrive on the
						queue then unblock it now. */
						if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
						{
							if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
							{
								/* The unblocked task has a priority higher than
								our own so yield immediately.  Yes it is ok to
								do this from within the critical section - the
								kernel takes care of that. */
								queueYIELD_IF_USING_PREEMPTION();
							}
							else
							{
								mtCOVERAGE_TEST_MARKER();
							}
						}
						else if( xYieldRequired != pdFALSE )
						{
							/* This path is a special case that will only get
							executed if the task was holding multiple mutexes
							and the mutexes were given back in an order that is
							different to that in which they were taken. */
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
				}
				#else /* configUSE_QUEUE_SETS */
				{
					/* If there was a task waiting for data to arrive on the
					queue then unblock it now. */
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							/* The unblocked task has a priority higher than
							our own so yield immediately.  Yes it is ok to do
							this from within the critical section - the kernel
							takes care of that. */
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else if( xYieldRequired != pdFALSE )
					{
						/* This path is a special case that will only get
						executed if the task was holding multiple mutexes and
						the mutexes were given back in an order that is
						different to that in which they were taken. */
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif /* configUSE_QUEUE_SETS */

				taskEXIT_CRITICAL();
				return pdPASS;
			}
			else
			{
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/* The queue was full and no block time is specified (or
					the block time has expired) so leave now. */
					taskEXIT_CRITICAL();

					/* Return to the original privilege level before exiting
					the function. */
					traceQUEUE_SEND_FAILED( pxQueue );
					return errQUEUE_FULL;
				}
				else if( xEntryTimeSet == pdFALSE )
				{
					/* The queue was full and a block time was specified so
					configure the timeout structure. */
					vTaskInternalSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				}
				else
				{
					/* Entry time was already set. */
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		taskEXIT_CRITICAL();

		/* Interrupts and other tasks can send to and receive from the queue
		now the critical section has been exited. */

		vTaskSuspendAll();
		prvLockQueue( pxQueue );

		/* Update the timeout state to see if it has expired yet. */
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
		{
			if( prvIsQueueFull( pxQueue ) != pdFALSE )
			{
				traceBLOCKING_ON_QUEUE_SEND( pxQueue );
				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );

				/* Unlocking the queue means queue events can effect the
				event list.  It is possible that interrupts occurring now
				remove this task from the event list again - but as the
				scheduler is suspended the task will go onto the pending
				ready last instead of the actual ready list. */
				prvUnlockQueue( pxQueue );

				/* Resuming the scheduler will move tasks from the pending
				ready list into the ready list - so it is feasible that this
				task is already in a ready list before it yields - in which
				case the yield will not cause a context switch unless there
				is also a higher priority task in the pending ready list. */
				if( xTaskResumeAll() == pdFALSE )
				{
					portYIELD_WITHIN_API();
				}
			}
			else
			{
				/* Try again. */
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		}
		else
		{
			/* The timeout has expired. */
			prvUnlockQueue( pxQueue );
			( void ) xTaskResumeAll();

			traceQUEUE_SEND_FAILED( pxQueue );
			return errQUEUE_FULL;
		}
	}
}
  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_45281309

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值