转载自 https://blog.csdn.net/zhoutaopower/article/details/106550648

Based On FreeRTOS Kernel V10.3.1

1、相关文件

链表结构是 OS 内部经常使用到的,FreeRTOS 自然也不例外,在深入分析各个模块的工作原理之前,首先来分析 FreeRTOS 的链表结构,和链表相关的代码被定义在:

list.h

list.c

2、数据结构

不得不说,FreeRTOS 另一个成功的因素,在于他的代码注释,非常的完备,有的时候,代码、结构等的定义,和具体的场景相关性很强,也就是说,没有分析到更后面的使用场景,那么可能便很难理解当前看到的结构定义;

FreeRTOS 这一点做得非常的棒,它有很多很棒的注释,能够帮助大家在初期能够把握一些全局性的东西;

FreeRTOS 使用双向链表来描述链表结构,FreeRTOS 中定义了 3 个相关的结构:

FreeRTOS 使用双向链表来描述链表结构,FreeRTOS 中定义了 3 个相关的结构:

ListItem_t:用来表示链表中的一个元素;

MiniListItem_t:用来表示链表中初始的那个元素;

List_t:用来表示一个链表;

2.1、ListItem_t

ListItem_t 用于描述链表中的一个元素,它的定义为:

/*
* Definition of the only type of object that a list can contain.
*/
struct xLIST;
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t;

结构体中的各个成员的描述为:

listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE :

这个是新版本加上的,用于链表是否有效的判断,当定义了 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 这个为 1 的时候,链表的这个成员便有效,否则是空定义;初始化的时候,这个占位的 Tag 被设置成为固定的 0x5a5a5a5aUL,作用是,在使用链表的时候,判断这个成员是否可能被踩;

TickType_t xItemValue:

这个成员用于排序,看得出来,被定义成为了 TickType_t 类型,也就是按照时间的值来排序;

struct xLIST_ITEM * configLIST_VOLATILE pxNext:

指向下一个成员的指针;

struct xLIST_ITEM * configLIST_VOLATILE pxPrevious:

指向上一个成员的指针;

void * pvOwner:

指向拥有这个 Item 成员的结构体,通常是描述进程 TCB 的指针;

struct xLIST * configLIST_VOLATILE pxContainer:

指向这个 Item 所在的链表的指针;

listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE:

含义和第一个成员一样

2.2、MiniListItem_t

从名字就看得出来,是一个迷你型的 Item,它的定义为:

struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;

看得出来,它和 ListItem_t 的定义非常类似,关键成员少了 pvOwner、pxContainer;为什么少定义这两个呢?因为这个成员根本用不到这两个,后面我们在谈原因;

2.3、List_t

主角登场,List_t 用于描述一个链表,它的定义如下:

/*
* Definition of the type of queue used by the scheduler.
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
volatile UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;

可以看到,FreeRTOS 对它的注释是:

Definition of the type of queue used by the scheduler

被调度器使用的队列,也就是说进程调度要用到它;

结构体一前一后两个定义不再多说,和前面的一样,为了检测结构是否被踩的可能性;

volatile UBaseType_t uxNumberOfItems:

定义了当前这个链表中有多少个 Item ,增加一个链表元素,这个值加1,反之,减1;

ListItem_t * configLIST_VOLATILE pxIndex:

用于链表遍历的节点,怎么个遍历法,后面马上献上;

MiniListItem_t xListEnd:

用于链表的最后的元素,相当于一个标记;

3、函数

既然数据结构介绍完毕(虽然看上去比较抽象,包括几个不容易理解的数据结构),那么接下来一边分析函数,一边分析数据结构的使用方式;

3.1、vListInitialise

一个链表的初始化函数为:

void vListInitialise( List_t * const pxList )
{
/* The list structure contains a list item which is used to mark the
end of the list. To initialise the list the list end is inserted
as the only list entry. */
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ /* The list end value is the highest possible value in the list to
ensure it remains at the end of the list. */
pxList->xListEnd.xItemValue = portMAX_DELAY; /* The list end next and previous pointers point to itself so we know
when the list is empty. */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ pxList->uxNumberOfItems = ( UBaseType_t ) 0U; /* Write known values into the list if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}

传入一个表征链表的结构体指针 List_t * const pxList

请注意一点,在 List_t 结构中,用于标记链表最后的 xListEnd 结构是一个定义,而不是指针,这里首先将传入链表的

pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );  // MiniListItem_t 结构强转

强转为 ListItem_t 结构,并赋值给了 pxIndex,也就是给 pxIndex 内容为这个 List 的 xListEnd 的地址;

接下来便将 xListEnd 的 xItemValue 写入最大值 0xFFFFFFFF(32位CPU);

然后便将 xListEnd 的 next 和 prev 指针全部指向它自己,已达到初始化的目的;

最后初始化该链表中有效元素的个数为 0 个,即 uxNumberOfItems = ( UBaseType_t ) 0U;

最后是,如果使能了 Check 链表有效性的那个宏,那么这里给链表结构的前后两个 TAG 位赋值成为固定的 0x5a5a5a5aUL;

初始化过程为:

3.2、vListInitialiseItem

初始化一个链表元素:

void vListInitialiseItem( ListItem_t * const pxItem )

它的实现非常简单:

void vListInitialiseItem( ListItem_t * const pxItem )
{
/* Make sure the list item is not recorded as being on a list. */
pxItem->pxContainer = NULL; /* Write known values into the list item if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

仅仅是将元素的容器指针给赋值成为 NULL;

3.3、vListInsertEnd

这个 API 是往指定的链表的后部插入一个 Item:

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex; /* Only effective when configASSERT() is also defined, these tests may catch
the list data structures being overwritten in memory. They will not catch
data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); /* Insert a new list item into pxList, but rather than sort the list,
makes the new list item the last item to be removed by a call to
listGET_OWNER_OF_NEXT_ENTRY(). */
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious; /* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY(); pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem; /* Remember which list the item is in. */
pxNewListItem->pxContainer = pxList; ( pxList->uxNumberOfItems )++;
}

入参 pxList 是指定的链表指针,pxNewListItem 是待插入的 Item;

前面两行不多说了,配置用于检查链表和 Item 被踩的 Tag;

首先获取链表的 pxIndex 结构指针,此指针在链表初始化的时候,是指向了 xListEnd;

所以对于一个新的链表创建后,依次插入两个元素 NewItem_1 和 NewItem_2 的过程如下所示:

可以看到,pxIndex 始终处于最开始的那个 xListEnd 结构,而 xListEnd 结构在 List 中有定义成为只有 next 和 prev 的结构,这就凸显出 FreeRTOS 在设计的时候,一点点空间都在节约的精益求精的准则;

3.4、vListInsert

这个 API 和前一个不同的是,前一个是插入到尾部,这个 API 按照 Item 中的 xItemValue 排序插入,xItemValue 越大,越靠近 xListEnd:

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; /* Only effective when configASSERT() is also defined, these tests may catch
the list data structures being overwritten in memory. They will not catch
data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); /* Insert the new list item into the list, sorted in xItemValue order.
If the list already contains a list item with the same item value then the
new list item should be placed after it. This ensures that TCBs which are
stored in ready lists (all of which have the same xItemValue value) get a
share of the CPU. However, if the xItemValue is the same as the back marker
the iteration loop below will not end. Therefore the value is checked
first, and the algorithm slightly modified if necessary. */
if( xValueOfInsertion == portMAX_DELAY )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
/* *** NOTE ***********************************************************
If you find your application is crashing here then likely causes are
listed below. In addition see https://www.freertos.org/FAQHelp.html for
more tips, and ensure configASSERT() is defined!
https://www.freertos.org/a00110.html#configASSERT
1) Stack overflow -
see https://www.freertos.org/Stacks-and-stack-overflow-checking.html
2) Incorrect interrupt priority assignment, especially on Cortex-M
parts where numerically high priority values denote low actual
interrupt priorities, which can seem counter intuitive. See
https://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition
of configMAX_SYSCALL_INTERRUPT_PRIORITY on
https://www.freertos.org/a00110.html
3) Calling an API function from within a critical section or when
the scheduler is suspended, or calling an API function that does
not end in "FromISR" from an interrupt.
4) Using a queue or semaphore before it has been initialised or
before the scheduler has been started (are interrupts firing
before vTaskStartScheduler() has been called?).
**********************************************************************/ for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */
{
/* There is nothing to do here, just iterating to the wanted
insertion position. */
}
} pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem; /* Remember which list the item is in. This allows fast removal of the
item later. */
pxNewListItem->pxContainer = pxList; ( pxList->uxNumberOfItems )++;
}

如果 xItemValue 为 portMAX_DELAY(32bit CPU 中,这个值是 0xFFFFFFFF),那么直接插入到 xListEnd 右边:

否则,按照从 xListEnd 开始,从最右边遍历,从最右往左,依次排列 xItemValue 最小的,比如;

3.5、uxListRemove

该接口用于将链表中的特定元素摘除:

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the list
item. */
List_t * const pxList = pxItemToRemove->pxContainer; pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; /* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY(); /* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
} pxItemToRemove->pxContainer = NULL;
( pxList->uxNumberOfItems )--; return pxList->uxNumberOfItems;
}

首先从指定元素中的 pxContainer 获取到该元素所属的链表结构;再将元素从链表中摘除;

4、宏

除了上面几个常用到的 API,在 list.h 中还以宏的方式提供了一些常用的宏以及宏函数,下面我们依次看下:

4.1、listSET_LIST_ITEM_OWNER、listGET_LIST_ITEM_OWNER

listSET_LIST_ITEM_OWNER 用于设置一个 Item 的 Owner:

/*
* Access macro to set the owner of a list item. The owner of a list item
* is the object (usually a TCB) that contains the list item.
*
* \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
* \ingroup LinkedList
*/
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )

listGET_LIST_ITEM_OWNER 用于获取一个 Item 的 Owner:

/*
* Access macro to get the owner of a list item. The owner of a list item
* is the object (usually a TCB) that contains the list item.
*
* \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
* \ingroup LinkedList
*/
#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )

4.2、listSET_LIST_ITEM_VALUE、listGET_LIST_ITEM_VALUE

listSET_LIST_ITEM_VALUE 用于设置 Item 的 xItemValue 值:

/*
* Access macro to set the value of the list item. In most cases the value is
* used to sort the list in descending order.
*
* \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE
* \ingroup LinkedList
*/
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )

listGET_LIST_ITEM_VALUE 用于获取 Item 的 xItemValue 值:

/*
* Access macro to retrieve the value of the list item. The value can
* represent anything - for example the priority of a task, or the time at
* which a task should be unblocked.
*
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
* \ingroup LinkedList
*/
#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )

4.3、listGET_ITEM_VALUE_OF_HEAD_ENTRY

listGET_ITEM_VALUE_OF_HEAD_ENTRY 用于获取指定链表的 Entry 的 xItemValue,这里我们需要知道 Entry 的定义,其实就是 xListEnd->pxNext 的那个元素:

/*
* Access macro to retrieve the value of the list item at the head of a given
* list.
*
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
* \ingroup LinkedList
*/
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )

4.4、listGET_HEAD_ENTRY

listGET_HEAD_ENTRY 用于获取指定链表的 Entry 的 Item 指针:

/*
* Return the list item at the head of the list.
*
* \page listGET_HEAD_ENTRY listGET_HEAD_ENTRY
* \ingroup LinkedList
*/
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )

4.5、listGET_NEXT

listGET_NEXT 用户获取传入 Item 的 next 指针:

/*
* Return the next list item.
*
* \page listGET_NEXT listGET_NEXT
* \ingroup LinkedList
*/
#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )

4.6、listGET_END_MARKER

listGET_END_MARKER 用来获取指定链表的 xListEnd 标记位置:

/*
* Return the list item that marks the end of the list
*
* \page listGET_END_MARKER listGET_END_MARKER
* \ingroup LinkedList
*/
#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )

4.7、listLIST_IS_EMPTY

listLIST_IS_EMPTY 用来获取指定链表中是否有 Item,主要是通过查看链表的数据统计的变量来获取:

/*
* Access macro to determine if a list contains any items. The macro will
* only have the value true if the list is empty.
*
* \page listLIST_IS_EMPTY listLIST_IS_EMPTY
* \ingroup LinkedList
*/
#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )

4.7、listCURRENT_LIST_LENGTH

listCURRENT_LIST_LENGTH 获取链表元素的个数:

/*
* Access macro to return the number of items in the list.
*/
#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )

4.8、listGET_OWNER_OF_NEXT_ENTRY

listGET_OWNER_OF_NEXT_ENTRY 用于获取指定链表的下一个 TCB 结构:

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )                                        \
{ \
List_t * const pxConstList = ( pxList ); \
/* Increment the index to the next item and return the item, ensuring */ \
/* we don't return the marker used at the end of the list. */ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
} \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
}

4.9、listGET_OWNER_OF_HEAD_ENTRY

listGET_OWNER_OF_HEAD_ENTRY 用于获取 Entry 节点的 Owner:

/*
* Access function to obtain the owner of the first entry in a list. Lists
* are normally sorted in ascending item value order.
*
* This function returns the pxOwner member of the first item in the list.
* The pxOwner parameter of a list item is a pointer to the object that owns
* the list item. In the scheduler this is normally a task control block.
* The pxOwner parameter effectively creates a two way link between the list
* item and its owner.
*
* @param pxList The list from which the owner of the head item is to be
* returned.
*
* \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY
* \ingroup LinkedList
*/
#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )

4.10、listIS_CONTAINED_WITHIN

listIS_CONTAINED_WITHIN 用于判断给定的 Item 是否属于一个指定的链表:

/*
* Check to see if a list item is within a list. The list item maintains a
* "container" pointer that points to the list it is in. All this macro does
* is check to see if the container and the list match.
*
* @param pxList The list we want to know if the list item is within.
* @param pxListItem The list item we want to know if is in the list.
* @return pdTRUE if the list item is in the list, otherwise pdFALSE.
*/
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) )

4.11、listLIST_ITEM_CONTAINER

listLIST_ITEM_CONTAINER 用于获取 Item 属于的链表结构:

/*
* Return the list a list item is contained within (referenced from).
*
* @param pxListItem The list item being queried.
* @return A pointer to the List_t object that references the pxListItem
*/
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer )

4.12、listLIST_IS_INITIALISED

listLIST_IS_INITIALISED 用于判断一个指定的链表是否被初始化过:

/*
* This provides a crude means of knowing if a list has been initialised, as
* pxList->xListEnd.xItemValue is set to portMAX_DELAY by the vListInitialise()
* function.
*/
#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )

到这里,链表相关的函数和结构,以及常用到的宏也就分析完毕了,更多的在后面分析任务以及任务调度的时候,会经常涉及这些;

值得注意的一点是,List 中的 xListEnd 作为一个 marker,是不动的,而 pxIndex 成员,用来做链表遍历,初始化的时候,指向 xListEnd 位置,但是每次调用 listGET_OWNER_OF_NEXT_ENTRY 后,pxIndex 都会往当前的 pxIndex->pxNext 索引一次;这也是为后面的进程相关的逻辑做准备,提高效率;

FreeRTOS --(1)链表的更多相关文章

  1. 【freertos】007-系统节拍和系统延时管理实现细节

    前言 本章节的时钟系统节拍主要分析FreeRTOS内核相关及北向接口层,南向接口层不分析. 本章节的系统延时主要分析任务系统延时实现. 原文:李柱明博客:https://www.cnblogs.com ...

  2. FreeRTOS数据结构(一)--链表和链表项

    结构体定义 /*链表结构体*/ typedef struct xLIST { listFIRST_LIST_INTEGRITY_CHECK_VALUE /*用于链表完整性检查*/ configLIST ...

  3. FreeRTOS链表实现

    直接上源码分析 void vListInitialise( List_t * const pxList ){ pxList->pxIndex = ( ListItem_t * ) &( ...

  4. FreeRTOS 使用指南(转)

    源:FreeRTOS 使用指南 繁星电子开发团队制作 作为一个轻量级的操作系统,FreeRTOS 提供的功能包括:任务管理.时间管理.信号量.消息队列.内存管理.记录功能等,可基本满足较小系统的需要. ...

  5. FreeRTOS初步认识

    源:FreeRTOS初步认识 用了半天时间对FreeRTOS有了一个初步的认识,大概总结一下,其中混杂了系统实现和实际应用方面的问题. 现只是以应用为目的,实现方面待以后进一步研究. 1.FreeRT ...

  6. FreeRTOS代码剖析

    FreeRTOS代码剖析之1:内存管理Heap_1.c   FreeRTOS代码剖析之2:内存管理Heap_2.c   FreeRTOS(V8.0.1)系统之xTaskGenericCreate() ...

  7. FreeRTOS的内存管理

    FreeRTOS提供了几个内存堆管理方案,有复杂的也有简单的.其中最简单的管理策略也能满足很多应用的要求,比如对安全要求高的应用,这些应用根本不允许动态内存分配的. FreeRTOS也允许你自己实现内 ...

  8. FreeRTOS 动态内存管理

    以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面 ...

  9. FreeRTOS内存管理

    简介 Freertos的内存管理分别在heap_1.c,heap_2.c,heap_3.c,heap_4.c,heap_5.c个文件中,选择合适的一种应用于嵌入式项目中即可. 本文的图片中 红色部分B ...

随机推荐

  1. 输入URL回车之后,究竟发生了什么

    https://blog.csdn.net/androidstarjack/article/details/107031771 在浏览器输入URL回车之后发生了什么?(超详细版)   前言 这个问题已 ...

  2. Java 中怎么打印数组?

    你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法来打印数组.由 于数组没有实现 toString() 方法,所以如果将数组传递给 System.ou ...

  3. 有哪些类型的通知(Advice)?

    Before - 这些类型的 Advice 在 joinpoint 方法之前执行,并使用 @Before 注解标记进行配置. After Returning - 这些类型的 Advice 在连接点方法 ...

  4. 您对 Mike Cohn 的测试金字塔了解多少?

    Mike Cohn 提供了一个名为 Test Pyramid 的模型.这描述了软件开发所需的自 动化测试类型. 根据金字塔,第一层的测试数量应该最高.在服务层,测试次数应小于单元测试 级别,但应大于端 ...

  5. Saltstack自动化扩容

    一. etcd服务的安装和使用 1.安装etcd应用: wget https://github.com/coreos/etcd/releases/download/v2.2.5/etcd-v2.2.5 ...

  6. Socket.io+Notification实现浏览器消息推送

    前言 socket.io: 包含对websocket的封装,可实现服务端和客户端之前的通信.详情见官网(虽然是英文文档,但还是通俗易懂).Notification: Html5新特性,用于浏览器的桌面 ...

  7. web页面性能优化之接口前置

    上个Q做了一波web性能优化,积累了一点点经验 记录分享一下. 先分享一个比较常用的接口前置 的优化方案吧 优化前首屏秒开大约在40%左右 首屏秒开大约提高了25% 先发一张优化成果图 前置原因 对于 ...

  8. 深入HTTP协议

    一.HTTP定义 超文本传输协议(HTTP)是一种通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器. HTTP是一个属于应用层的面向对象协议,由于其简捷.快速的方式, ...

  9. token的工作原理及其功能

    一.前言 登录模块是我们在前端项目中一定会有的模块,模块中有一个重要的部分是用户登录验证,对于验证用户是否登录过,我们直接处理办法是检查缓存中是否存在token值,若存在则用户可直接登录,反之,用户需 ...

  10. java继承时能包括静态的变量和方法吗?举例说明!

    子类继承了超类定义的所有实例变量和方法包括静态的变量和方法(马克-to-win见下例),并且为它自己增添了独特的元素.子类只能有一个超类.Java不支持多超类的继承. 子类拥有超类的所有成员,但它不能 ...