freeRTOS简单学习及协程实现理解

freeRtos是开源免费的嵌入式操作系统,其官方网址http://www.freertos.org。最近刚刚看了LiteOS的代码,所以想顺便了解下freeRtos的架构差异。从代码情况上讲,freeRtos的源代码中提供了更多和不同平台相关的代码,为了方便了解,选择基于cortexM4为基础的STM32F407的平台代码来加深了解。
freeRtos提供的功能
作为嵌入式操作系统,freeRtos提供了任务、消息队列、事件、信号量、互斥锁、软件定时器等基础功能,另外为了解决问题方便,freeRtos同样具有越界检查、trace机制、任务耗时统计等辅助功能。在freeRtos的使用手册中有对函数接口、具体事例等实际使用说明。freeRtos提供了协程co-routine。
关于系统启动
在freeRtos代码中,一般启动文件名带有startup或boot字样。在启动代码中进行中断向量服务程序初始化,对于cortexM4的中断向量0-15为内部中断,16-255位外部IRQ,在示例中除了0-15中断服务程序初始化外,外部的FLASH、DMA、I2C等等也做了初始化。在系统初始化后,进入reset_handler函数,然后跳转到main执行。在main函数中进行硬件等初始化,启动操作系统运行。
freeRtos的实现
对于freeRots的使用接口及文件组织形式在其提供的手册中都有简要说明。freeRtos操作系统的文件按照功能划分,task.c实现任务/线程的功能;list.c实现链表功能;queue.c实现消息队列及信号量; timer.c实现了软件定时器的功能;event_goups.c实现了事件;而croutine.c则提供了协程的使用。从代码上看,个人觉得没有liteOS代码看起来舒服,太多的宏编译条件以及对齐问题,可能支持平台功能较多原因?
本质上操作系统的很多实现大同小异,基本都是基于链表调度。在freeRtos中的任务也存在执行、挂起、就绪、阻塞等状态,在具体实现上会存在些差别,如freeRtos中task优先级0表示最低优先级,而在liteOS中是最高优先级等等。
对于上下文的切换freeRtos也是通过中断服务程序PendSV来辅助实现,同样的还有SysTick的实现是通过SysTickHandler中断服务程序来进行计算的。不同于LiteOS,freeRtos提供了协程功能,这个想去重点理解下。
协程实现理解
什么是协程?其主要工作是什么?协程又有叫做用户级线程的,线程或任务是可以保留上下文的,而协程我觉得通俗讲就是可维护上下文现场的调用函数。也就是后一次调用是基于前一次调用的基础上执行新的结果。实现协程功能需要功能:保存执行现场;恢复现场继续执行。保存现场通常需要存储空间,全局变量、static等方式。而恢复现场在c语言协程实现有goto、达夫设备(duff's device:亦即switch...case方式,利用case条件在switch的子程序块中仍然有效的特性)。在freeRtos中选择了使用达夫设备的特性。
数据结构:freeRtos为协程定义了一个单独的控制块结构
typedef struct corCoRoutineControlBlock
{
	crCOROUTINE_CODE 	pxCoRoutineFunction;
	ListItem_t			xGenericListItem;	/*< List item used to place the CRCB in ready and blocked queues. */
	ListItem_t			xEventListItem;		/*< List item used to place the CRCB in event lists. */
	UBaseType_t 		uxPriority;			/*< The priority of the co-routine in relation to other co-routines. */
	UBaseType_t 		uxIndex;			/*< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */
	uint16_t 			uxState;			/*< Used internally by the co-routine implementation. */
} CRCB_t; /* Co-routine control block.  Note must be identical in size down to uxPriority with TCB_t. */
其中crCOROUTINE_CODE 为协程入口函数指针。xGenericListItem、xEventListItem用来维护多个协程调用的链表及触发事件,处理上和任务类似;uxPriority为优先级;uxIndex用来区分调用同一处理函数的不同协程,也就是同一函数支持作为多个协程的入口;uxSate保留协程函数的执行状态,在再次调用协程函数时根据保留状态恢复执行。不同协程函数间可以通过消息队列交互crQUEUE_SEND与crQUEUE_RECEIVE。
找到一个freeRtos代码实现协程的例子,prvFlashCoRoutine协程函数实现的相关部分代码:
//... ...
/* Co-routines MUST start with a call to crSTART. */
crSTART( xHandle );
for( ;; )
{
    crQUEUE_SEND( xHandle, xFlashQueue, ( void * ) &uxIndex, crfPOSTING_BLOCK_TIME, &xResult );
    if( xResult != pdPASS )
    {
        xCoRoutineFlashStatus = pdFAIL;
    }

    crDELAY( xHandle, xFlashRates[ uxIndex ] );
}
crEND();
//... ...
分解找到函数实现中使用到的宏定义:
#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult )            \
{                                                                                        \
    *( pxResult ) = xQueueCRSend( ( pxQueue) , ( pvItemToQueue) , ( xTicksToWait ) );    \
    if( *( pxResult ) == errQUEUE_BLOCKED )                                                \
    {                                                                                    \
        crSET_STATE0( ( xHandle ) );                                                    \
        *pxResult = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), 0 );                    \
    }                                                                                    \
    if( *pxResult == errQUEUE_YIELD )                                                    \
    {                                                                                    \
        crSET_STATE1( ( xHandle ) );                                                    \
        *pxResult = pdPASS;                                                                \
    }                                                                                    \
}

#define crDELAY( xHandle, xTicksToDelay )                                                \
    if( ( xTicksToDelay ) > 0 )                                                            \
    {                                                                                    \
        vCoRoutineAddToDelayedList( ( xTicksToDelay ), NULL );                            \
    }                                                                                    \
    crSET_STATE0( ( xHandle ) );
    
#define crSTART( pxCRCB ) switch( ( ( CRCB_t * )( pxCRCB ) )->uxState ) { case 0:
#define crEND() }
#define crSET_STATE0( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2):
#define crSET_STATE1( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1):

#define pdFALSE            ( ( BaseType_t ) 0 )
#define pdTRUE            ( ( BaseType_t ) 1 )

#define pdPASS            ( pdTRUE )
#define pdFAIL            ( pdFALSE )
将宏替换得到函数完整表达形式
switch( ( ( CRCB_t * )( xHandle ) )->uxState ) { case 0:;
    for( ;; )
    {
        *( &xResult  ) = xQueueCRSend( ( xFlashQueue) , ( &uxIndex) , ( crfPOSTING_BLOCK_TIME ) );
        if( *( &xResult  ) == errQUEUE_BLOCKED )
        {
            ( ( CRCB_t * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2):;
            *(&xResult) = xQueueCRSend( ( pxQueue ), ( &uxIndex), 0 );
        }
        if( *(&xResult) == errQUEUE_YIELD )
        {
            ( ( CRCB_t * )( xHandle ) )->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1):;
            *(&xResult) = 1;
        }
        if( xResult != 1 )
        {
            xCoRoutineFlashStatus = 0;
        }

        if( ( xFlashRates[ uxIndex ]) > 0 )
        {
            vCoRoutineAddToDelayedList( (  xFlashRates[ uxIndex ] ), NULL );
        }
        ( ( CRCB_t * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2):;
    }
};
清除掉实际代码,只留下状态转换的代码,得到结果如下:
switch( ( ( CRCB_t * )( xHandle ) )->uxState ) { case 0:;
    for( ;; )
    {
        ...
        if(... )
        {
            ( ( CRCB_t * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2):;    //line1
            ...
        }
        if( ...)
        {
            ( ( CRCB_t * )( xHandle ) )->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1):;
            ...
        }
        ...
        ( ( CRCB_t * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2):; //line2
    }
};
可以看出freeRtos中使用了达夫设备的特性,并巧妙使用__LINE__行号来作为状态变量,动态得到状态机的值,虽然line1和line2代码看起来相同,但由于__LINE__不一样,最终得到的编译结果亦不雷同。问题:为什么需要使用__LINE__*2和__LINE*2+1两类状态,使用场景?在协程执行过程中,根据CRCB_t(独占的存储空间)里面的状态机选择每次开始执行的位置,并将状态在退出函数之前进行更新。
备注:
关于__weak使用
使用 __weak 定义的函数将其弱化。__weak 声明函数,但随后没有使用 __weak 对其进行定义,则此函数与非弱函数的行为相同。如果有非弱化的同名函数,则链接到非弱化的函数,如果没有其他定义,则弱化函数和正常函数没有差别。

你可能感兴趣的:(操作系统)