调度器学习笔记一:合作式调度器

   

转载于http://blog.csdn.net/wuhenyouyuyouyu/article/details/53437830

最近看了很多关于调度方面的文章,看的云里雾里,现整理如下,还请大神们多多指教。

    目前MCU大部分是不上操作系统的,基本都是裸编。也就是说,我们自己要去管理任务的调度,举个大家常用的例子:

     main()

    {

//初始化芯片

。。。

//初始化变量,或者其他功能


while(1)

{

task1();

task2();

task3();

......

}

    }

那么while(1)就是个最简单的调度器,切没有优先级、没有复杂的调度策略,其耗费资源少。其缺点就是,当任务多的时候,

或者某个任务执行时间长的时候,无法保证"需要及时响应的任务",无法满足任务的“实时”要求。不过可以对任务进行改造,什么意思呢?

所有任务都是按着状态机方式编写,把长的、大的任务分解为几个小的片段,每次只执行一个片段,如:

void task(void )

{

switch(state_a)

{

case 1:

......
break;

case 2:

......
break;

case 3:

......
break;

.......

}

}

这应该就是合作式调度器一个核心思想,谁也不独占MCU,运行完一个状态就主动让出MCU。但是这种方式的状态机编程,仍然有问题,

什么问题呢?就是假如我的某个任务很复杂,状态比较多,而且状态间调转条件复杂,怎么办?

有个办法,用子状态机办法,就是我的父状态机里面嵌套子状态机,把复杂的任务分解。不过这么处理仍然有问题,就是当嵌套层数递增,所消耗的

栈也越多,同时也不利于状态复用。要解决这个问题,我感觉用指针法状态机,应该是解决本质问题的方法之一。为了引出什么是指针状态机,首先来

看一个基于时间驱动型合作式调度器的例子:


//内核数据类型
typedef struct_Core_struct{

void (*pTask)(void);//任务指针函数

_CORE_UWORD Delay;//任务执行间隔
_CORE_UWORD Period;//任务循环执行间隔

_CORE_UBYTE RunMe;//任务执行标志

}Core_Task;


/******************************************************************************
 * Function:        void Core_Clear_Error(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        内核任务更新函数
 *                  
 * Note:            放到心跳定时器中断中
 *****************************************************************************/
void Core_Update(void)
{
_CORE_UBYTE  Index;

for(Index=0;Index<CORE_MAX_TASKS;Index++)
{
if(core_task_data[Index].pTask)
{
if(core_task_data[Index].Delay==0)
{
core_task_data[Index].RunMe+=0x01;


if(core_task_data[Index].Period)
{
core_task_data[Index].Delay=core_task_data[Index].Period;
}
}
else
{
core_task_data[Index].Delay-=0x01;
}
}
}
}


/******************************************************************************
 * Function:        _CORE_BOOL Core_User_DelayTask
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          1:成功;0:失败
 *
 * Side Effects:    None
 *
 * Overview:        内核调度函数
 *                  
 * Note:            必须在main在主循环里调用(所有任务必须都是非阻塞)
 *****************************************************************************/
void Core_User_Scheduling(void)
{
_CORE_UBYTE  Index=0;


for(Index=0;Index<CORE_MAX_TASKS;Index++)
{
if(core_task_data[Index].RunMe)
{
if(core_task_data[Index].pTask)
{
(core_task_data[Index].pTask());
}


core_task_data[Index].RunMe-=0x01;

if(core_task_data[Index].Period==0)
{
Core_User_DelateTask_Index(Index);
}
}
}
}

这是一个基于时间片调度器关键性代码,Core_Update负责刷新任务队列,Core_User_Scheduling负责

调度。这种调度思想非常适合做周期性任务和一次性任务。假如,有一个通信LED执行灯,当有通讯时候

闪烁,通信结束则灭。那怎么实现这个任务呢?简单通过这个调度器,无法实现。我认为可以增加message

机制,可以解决这个问题。扯远了,通过这个例子,想必大家应该知道什么是基于指针的调度了。有了这个

前提,那么我们把通过改造这个调度算法,同时把任务拆分为一个子状态,同时每个子状态就是一个函数,

就可以实现指针法调度器。

下面是一个例子,例子是从其他网站摘入的代码,如果有异议,我会删除:

//! \brief task control block
typedef struct __task task_t;


//! \brief task prototype
typedef void* task_routine_t(task_t *ptTask);
/*

typedef void* task_routine_t(task_t *ptTask);用的时候task_routine_t *fnRoutine;

typedef void* (*task_routine_t)(task_t *ptTask);用的时候task_routine_t  fnRoutine;

*/

//! \name task control block structure
//! @{
struct __task {
    task_routine_t *fnRoutine;    //!< task routine
    bool bLocked;
    void *pArg;                    //!< task argument
};
//! @}






//! \brief declare a task pool
#ifndef TASK_POOL_SIZE
#define TASK_POOL_SIZE    4
#endif
static task_t s_tTaskPool[TASK_POOL_SIZE] = {0};










bool new_task(task_routine_t *fnRoutine, void *pArg)
{
    uint8_t n;
    bool bResult = false;
    if (NULL == fnRoutine) {
        return bResult;
    }


    //! search for free task. As shared resource involved, atom access is required.
    SAFE_ATOM_CODE(
        for (n = 0; n < UBOUND(s_tTaskPool); n++) {
            if (NULL == s_tTaskPool[n].fnRoutine) {
                s_tTaskPool[n].fnRoutine = fnRoutine;
                s_tTaskPool[n].pArg = pArg;
                s_tTaskPool[n].bLocked = false;
                bResult = true;
                break;
            }
        }
    )


    return bResult;
}






bool scheduler(void)
{
    static uint8_t s_tTaskCounter = 0;
    static uint8_t s_tFreeCounter = 0;
    task_t *ptTask;


    //! access shared resource, atom access is required
    SAFE_ATOM_CODE(
        ptTask = &s_tTaskPool[s_tTaskCounter++];
        if (s_tTaskCounter >= UBOUND(s_tTaskPool)) {
            s_tTaskCounter = 0;
        }


        if (NULL != ptTask->fnRoutine) {
            s_tFreeCounter = 0;
            if (!ptTask->bLocked) {
                ptTask->bLocked = true;
            } else {
                ptTask = NULL;
            } 
        } else {
            s_tFreeCounter++;
            if (UBOUND(s_tTaskPool) <= s_tFreeCounter) {
                s_tFreeCounter = UBOUND(s_tTaskPool);
                EXIT_SAFE_ATOM_CODE();
                return false;
            }
            ptTask = NULL;
        }
    )
    
    if (NULL != ptTask) {
        ptTask->fnRoutine = (task_routine_t *)ptTask->fnRoutine(ptTask);
        SAFE_ATOM_CODE(
            ptTask->bLocked = false;
        )
    }
    
    return true;
}














static void idle_task(void)
{
    sleep();
}


int main(void)
{
    ...
    while(1) {
        if (!scheduler()) {
            //! return false means system is idle, run idle to enter sleep mode.
            idle_task();
        }
    }


    return 0;
}


//! the most amazing part is here
/*! \note this is a simple demonstration for the fact that the scheduler can works well in 
 *! real multi-task system from a simple front/back-end system to multi-task OS environment.
 *! Is it useful? *^_^* Try to think the multi-core and multi-thread programmng model.
 */
ISR(TIM0_COMPA_vect)
{
    //! 1ms compare match interrupt service routine


    //! we can call scheduler both at super loop and ISR simultaneously. this is useful!
    scheduler();
}
...
ISR(ADC_vect)
{


    ...
    scheduler();
}
...

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值