笔者以前一直是裸机写代码,还没有研究过其他的操作系统。以下技巧,获取其他操作系统中也有,但笔者感觉还是很精巧的,因此写下来,供大家分享。同时加快大家读代码的速度。
操作系统使用过程中最重要的就是终端使用和任务调度。
LiteOS在任务建立时,是建立了一个大的任务数组,每个任务按照索引分配一个单独的任务号,最终通过任务号完成对每个任务的查找或者指定。其中比较有意思的是,每个任务的首地址的查找,这个在本文最后写,我们先看任务创建:
代码如下:
LITE_OS_SEC_TEXT_INIT UINT32 osTaskInit(VOID)
{
UINT32 uwSize;
UINT32 uwIndex;
LOS_DL_LIST *pstListObject;
uwSize = (g_uwTskMaxNum + 1) * sizeof(LOS_TASK_CB);
g_pstTaskCBArray = (LOS_TASK_CB *)LOS_MemAlloc(m_aucSysMem0, uwSize); //分配任务序列的空间
.............................
(VOID)memset(g_pstTaskCBArray, 0, uwSize); //清空序列空间
................................
for (uwIndex = 0; uwIndex <= LOSCFG_BASE_CORE_TSK_LIMIT; uwIndex++)
{
g_pstTaskCBArray[uwIndex].usTaskStatus = OS_TASK_STATUS_UNUSED;
g_pstTaskCBArray[uwIndex].uwTaskID = uwIndex; //设置任务编号
LOS_ListTailInsert(&g_stLosFreeTask, &g_pstTaskCBArray[uwIndex].stPendList);
}
..................................................
return LOS_OK;
}
LiteOS中使用了大量的宏定义用来做地址计算,下面我们就看看这个宏定义:
#define LOS_DL_LIST_ENTRY(item, type, member) \
((type *)((char *)item - LOS_OFF_SET_OF(type, member))) \
先看注释:
*@ingroup los_list
*@brief Obtain the pointer to a structure that contains a doubly linked list.
注释说,属于los_list包,用来获取一个包含双向链表的结构指针。
该宏定义还包含了另外一个宏定义
#define LOS_OFF_SET_OF(type, member) ((long)&((type *)0)->member)
这个宏定义的实体是:
((long)&((type *)0)->member)
什么意思呢?
该实体通过(type *)0 把0地址强制转换成type类型,
&(type->member)又获取了type中member的地址,因为从0地址开始算的。那么member的地址也就是member到该type的偏移量了。最后强制类型转换为long类型。
因此该宏定义LOS_OFF_SET_OF(type, member)是获取了 type类型中member的再type中的便宜地址。
下面我们看
#define LOS_DL_LIST_ENTRY(item, type, member) \
((type *)((char *)item - LOS_OFF_SET_OF(type, member))) \
这个宏定义中,通过((char *)item - LOS_OFF_SET_OF(type, member))) 实现一个指针减去一个偏移量,指针位置前移。最后,又加了一个type*的类型转换。
我们最后看看这个宏定义怎么用的:
我们选osTaskScan函数中的调用
for (pstTaskCB = LOS_DL_LIST_ENTRY((pstListObject)->pstNext, LOS_TASK_CB, stTimerList);&pstTaskCB->stTimerList != (pstListObject);)
在这句代码中
LOS_DL_LIST_ENTRY((pstListObject)->pstNext, LOS_TASK_CB, stTimerList)
的 item = (pstListObject)->pstNext
type = LOS_TASK_CB;
member = stTimerList
LOS_TASK_CB的定义如下
typedef struct tagTaskCB
{
VOID *pStackPointer; /**< Task stack pointer */
UINT16 usTaskStatus;
...........
LOS_DL_LIST stPendList;
LOS_DL_LIST stTimerList;
............................
} LOS_TASK_CB;
通过宏定义 LOS_OFF_SET_OF,我们可以得到stTimerList到pStackPointer的偏移量
如果LOS_DL_LIST_ENTRY((pstListObject)->pstNext, LOS_TASK_CB, stTimerList)中
(pstListObject)->pstNext指向 一个LOS_TASK_CB中的stTimerList,那我们是不是就得到了 LOS_TASK_CB的首地址?
我们看看osTaskScan函数中(pstListObject)->pstNext的由来
LITE_OS_SEC_TEXT VOID osTaskScan(VOID)
{
.......
g_stTskSortLink.usCursor = (g_stTskSortLink.usCursor + 1) % OS_TSK_SORTLINK_LEN;
pstListObject = g_stTskSortLink.pstSortLink + g_stTskSortLink.usCursor;
.....
}
通过代码分析,我们可以看到(pstListObject)->pstNext正是指向 一个LOS_TASK_CB中的stTimerList。
至于具体逻辑,大家可以自己看代码,或者等我后续分析。
读懂这个宏定义,能大大加快大家读代码的速度。
希望能对大家有帮助。