当前位置: 首页 > article >正文

实现rtos操作系统 【二】基本任务切换实现

一、临界区保护

临界区保护是一个重要的概念,用于保护共享资源免受并发访问导致的数据损坏和不一致。临界区是指一段代码,这段代码访问共享资源(如全局变量、硬件设备等),并且在任何时候只能由一个线程执行,以确保操作的原子性和数据的一致性。

有些情况下一些特殊操作(比如 XIP 下 Flash 擦写、低功耗模式切换)不能被随意打断,或者一些共享数据区不能被无序访问(A 任务正在读,B 任务却要写),这时候就要用到临界区保护策略了。

RTOS 内实现的方式很简单,通过禁用中断、使用互斥锁或信号量等方法即可。在这里我们将实现一下禁用中断的临界区保护。

Cortex‐M3 权威指南 41 页标题

 我们只需要通过置位和复位 PRIMASK 寄存器即可实现临界段代码保护,代码如下:

uint32_t tTaskEnterCritical (void) 
{
    __disable_irq();
}

void tTaskExitCritical (void) {
    __set_PRIMASK(0);
}

 可是这样并不能嵌套保护,比如我们连续保护两次,这样在中间那层就开启了保护。

我们必须避免这个问题,解决办法如下:

uint32_t tTaskEnterCritical (void) 
{
    uint32_t primask = __get_PRIMASK();
    __disable_irq();        // CPSID I
    return primask;
}

void tTaskExitCritical (uint32_t status) {
    __set_PRIMASK(status);
}

至此,用我们保护时先获得当前的状态,退出保护时恢复原先的状态就可以了。

二、调度锁保护

调度锁的功能是让 RTOS 停止调度,我们只需要一个变量即可实现。

// 调度锁计数器
uint8_t schedLockCount;

 值得注意的是,我们在任务调度锁中为了安全,都开启了临界区保护。

以下是开启和关闭调度锁的函数:

//禁止任务调度函数
void tTaskSchedDisable (void) 
{
    uint32_t status = tTaskEnterCritical(); 

    if (schedLockCount < 255) 
    {
        schedLockCount++;
    }

    tTaskExitCritical(status);
}
///禁止任务调度函数
void tTaskSchedEnable (void) 
{
    uint32_t status = tTaskEnterCritical();

    if (schedLockCount > 0) 
    {
        if (--schedLockCount == 0) 
        {
            tTaskSched(); 
        }
    }

    tTaskExitCritical(status);
}

 最后在 tTaskSched 函数中进行调度锁判断。

schedLockCount > 0 即不调度

void tTaskSched (void) 
{
    // 进入临界区,以保护在整个任务调度与切换期间,不会因为发生中断导致currentTask和nextTask可能更改
    uint32_t status = tTaskEnterCritical();

    // 如何调度器已经被上锁,则不进行调度,直接退bm
    if (schedLockCount > 0) 
    {
        tTaskExitCritical(status);
        return;
    }
    .....
}

三、任务优先级

3.1 什么是任务优先级

在实时操作系统(Real Time Operating System, RTOS)中,任务优先级是一个重要的概念,它决定了任务被调度器调度的顺序。在多任务系统中,高优先级的任务可以打断低优先级任务的执行,获得CPU的使用权。

比如我们考虑 freeRTOS 的两个任务:

xTaskCreate(LED1_task, "LED1", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
xTaskCreate(LED2_task, "LED2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);

由于他们优先级都是2,所以他们同时运行会按照时间片来回调度。

xTaskCreate(LED1_task, "LED1", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
xTaskCreate(LED2_task, "LED2", configMINIMAL_STACK_SIZE, NULL, 3, NULL);

 如果 LED1 优先级是2,LED2 优先级是 3。那么 LED1 如果没有 rtos 延迟的话,freeRTOS 是不会调度到 LED2 中的。

void LED1_task(void * pvParameters)
{  
    for( ;; ){
        gpio_bit_set(GPIOB, GPIO_PIN_4);
        //vTaskDelay(1000);
    }
}

void LED2_task(void * pvParameters)
{  
    for( ;; ){
        gpio_bit_reset(GPIOB, GPIO_PIN_4);
        vTaskDelay(500);
    }
}

比如这种情况,此时任务即不会调度到 LED2_task 中。只有当我们取消掉 LDE1_TASK 的 vTaskDelay(1000)才会继续调度。

3.2 图数据结构

// 位图类型
typedef struct 
{
	uint32_t bitmap;
}tBitmap;
#include "tLib.h"

/**********************************************************************************************************
** Function name        :   tBitmapInit
** Descriptions         :   初始化bitmap将所有的位全清0
** parameters           :   无
** Returned value       :   无
***********************************************************************************************************/
void tBitmapInit (tBitmap * bitmap) 
{
	bitmap->bitmap = 0;
}

/**********************************************************************************************************
** Function name        :   tBitmapPosCount
** Descriptions         :   返回最大支持的位置数量
** parameters           :   无
** Returned value       :   最大支持的位置数量
***********************************************************************************************************/
uint32_t tBitmapPosCount (void) 
{
	return 32;
}

/**********************************************************************************************************
** Function name        :   tBitmapSet
** Descriptions         :   设置bitmap中的某个位
** parameters           :   pos 需要设置的位
** Returned value       :   无
***********************************************************************************************************/
void tBitmapSet (tBitmap * bitmap, uint32_t pos)
{
	bitmap->bitmap |= 1 << pos;
}

/**********************************************************************************************************
** Function name        :   tBitmapClear
** Descriptions         :   清除bitmap中的某个位
** parameters           :   pos 需要清除的位
** Returned value       :   无
***********************************************************************************************************/
void tBitmapClear (tBitmap * bitmap, uint32_t pos)
{
	bitmap->bitmap &= ~(1 << pos);
}

/**********************************************************************************************************
** Function name        :   tBitmapGetFirstSet
** Descriptions         :   从位图中第0位开始查找,找到第1个被设置的位置序号
** parameters           :   无
** Returned value       :   第1个被设置的位序号
***********************************************************************************************************/
uint32_t tBitmapGetFirstSet (tBitmap * bitmap) 
{
	static const uint8_t quickFindTable[] =     
	{
	    /* 00 */ 0xff, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 10 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 20 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 30 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 40 */ 6,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 50 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 60 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 70 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 80 */ 7,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 90 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* A0 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* B0 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* C0 */ 6,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* D0 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* E0 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* F0 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
	};

	if (bitmap->bitmap & 0xff)
    {
        return quickFindTable[bitmap->bitmap & 0xff];         
    }
    else if (bitmap->bitmap & 0xff00)
    {
        return quickFindTable[(bitmap->bitmap >> 8) & 0xff] + 8;        
    }
    else if (bitmap->bitmap & 0xff0000)
    {
        return quickFindTable[(bitmap->bitmap >> 16) & 0xff] + 16;        
    }
    else if (bitmap->bitmap & 0xFF000000)
    {
        return quickFindTable[(bitmap->bitmap >> 24) & 0xFF] + 24;
    }
    else
    {
        return tBitmapPosCount();
    }
}

其中位图是数据结构本质是一个 uint32_t 的变量。任务优先级中使用任务图数据结构的目的是返回最小的位值。

比如对于二进制 0011 1010 来说,我们就应该返回 1 ,因为最小被置位的 bit 在从右往左第二个。

uint32_t tBitmapGetFirstSet (tBitmap * bitmap) 
{
	static const uint8_t quickFindTable[] =     
	{
	    /* 00 */ 0xff, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 10 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 20 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 30 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 40 */ 6,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 50 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 60 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 70 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 80 */ 7,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 90 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* A0 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* B0 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* C0 */ 6,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* D0 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* E0 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* F0 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
	};

	if (bitmap->bitmap & 0xff)
    {
        return quickFindTable[bitmap->bitmap & 0xff];         
    }
    else if (bitmap->bitmap & 0xff00)
    {
        return quickFindTable[(bitmap->bitmap >> 8) & 0xff] + 8;        
    }
    else if (bitmap->bitmap & 0xff0000)
    {
        return quickFindTable[(bitmap->bitmap >> 16) & 0xff] + 16;        
    }
    else if (bitmap->bitmap & 0xFF000000)
    {
        return quickFindTable[(bitmap->bitmap >> 24) & 0xFF] + 24;
    }
    else
    {
        return tBitmapPosCount();
    }
}

对于这个函数来说,他可以快速的返回最小的置位 bit,比如我们输入十六进制 0x60,首先这个数字与 0XFF 相与为真,也就是说在前 8 比特有置位,首先他会进入这个函数:

	if (bitmap->bitmap & 0xff)
    {
        return quickFindTable[bitmap->bitmap & 0xff];         
    }

然后我们进入查表法,0x60 查表得知为 5。

static const uint8_t quickFindTable[] =     
	{
	    /* 00 */ 0xff, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 10 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 20 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 30 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 40 */ 6,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 50 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 60 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 70 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 80 */ 7,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* 90 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* A0 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* B0 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* C0 */ 6,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* D0 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* E0 */ 5,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
	    /* F0 */ 4,    0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
	};

所以返回 5。我们验证 0x60 二进制为 0110 0000,正好最小的置位就在 5 位。

3.3  通过图实现优先级

首先定义两个数据结构:

// 任务优先级的标记位置结构
tBitmap taskPrioBitmap;

// 所有任务的指针数组:简单起见,只使用两个任务
tTask * taskTable[TINYOS_PRO_COUNT];

之后我们在 tTaskInit 初始化任务时,将图置位。 

void tTaskInit (tTask * task, void (*entry)(void *), void *param, uint32_t prio, uint32_t * stack)
{
    //....之前的代码没有变,忽略

    taskTable[prio] = task;                             // 填入任务优先级表
    tBitmapSet(&taskPrioBitmap, prio);                  // 标记优先级位置中的相应位
}

 tTaskHighestReady函数会返回当前优先级最低的任务。

tTask * tTaskHighestReady (void) 
{
    uint32_t highestPrio = tBitmapGetFirstSet(&taskPrioBitmap);
    return taskTable[highestPrio];
}

 在调度函数 tTaskSched 中,我们始终将 nextTask 指向当前优先级最低的函数。

void tTaskSched (void) 
{
    tTask * tempTask;

    // 进入临界区,以保护在整个任务调度与切换期间,不会因为发生中断导致currentTask和nextTask可能更改
    uint32_t status = tTaskEnterCritical();

    // 如何调度器已经被上锁,则不进行调度,直接退bm
    if (schedLockCount > 0) 
    {
        tTaskExitCritical(status);
        return;
    }

    // 找到优先级最高的任务,如果其优先级比当前任务的还高,那么就切换到这个任务
    tempTask = tTaskHighestReady();
    if (tempTask != currentTask) 
    {
        nextTask = tempTask;
        tTaskSwitch();   
    }

    // 退出临界区
    tTaskExitCritical(status); 
}

四、任务延迟队列

#include "tLib.h"

/**********************************************************************************************************
** Function name        :   tNodeInit
** Descriptions         :   初始化结点类型
** parameters           :   无
** Returned value       :   无
***********************************************************************************************************/
void tNodeInit (tNode * node)
{
    node->nextNode = node;
    node->preNode = node;
}

// 以下是简化代码编写添加的宏
#define firstNode   headNode.nextNode
#define lastNode    headNode.preNode

/**********************************************************************************************************
** Function name        :   tListInit
** Descriptions         :   链表初始化
** parameters           :   无
** Returned value       :   无
***********************************************************************************************************/
void tListInit (tList * list)
{
	list->firstNode = &(list->headNode);
    list->lastNode = &(list->headNode);
    list->nodeCount = 0;
}

/**********************************************************************************************************
** Function name        :   tListCount
** Descriptions         :   返回链表中结点的数量
** parameters           :   无
** Returned value       :   结点数量
***********************************************************************************************************/
uint32_t tListCount (tList * list)
{
	return list->nodeCount;
}

/**********************************************************************************************************
** Function name        :   tListFirst
** Descriptions         :   返回链表的首个结点
** parameters           :   list 查询的链表
** Returned value       :   首个结点,如果链表为空,则返回0
***********************************************************************************************************/
tNode * tListFirst (tList * list)
{
    tNode * node = (tNode *)0;
	
	if (list->nodeCount != 0) 
	{
		node = list->firstNode;
	}    
    return  node;
}

/**********************************************************************************************************
** Function name        :   tListLast
** Descriptions         :   返回链表的最后一个结点
** parameters           :   list 查询的链表
** Returned value       :   最后的结点,如果链表为空,则返回0
***********************************************************************************************************/
tNode * tListLast (tList * list)
{
    tNode * node = (tNode *)0;
	
	if (list->nodeCount != 0) 
	{
		node = list->lastNode;
	}    
    return  node;
}

/**********************************************************************************************************
** Function name        :   tListPre
** Descriptions         :   返回链表中指定结点的前一结点
** parameters           :   list 查询的链表
** parameters           :   node 参考结点
** Returned value       :   前一结点结点,如果结点没有前结点(链表为空),则返回0
***********************************************************************************************************/
tNode * tListPre (tList * list, tNode * node)
{
	if (node->preNode == node) 
	{
		return (tNode *)0;
	} 
	else 
	{
		return node->preNode;
	}
}

/**********************************************************************************************************
** Function name        :   tListnextNode
** Descriptions         :   返回链表中指定结点的后一结点
** parameters           :   list 查询的链表
** parameters           :   node 参考结点
** Returned value       :   后一结点结点,如果结点没有前结点(链表为空),则返回0
***********************************************************************************************************/
tNode * tListNext (tList * list, tNode * node)
{
	if (node->nextNode == node) 
	{
		return (tNode *)0;
	}
	else 
	{
		return node->nextNode;
	}
}

/**********************************************************************************************************
** Function name        :   tListRemoveAll
** Descriptions         :   移除链表中的所有结点
** parameters           :   list 待清空的链表
** Returned value       :   无
***********************************************************************************************************/
void tListRemoveAll (tList * list)
{
    uint32_t count;
    tNode * nextNode;
        
    // 遍历所有的结点
	  nextNode = list->firstNode;
    for (count = list->nodeCount; count != 0; count-- )
    {
    	// 先纪录下当前结点,和下一个结点
    	// 必须纪录下一结点位置,因为在后面的代码中当前结点的next会被重置
        tNode * currentNode = nextNode;
        nextNode = nextNode->nextNode;
        
        // 重置结点自己的信息
        currentNode->nextNode = currentNode;
        currentNode->preNode = currentNode;
    }
    
    list->firstNode = &(list->headNode);
    list->lastNode = &(list->headNode);
    list->nodeCount = 0;
}

/**********************************************************************************************************
** Function name        :   tListAddFirst
** Descriptions         :   将指定结点添加到链表的头部
** parameters           :   list 待插入链表
** parameters			:   node 待插入的结点
** Returned value       :   无
***********************************************************************************************************/
void tListAddFirst (tList * list, tNode * node)
{
    node->preNode = list->firstNode->preNode;
    node->nextNode = list->firstNode;

    list->firstNode->preNode = node;
    list->firstNode = node;
    list->nodeCount++;
}

/**********************************************************************************************************
** Function name        :   tListAddLast
** Descriptions         :   将指定结点添加到链表的末尾
** parameters           :   list 待插入链表
** parameters			:   node 待插入的结点
** Returned value       :   无
***********************************************************************************************************/
void tListAddLast (tList * list, tNode * node)
{
	node->nextNode = &(list->headNode);
    node->preNode = list->lastNode;

    list->lastNode->nextNode = node;
    list->lastNode = node;
    list->nodeCount++;
}

/**********************************************************************************************************
** Function name        :   tListRemoveFirst
** Descriptions         :   移除链表中的第1个结点
** parameters           :   list 待移除链表
** Returned value       :   如果链表为空,返回0,否则的话,返回第1个结点
***********************************************************************************************************/
tNode * tListRemoveFirst (tList * list)
{
    tNode * node = (tNode *)0;

	if( list->nodeCount != 0 )
    {
        node = list->firstNode;

        node->nextNode->preNode = &(list->headNode);
        list->firstNode = node->nextNode;
        list->nodeCount--;
    }
    return  node;
}

/**********************************************************************************************************
** Function name        :   tListInsertAfter
** Descriptions         :   将指定的结点插入到某个结点后面
** parameters           :   list 			待插入的链表
** parameters           :   nodeAfter 		参考结点
** parameters           :   nodeToInsert 	待插入的结构
** Returned value       :   无
***********************************************************************************************************/
void tListInsertAfter (tList * list, tNode * nodeAfter, tNode * nodeToInsert)
{
    nodeToInsert->preNode = nodeAfter;
    nodeToInsert->nextNode = nodeAfter->nextNode;

    nodeAfter->nextNode->preNode = nodeToInsert;
    nodeAfter->nextNode = nodeToInsert;

    list->nodeCount++;
}

/**********************************************************************************************************
** Function name        :   tListRemove
** Descriptions         :   将指定结点从链表中移除
** parameters           :   list 	待移除的链表
** parameters           :   node 	待移除的结点
** Returned value       :   无
***********************************************************************************************************/
void tListRemove (tList * list, tNode * node)
{
    node->preNode->nextNode = node->nextNode;
    node->nextNode->preNode = node->preNode;
    list->nodeCount--;
}

4.1 任务链表化 

typedef struct _tTask {
	// 任务所用堆栈的当前堆栈指针
    tTaskStack * stack;
    // 任务延时计数器
    uint32_t delayTicks;
    // 延时结点:通过delayNode就可以将tTask放置到延时队列中
    tNode delayNode;
    // 任务的优先级
    uint32_t prio;
    // 任务当前状态
    uint32_t state;

 }tTask;

我们为任务结构体添加了一个 tNode delayNode 的属性。

void tTaskInit (tTask * task, void (*entry)(void *), void *param, uint32_t prio, uint32_t * stack)
{
    //...忽略之前的代码

    task->stack = stack;                                // 保存最终的值
    task->delayTicks = 0;
    task->prio = prio;                                  // 设置任务的优先级
    task->state = TINYOS_TASK_STATE_RDY;                // 设置任务为就绪状态

    tNodeInit(&(task->delayNode));                      // 初始化延时结点
    
    taskTable[prio] = task;                             // 填入任务优先级表
    tBitmapSet(&taskPrioBitmap, prio);                  // 标记优先级位置中的相应位
}

 在初始化任务时,即将 tNodeInit(&(task->delayNode)); 进行初始化。

4.2 延迟函数实现

void tTaskDelay (uint32_t delay) {
    // 进入临界区,以保护在整个任务调度与切换期间,不会因为发生中断导致currentTask和nextTask可能更改
    uint32_t status = tTaskEnterCritical();
 
    // 设置延时值,插入延时队列
    tTimeTaskWait(currentTask, delay);
 
    // 将任务从就绪表中移除
    tTaskSchedUnRdy(currentTask);

    // 然后进行任务切换,切换至另一个任务,或者空闲任务
    // delayTikcs会在时钟中断中自动减1.当减至0时,会切换回来继续运行。
    tTaskSched();

    // 退出临界区
    tTaskExitCritical(status); 
}
void tTimeTaskWait (tTask * task, uint32_t ticks)
{
    task->delayTicks = ticks;
    tListAddLast(&tTaskDelayedList, &(task->delayNode)); 
    task->state |= TINYOS_TASK_STATE_DELAYED;
}


void tTimeTaskWakeUp (tTask * task)
{
    tListRemove(&tTaskDelayedList, &(task->delayNode));
    task->state &= ~TINYOS_TASK_STATE_DELAYED;
}

4.3 时钟节拍函数 

任务链表化最终应用就是再 tTaskSystemTickHandler 节拍函数中,通过循环任务链表将任务判断移除和恢复就绪态。

使用链表替代了之前的数组。

void tTaskSystemTickHandler () 
{
    tNode * node;
    
    // 进入临界区,以保护在整个任务调度与切换期间,不会因为发生中断导致currentTask和nextTask可能更改
    uint32_t status = tTaskEnterCritical();
    
    // 检查所有任务的delayTicks数,如果不0的话,减1。
    for (node = tTaskDelayedList.headNode.nextNode; node != &(tTaskDelayedList.headNode); node = node->nextNode)
    {
        tTask * task = tNodeParent(node, tTask, delayNode);
        if (--task->delayTicks == 0) 
        {
            // 将任务从延时队列中移除
            tTimeTaskWakeUp(task);

            // 将任务恢复到就绪状态
            tTaskSchedRdy(task);            
        }
    }
 
     // 退出临界区
    tTaskExitCritical(status); 

    // 这个过程中可能有任务延时完毕(delayTicks = 0),进行一次调度。
    tTaskSched();
}

五、同优先级时间片运行

5.1 初始化任务

此时我们使用 taskTable[prio] 保存任务链表,如果优先级是 1 我们则保存到 taskTable[1] 链表中,插入到最后即可。

void tTaskInit (tTask * task, void (*entry)(void *), void *param, uint32_t prio, uint32_t * stack)
{
    //...

    tNodeInit(&(task->delayNode));                      // 初始化延时结点
    
    tNodeInit(&(task->linkNode));                       // 初始化链接结点
    tListAddLast(&taskTable[prio], &(task->linkNode));  // 插入对应的优先级队列中

    tBitmapSet(&taskPrioBitmap, prio);                  // 标记优先级位置中的相应位
}

5.3 滴答定时器的时间片函数

tTaskSystemTickHandler() 直接被滴答定时器回调,其工作是将所有任务delay延迟都先减 -1。

其次是将当前任务优先级的链表后移一位,做到优先级相同切换时间片效果。

在最后进入任务调度函数接口。

void tTaskSystemTickHandler (void)
{
    tNode * node;
    
    // 进入临界区,以保护在整个任务调度与切换期间,不会因为发生中断导致currentTask和nextTask可能更改
    uint32_t status = tTaskEnterCritical();
    
    // 检查所有任务的delayTicks数,如果不0的话,减1。
    for (node = tTaskDelayedList.headNode.nextNode; node != &(tTaskDelayedList.headNode); node = node->nextNode)
    {
        tTask * task = tNodeParent(node, tTask, delayNode);
        if (--task->delayTicks == 0) 
        {
            // 将任务从延时队列中移除
            tTimeTaskWakeUp(task);

            // 将任务恢复到就绪状态
            tTaskSchedRdy(task);            
        }
    }

    // 检查下当前任务的时间片是否已经到了
    if (--currentTask->slice == 0)
    {
        // 如果当前任务中还有其它任务的话,那么切换到下一个任务
        // 方法是将当前任务从队列的头部移除,插入到尾部
        // 这样后面执行tTaskSched()时就会从头部取出新的任务取出新的任务作为当前任务运行
        if (tListCount(&taskTable[currentTask->prio]) > 0)
        {
            tListRemoveFirst(&taskTable[currentTask->prio]);
            tListAddLast(&taskTable[currentTask->prio], &(currentTask->linkNode));

            // 重置计数器
            currentTask->slice = TINYOS_SLICE_MAX;
        }
    }

     // 退出临界区
    tTaskExitCritical(status); 

    // 这个过程中可能有任务延时完毕(delayTicks = 0),进行一次调度。
    tTaskSched();
}

5.2 任务调度接口

使用 tTaskHighestReady(); 找到优先级最高的任务。

void tTaskSched (void) 
{

    //...
    tTask * tempTask;

    //只要判断不是当前任务,就立即切换过去
    tempTask = tTaskHighestReady();
    if (tempTask != currentTask) 
    {
        nextTask = tempTask;
        tTaskSwitch();   
    }
    //...
}

 tTaskHighestReady() 中先取优先级最低的,然后返回头链表得到任务。

tTask * tTaskHighestReady (void) 
{
    uint32_t highestPrio = tBitmapGetFirstSet(&taskPrioBitmap);
    tNode * node = tListFirst(&taskTable[highestPrio]);
    return (tTask *)tNodeParent(node, tTask, linkNode);
}

5.3 RTOS 延迟函数

延迟函数的工作是将延迟值加入到任务中,并且入队延迟链表。

此外还有将延迟函数从就绪链表移除。

void tTaskDelay (uint32_t delay) {
    // 进入临界区,以保护在整个任务调度与切换期间,不会因为发生中断导致currentTask和nextTask可能更改
    uint32_t status = tTaskEnterCritical();
 
    // 设置延时值,插入延时队列
    tTimeTaskWait(currentTask, delay);
 
    // 将任务从就绪表中移除
    tTaskSchedUnRdy(currentTask);

    // 然后进行任务切换,切换至另一个任务,或者空闲任务
    // delayTikcs会在时钟中断中自动减1.当减至0时,会切换回来继续运行。
    tTaskSched();

    // 退出临界区
    tTaskExitCritical(status); 
}
//将任务加入延时队列中
void tTimeTaskWait (tTask * task, uint32_t ticks)
{
    task->delayTicks = ticks;
    tListAddLast(&tTaskDelayedList, &(task->delayNode)); 
    task->state |= TINYOS_TASK_STATE_DELAYED;
}

//将延时的任务从延时队列中唤醒
void tTimeTaskWakeUp (tTask * task)
{
    tListRemove(&tTaskDelayedList, &(task->delayNode));
    task->state &= ~TINYOS_TASK_STATE_DELAYED;
}

//将任务从就绪列表中移除
void tTaskSchedUnRdy (tTask * task)
{
    tListRemove(&taskTable[task->prio], &(task->linkNode));

    // 队列中可能存在多个任务。只有当没有任务时,才清除位图标记
    if (tListCount(&taskTable[task->prio]) == 0)
    {
        tBitmapClear(&taskPrioBitmap, task->prio);
    }
}


http://www.kler.cn/a/389185.html

相关文章:

  • GISBox VS ArcGIS:分别适用于大型和小型项目的两款GIS软件
  • AI绘画经验(stable-diffusion)
  • AI写作(二)NLP:开启自然语言处理的奇妙之旅(2/10)
  • MySQL数据库:SQL语言入门 【上】(学习笔记)
  • Java中的不可变集合:性能与安全并重的最佳实践
  • mysql 更改 字段长度
  • 大模型,智能家居的春秋战国之交
  • goframe开发一个企业网站 验证码17
  • DotNet使用CsvHelper快速读取和写入CSV文件的操作方法
  • Apache DolphinScheduler + OceanBase,搭建分布式大数据调度平台的实践
  • 2411d,右值与移动
  • linux入门教程:perl库
  • 【刷题12】ctfshow刷题
  • 面试官:进程与线程的关系和区别到底是什么?
  • 大数据学习10之Hive高级
  • 【从零开始的LeetCode-算法】540. 有序数组中的单一元素
  • 营收增长,净利润却暴跌695.9%,昆仑万维在赌什么?
  • 后端-内连接(INNER JOIN),左外连接(LEFT JOIN)和右外连接(RIGHT JOIN)的区别
  • 【Vue】-组件开发-一个树组件
  • jpeg2000dataset.cpp:40:10: fatal error:
  • 【网络安全】Java线程安全及非线程安全
  • 使用GPT-SoVITS训练语音模型
  • 浅谈C++ MFC
  • iview图片放大缩小旋转预览功能
  • 【eNSP】企业网络架构实验——vlan间的路由通信(三)
  • 【万字详解】如何在微信小程序的 Taro 框架中设置静态图片 assets/image 的 Base64 转换上限值