zoukankan      html  css  js  c++  java
  • REX系统2

    REX(Real Time Executive)是一个面向嵌入式应用的,简单高效的,抢先式,多任务实时操作系统,支持基于优先级的任务调度算法(支持优先级反转)。它提供了任务控制,任务同步,互斥,定时器和终端控制等API。

      REX所有的函数都在任务上下文环境里执行。

      REX只需要少于5k的ROM控件,需要的RAM空间取决于运行的任务数目加上几k字节的状态数据和堆栈空间。

      REX处理了IRQ中断。

     

    1. 数据定义与宏定义

      1.1 数据结构

      rex.h中定义了REX中的各种数据结构。

      1.1.1 TCB(任务控制块)

      * 用于描述一个REX任务

      * 不能被外部直接访问

      * 由于内核按照排列书序对其进行访问,结构中数据的排列顺序不能更改。

    typedef struct rex_tcb_struct{

      void  *sp;  //堆栈指针

      void  *stack_limit; //堆栈限值

      unsigned long  slices;  //任务的时间片

      rex_sigs_type  sigs;  //当前持有的信号量

      rex_sigs_type  wait;  //等待获取的信号量

      rex_priority_type  pri;  //任务优先级

      #if defined FEATURE_REX_PROFILE

        unsigned long  time_samples;  //profiling information

        unsigned long  max_intlock_time;  //profiling info

      #endif

      #if defined TIMETEST

        word  leds;  //TIMETEST val

      #endif

      #if defined FEATURE_SOFTWARE_PROFILE

        //32 bits counter, ~30 usec/tick, ~35 hours

        dword  numticks;

      #endif

      #ifdef FEATURE_REX_APC

        long  num_apcs;  //APC调用数目

      #endif

      //以上数据域的偏移量已经在rexarm.h中定义。注意保持两者一致

      rex_tcb_link_type  cs_link;  //当等待临街区域时,为非空

      rex_crit_sect_type  *cs_stack[REX_CRIT_SECT_MAX];  //持有和等待临界区的TCB堆栈

      rex_crit_sect_type  **cs_sp;  //临界区堆栈指针

      boolean  suspended;  //任务是否挂起

      char  task_name[REX_TASK_NAME_LEN + 1];

      #if defined FEATURE_REX_EXTENDED_COUNTEXT

        void  *ext;

      #endif

     

      unsigned long  thread_id;

      unsigned long  stack_size;

      //检查task堆栈的使用情况,该特性没有打开

      #ifdef FEATURE_SI_STACK_WM

        unsigned long  stack_wm;

      #endif

      //用于BSD socket数据服务

      #if defined FEATURE_DS_SOCKETS_BSD

        void  *bsdcb_ptr;

      #endif

      int  err_num;  //error code

      //用于在task被阻塞时,通知dog停止监视

      int  dog_report_val;  //dog report id

      int  autodog_enabled;  //dog report enabled ?

      #if defined FEATURE_REX_CREATE_TASK || defined FEATURE_ZREX

        boolean  is_dynamic;

      #endif

      #ifdef FEATURE_REX_IPC

        rex_ipc_info_type  ipc_info;

      #endif

    }rex_tcb_type;

     

    1.1.2 定时器(timer)数据结构

      *描述REX使用的定时器

      *不能被外部直接访问

    typedef struct rex_timer_struct

    {

      struct{

        struct rex_timer_struct  *next_ptr;

        struct rex_timer_struct  *prev_ptr;

      }link;

      rex_timer_cnt_type  cnt;  //当前计数值

      rex_tcb_type  *tcb_ptr;  //指向需要信号的TCB结构

      rex_sigs_type  sigs;  //关联的信号量

      #ifdef FEATURE_REX_TIMER_EX

        rex_timer_cb_type  cb_ptr;  //function called when timer expires

        unsigned long  cb_param;  //arguments to callback function

      #endif

    }rex_timer_type;

     

    1.1.3 临界区(critical section)数据结构

      *提供互斥机制

    typedef struct{

      byte  lock_count;  //  >0 if crit sect is taken

      struct rex_tcb_struct  *owner;  //持有者的TCB指针

      struct rex_tcb_struct  *tcb_link;  //等待队列的头指针

      rex_priority_type  orig_pri;  //原始优先级,为支持优先级反转而提供

    }rex_crit_sect_type;

     

    1.1.4  上下文帧的结构

      *任务的上下文,记录了ARM的各个寄存器的数据

    typedef PACKED struct{

      rex_cpu_register_type  spsr;

      rex_cpu_register_type  r[13];  //r0-r7,r8-r12

      rex_cpu_register_type  lr;  //r14

      rex_cpu_register_type  pc;  //r15

    }rex_context_frame_type;

     

    1.2 几个全局变量

      1.2.1 rex_curr_task

      *当前任务的控制块TCB指针

      rex_tcb_type  *rex_curr_task;

    1.2.2 rex_best_task

      *处于ready状态,优先级最高的task

      *将想要成为rex_curr_task的任务设为rex_best_task,再调用rex_sched()进行任务的上下文切换

      *有些情况下,由于任务调度被枷锁,或处于ISR中端模式,rex_sched()不会被马上进行任务切换。因此,在rex_sched()真正进行调度之前,             rex_best_task可能在不同地方被多次更改。所以在确定是否rex_best_task时,应该将需要切换的任务与rex_best_task进行比较(而不是                 rex_curr_task)。只有当改任务处于ready状态,且优先级比rex_best_task更高时,才允许更新rex_best_task

      

      rex_tcb_type  *rex_best_task;

    1.2.3  rex_task_list

      *任务链表的头结点

      rex_tcb_type  rex_task_list;

     

    1.2.4 rex_num_tasks

      *任务个数

      int rex_num_stasks = 0;

    1.2.5 rex_kernel_tcb

      *任务链表的最末位的节点,及其堆栈

      *一般指向Idle task

      static rex_tcb_type rex_nernel_tcb;

      rex_stack_word_type  rex_kernel_stack[REX_KERNEL_STACK_SIZE/sizeof(rex_stack_word_type)];

     

    1.2.6 rex_sched_allow

      *任务调度室否加锁的标志

      int rex_sched_allow = TRUE; //turns sched on/off

    1.2.7 rex_nest_depth

      *用于支持任务调度的乔涛加锁,记录嵌套层数

      unsigned int rex_nest_depth = 0; //supports nesting of TASKLOCK FREE

    1.2.8 rex_timer_list

      *定时器链表的头节点

      static rex_timer_type rex_timer_list;

    1.2.9 rex_null_timer

      *空定时器

      *定时器链表的最末尾节点

      static rex_timer_type rex_null_timer;

    1.2.10 rex_irq_vector & rex_fiq_vector

      *包含用户定义的ISR中断服务函数的入口点

      void  (*rex_irq_vector)(void);

      void  (*rex_fiq_vector)(void);

    1.3 MACROS(几个宏定义)

    1.3.1  REX_VERSION_NUMBER

      #define REX_VERION_NUMBER ((unsigned long)403)

    1.3.2 任务链表操作宏

      *REX_TASK_LIST_FRONT()  获得任务链表的头结点,多用于链表循环

       *REX_TASK_LIST_NEXT(tcb_ptr)获得指定任务的下一个任务

      * REX_TASK_LIST_PREV(tcb_ptr)获得指定任务的前一个任务

      * REX_TASK_LIST_POP(tcb_ptr)将指定任务从任务链表中移除

       #define REX_TASK_LIST_FRONT()(&rex_task_list)

      #define REX_TASK_LIST_NEXT(tcb_ptr)((rex_tcb_type*)tcb->link.next_ptr)

      #define REX_TASK_LIST_PREV(tcb_ptr)((rex_tcb_type*)tcb_ptr->link.prev_ptr)

     1.3.3 REX_TASK_RUNNABLE(tcb)

      *判断指定任务是否处于ready状态

      *判据: 1.任务是否挂起; 2.任务是否在等待进入临界区 3.任务是否在等待信号量;4.是否有APC队列需要处理

       #define REX_TASK_RUNNABLE(tcb)((tcb->suspended ==FALSE)&&(tcb->cs_link.next_ptr==NULL)&&((tcb->wait == 0)|| (tcb->num_apcs)>0)))

     

      1.3.4 看门狗操作宏

      *REX_PAUSE_DOG_MONITOR(tcb_ptr)通知DOG停止监视该任务

      *REX_RESUME_DOG_MONITOR(tcb_ptr)通知DOG恢复对该任务的监视

      #define REX_PAUSE_DOG_MONITOR(tcb_ptr)

      {

        if((tcb_ptr->autodog_enabled)&&(tcb_ptr->dog_report_val>=0))

        {

          dog_monitor_pause(tcb_ptr->dog_report_val);

        }

      }

      #define REX_RESUME_DOG_MONITOR(tcb_ptr)

      {

        if(tcb_ptr->dog_report_val>=0)

        {

          dog_monitor_resume(tcb_ptr->dog_report_val);

        }

      }

    2. 任务(TASK)

      REX把task当做一个个独立的入口函数,每个task都拥有各自的堆栈,优先级,这些共同构成了任务的上下文。每个任务都有一个相关联的数据结构,成为TCB(任务控制块)。

      REX允许在执行任意时刻创建任意数目的task。实际上,每增加一个任务,由于遍历更长的任务链表,REX性能会有轻微的下降。需要小心控制任务的数目。

      *任务堆栈:

        每个任务都有用自己的堆栈,在运行时被使用。当任务挂起时(如运行其他任务或进行中断服务),任务的寄存器会被压入任务对战中,并将栈顶指针保存在任务TCB里。等到任务被选中再次运行时,从TCB里获取栈顶指针,将任务的寄存器值从栈顶弹出,任务于是从上次被中断的位置继续运行。这些任务切换的处理对于任务来说是透明的(可以参考【第三章  调度】)

     

    2.1 创建任务

      2.1.1 rex_def_task_internal

      定义和创建一个任务

      *定义初始的任务上下文,初始化TCB结构信息

      *将任务按优先级顺序插入到rex_task_list中

      *若是该任务优先级比rex_best_task更高且没有挂起,则进行任务调度

      void rex_def_task_internal(
    rex_tcb_type *p_tcb, /* valid tcb for new task */
    unsigned char* p_stack, /* stack for new task */
    rex_stack_size_type p_stksiz, /* stack size in bytes */
    rex_priority_type p_pri, /* priority for new task */
    rex_task_func_type p_task, /* task startup function */
    dword p_param, /* parameter for new task */
    char *p_tskname, /* A/N string for task name */
    boolean p_susp, /* is task initially suspended? */
    void *p_parent, /* opaque handle to container */
    boolean p_dynamic, /* stack/tcb alloc'd via dyna mem */
    int dog_report_val /* Dog report value */
    )
    {
    word index = 0;
    byte *stack_ptr = NULL;
    rex_context_frame_type *cf_ptr = NULL;
    /*-------------------------------------------------------
    ** Task stack pointer points to the bottom of allocated
    ** stack memory. p_stksiz is the number of 8-bit bytes.
    **-----------------------------------------------------*/
    stack_ptr = (byte *)((unsigned long)p_stack + (unsigned long)p_stksiz - sizeof(unsigned long) );
    /*-------------------------------------------------------
    ** Creates room for the context.
    ** sp points to the top of the context frame.
    **-----------------------------------------------------*/
    stack_ptr -= sizeof( rex_context_frame_type );

    /*-------------------------------------------------------
    ** Defines the initial context.
    ** 设置任务的pc、lr为通用任务入口函数rex_task_preamble(),其参数为
    ** p_task、p_param。
    **-----------------------------------------------------*/
    cf_ptr = (rex_context_frame_type*)stack_ptr;
    cf_ptr->spsr.val = PSR_Supervisor | PSR_Thumb;
    cf_ptr->r[0].task = p_task;
    cf_ptr->r[1].arg = p_param;
    cf_ptr->r[10].arg = (unsigned long)p_stack;
    cf_ptr->lr.preamble = rex_task_preamble;
    cf_ptr->pc.preamble = rex_task_preamble;
    /* ------------------------------------------------------
    ** Initialize the task control block (TCB)
    ** ------------------------------------------------------ */
    p_tcb->sp = stack_ptr;
    p_tcb->stack_limit = p_stack;
    p_tcb->stack_size = p_stksiz;
    p_tcb->slices = 0;
    p_tcb->sigs = 0;
    p_tcb->wait = 0;
    p_tcb->pri = p_pri;
    p_tcb->cs_link.next_ptr = NULL;
    p_tcb->cs_link.prev_ptr = NULL;
    p_tcb->cs_sp = p_tcb->cs_stack; - -p_tcb->cs_sp;
    p_tcb->suspended = p_susp;
    #ifdef FEATURE_SI_STACK_WM
    rex_swm_init( p_tcb );
    #endif /* FEATURE_SI_STACK_WM */
    p_tcb->task_name[REX_TASK_NAME_LEN] = '';
    if (p_tskname != NULL) /* copy task name if one was supplied */
    {
    /* copy bytes until /0 received or enough chars have been copied */
    while ( (p_tcb->task_name[index] = p_tskname[index] ) &&
    ( index++ < REX_TASK_NAME_LEN ) );;
    }
    #if defined FEATURE_REX_APC
    p_tcb->num_apcs = 0; /* Number of queued APCs */
    #endif

    /*-------------------------------------------------------
    ** Defines the initial context.
    ** 设置任务的pc、lr为通用任务入口函数rex_task_preamble(),其参数为
    ** p_task、p_param。
    **-----------------------------------------------------*/
    cf_ptr = (rex_context_frame_type*)stack_ptr;
    cf_ptr->spsr.val = PSR_Supervisor | PSR_Thumb;
    cf_ptr->r[0].task = p_task;
    cf_ptr->r[1].arg = p_param;
    cf_ptr->r[10].arg = (unsigned long)p_stack;
    cf_ptr->lr.preamble = rex_task_preamble;
    cf_ptr->pc.preamble = rex_task_preamble;
    /* ------------------------------------------------------
    ** Initialize the task control block (TCB)
    ** ------------------------------------------------------ */
    p_tcb->sp = stack_ptr;
    p_tcb->stack_limit = p_stack;
    p_tcb->stack_size = p_stksiz;
    p_tcb->slices = 0;
    p_tcb->sigs = 0;
    p_tcb->wait = 0;
    p_tcb->pri = p_pri;
    p_tcb->cs_link.next_ptr = NULL;
    p_tcb->cs_link.prev_ptr = NULL;
    p_tcb->cs_sp = p_tcb->cs_stack; - -p_tcb->cs_sp;
    p_tcb->suspended = p_susp;
    #ifdef FEATURE_SI_STACK_WM
    rex_swm_init( p_tcb );
    #endif /* FEATURE_SI_STACK_WM */
    p_tcb->task_name[REX_TASK_NAME_LEN] = '';
    if (p_tskname != NULL) /* copy task name if one was supplied */
    {
    /* copy bytes until /0 received or enough chars have been copied */
    while ( (p_tcb->task_name[index] = p_tskname[index] ) &&
    ( index++ < REX_TASK_NAME_LEN ) );;
    }
    #if defined FEATURE_REX_APC
    p_tcb->num_apcs = 0; /* Number of queued APCs */
    #endif

    p_tcb->link.prev_ptr = tcb_ptr->link.prev_ptr;
    p_tcb->link.next_ptr = tcb_ptr;
    tcb_ptr->link.prev_ptr->link.next_ptr = p_tcb;
    tcb_ptr->link.prev_ptr = p_tcb;
    }
    #ifdef FEATURE_REX_IPC
    if (ipcns_node_register(p_tcb) == FALSE)
    {
    return;
    }
    #endif
    /*---------------------------------------------------
    ** Make this task the best task if it is higher
    ** priority than the present best task.
    **---------------------------------------------------*/
    /* Always compare with REX_BEST_TASK, not REX_CURR_TASK */
    if ( (p_pri > rex_best_task->pri) && (p_tcb->suspended == FALSE) )
    {
    rex_best_task = p_tcb;
    /* swap the task in */
    rex_sched();
    }
    rex_num_tasks++;
    REX_INTFREE();
    return;
    }

      相关的API: rex_def_task()、rex_def_task_ext()、rex_def_task_ext2()

    2.2. 任务的通用引导函数

      2.2.1. rex_task_preamble() 

      *每个新创建的任务在第一次运行时,都会首先执行这个函数。这样做的好处是可以处理任务入口函数返回的情况(在这里,会将该任务直接删除)。

      *只能由REX内部调用

      void rex_task_preamble(
      void (*func_ptr)( dword arg ),
          dword arg
      )

      {
        func_ptr( arg );
        /* if we return, kill the task */
        rex_kill_task( rex_self() );
      } /* END rex_task_preamble */

    2.3. 任务挂起和继续

      2.3.1. rex_suspend_task()

      *挂起一个任务,使其不再接受调度

      *如果挂起的是当前任务,则要进行一次任务调度

    void rex_suspend_task( rex_tcb_type *p_tcb)
    {
    p_tcb->suspended = TRUE;
    REX_INTLOCK();
    if ( ( p_tcb == rex_curr_task ) && !rex_is_in_irq_mode( ) )
    {
    rex_set_best_task( REX_TASK_LIST_FRONT() );
    rex_sched( );
    }
    REX_INTFREE();
    return;
    } /* END rex_suspend_task */

      2.3.2. rex_resume_task

      *使任务重新接受调度

      *若该任务优先级比rex_best_task更高,则进行任务调度

    void rex_resume_task( rex_tcb_type *p_tcb)
    {
    REX_INTLOCK();
    /* basic sanity check to see if we should even be here or not */
    if (p_tcb->suspended == TRUE)
    {
    p_tcb->suspended = FALSE;
    if ((p_tcb->pri > rex_best_task->pri) && REX_TASK_RUNNABLE(p_tcb))
    {
    rex_best_task = p_tcb;
    rex_sched();
    }

    }
    REX_INTFREE();
    return;
    } /* END rex_resume_task */

    2.4. 删除任务

      2.4.1. rex_remove_task

      *将一个任务控制块TCB从任务列表rex_task_list从移除

    void rex_remove_task( rex_tcb_type *tcb_ptr /* pointer to tcb */)
    {
    rex_tcb_type *prev_tcb_ptr;
    rex_tcb_type *next_tcb_ptr;
    prev_tcb_ptr = REX_TASK_LIST_PREV( tcb_ptr );
    next_tcb_ptr = REX_TASK_LIST_NEXT( tcb_ptr );
    if ( ( prev_tcb_ptr == NULL ||
    prev_tcb_ptr->pri != tcb_ptr->pri ) &&
    next_tcb_ptr != NULL &&
    next_tcb_ptr->pri == tcb_ptr->pri )
    {
    /* 若该任务是当前优先级别的代表(最靠前的任务),寻找下一个同一优先级别的任务,作为代表(并未使用) */
    rex_tcb_type *temp_tcb_ptr = next_tcb_ptr;
    while ( temp_tcb_ptr->pri == tcb_ptr->pri )
    {
    temp_tcb_ptr->pri_rep_ptr = next_tcb_ptr;
    temp_tcb_ptr = REX_TASK_LIST_NEXT( temp_tcb_ptr );
    }
    }
    REX_TASK_LIST_POP( tcb_ptr );
    tcb_ptr->link.prev_ptr = NULL;
    tcb_ptr->link.next_ptr = NULL;
    return;
    } /* END rex_remove_task */

      2.4.2. rex_kill_task_ext()

      *首先将任务从rex_task_list从移除

      *移除与其相关的定时器

      *通知DOG停止对其的监视

      *如果持有临界区,则需要释放它

      *如果需要任务调度,先检查该任务是否持有任务调度锁定,若有则需释放锁定,再进行任务调度

    void rex_kill_task_ext(
    rex_tcb_type *p_tcb,
    boolean schedule_new_task
    )
    {
    REX_INTLOCK();
    TASKLOCK();
    /* Task is alive only if it is still linked into TCB list.
    */
    if ( (p_tcb->link.prev_ptr != NULL ) || (p_tcb->link.next_ptr != NULL) )
    {
    /* Remove TCB from the task list.
    */
    rex_remove_task( p_tcb );
    /* Remove REX timers associated with the task from the timer list.
    */
    rex_delete_task_timers( p_tcb );
    /* Tell Dog to stop monitoring this task.
    */
    REX_PAUSE_DOG_MONITOR( p_tcb );
    /* Check if we were holding or waiting on a critical section */
    while (p_tcb->cs_sp >= p_tcb->cs_stack)
    {
    if ( p_tcb->cs_link.next_ptr == NULL) /* holding crit section */
    {
    /* free the crit section, but don't call rex_sched() yet */
    rex_leave_crit_sect_internals( *p_tcb->cs_sp, p_tcb, FALSE);
    }
    else /* we were waiting on the list */
    {
    /* if item is first on the list, fix up list head */
    if (p_tcb->cs_link.prev_ptr == REX_CRIT_SECT_FLAG)
    {
    (*p_tcb->cs_sp)->tcb_link = p_tcb->cs_link.next_ptr;
    }
    else /* fix up previous item on list */

    {
    p_tcb->cs_link.prev_ptr->cs_link.next_ptr =
    p_tcb->cs_link.next_ptr;
    }
    /* if item is NOT the last on the list */
    if (p_tcb->cs_link.next_ptr != REX_CRIT_SECT_FLAG)
    {
    p_tcb->cs_link.next_ptr->cs_link.prev_ptr =
    p_tcb->cs_link.prev_ptr;
    }
    --p_tcb->cs_sp;
    }
    } /* END we needed to deal with crit section */
    rex_num_tasks--;
    if( schedule_new_task )
    {
    /* 如果任务是想杀死自身,并且持有任务锁定,则要释放任务锁定*/
    if (p_tcb == rex_curr_task)
    {
    if (rex_nest_depth > 0)
    {
    rex_nest_depth = 0;
    rex_sched_allow = TRUE;
    }
    } /* end-if task was killing itself */
    rex_set_best_task( REX_TASK_LIST_FRONT() );
    rex_sched();
    } /* END needed to reschedule */
    } /* END TCB was still in active list */
    TASKFREE();
    REX_INTFREE();
    return;
    } /* END rex_kill_task_ext */

      相关的API:rex_kill_task()

    2.5. Others

      2.5.1. rex_self()

      *获得当前任务的控制块TCB

    rex_tcb_type *rex_self( void )
    {
    /*-------------------------------------------------------
    ** The currently running task is in rex_curr_task
    **-----------------------------------------------------*/
    return rex_curr_task;
    } /* END rex_self */

      2.5.2. rex_get_pri()

      *获得当前任务的优先级

    rex_priority_type rex_get_pri( void )
    {
    /*-------------------------------------------------------
    ** Just return the priority field of the current task
    **-----------------------------------------------------*/
    return rex_curr_task->pri;
    } /* END rex_get_pri */

      2.5.3. rex_set_pri()

      *设置任务的优先级

    rex_priority_type rex_set_pri(
    rex_priority_type p_pri /* the new priority */
    )
    {
    /*-------------------------------------------------------
    ** A wrapper function that just calls rex_task_pri with
    ** the current task
    **-----------------------------------------------------*/
    return rex_task_pri(rex_curr_task, p_pri);
    } /* END rex_set_pri */

      2.5.4. rex_task_pri()

      设置指定任务的优先级

      *从任务链表中移除

      *改变该任务的优先级

      *将该任务按照新优先级插入任务链表中

      *若满足调度条件,则进行任务调度

    rex_priority_type rex_task_pri(
    rex_tcb_type *p_tcb, /* tcb to set priority on */
    rex_priority_type p_pri /* the new priority */
    )
    {
    rex_priority_type prev_pri = p_tcb->pri; /* the priority before the set */
    boolean comp = FALSE; /* Comparator */
    REX_INTLOCK();
    comp = (p_pri == p_tcb->pri);
    REX_INTFREE();
    /* Return if the priority is the same */
    if( comp )
    {
    return prev_pri;
    }
    REX_INTLOCK();
    /* 先从链表中移除,在根据新优先级将其重新插入到一个新位置 */
    p_tcb->link.next_ptr->link.prev_ptr = p_tcb->link.prev_ptr;
    p_tcb->link.prev_ptr->link.next_ptr = p_tcb->link.next_ptr;
    p_tcb->pri = p_pri;
    /* 按照优先级大小,将任务插入任务链表;rex_idle_task(the kernel task)优先级为0,处于链表末尾 */
    search_ptr = rex_task_list.link.next_ptr;
    while(search_ptr->pri > p_pri) {
    search_ptr = search_ptr->link.next_ptr;
    }
    p_tcb->link.prev_ptr = search_ptr->link.prev_ptr;
    p_tcb->link.next_ptr = search_ptr;
    search_ptr->link.prev_ptr->link.next_ptr = p_tcb;
    search_ptr->link.prev_ptr = p_tcb;
    /* 如果任务处于ready状态,且优先级比rex_best_task更高,则进行任务切换 */
    if ( (p_pri > rex_best_task->pri) && ( REX_TASK_RUNNABLE(p_tcb) ) )
    {

    rex_best_task = p_tcb;
    rex_sched();
    }
    REX_INTFREE();
    return prev_pri;
    } /* END rex_task_pri */

    3. 调度(Schedule)

      REX使用基于优先级的调度算法。每个任务都有一个32位非零的正整数作为其优先级,优先级越高、数字越大,优先级0保留给kernel task(即idle task)使用。老版本的REX要求每个任务的优先级是唯一的,现在的版本中无此限制。
      在任务调度时,REX总是选择优先级最高的ready状态的任务——优先级最高且不等待任何事件的任务。如果选择不唯一,REX在其中任意选择一个。被选中的任务将会开始运行,直到它自愿挂起,或者中断激活了一个更高优先级的任务。
      当一个被挂起的任务所等待的条件被满足时,任务将会进入ready状态。当所以任务被挂起时,idle任务将会执行。
    REX还提供了机制,允许任务改变自身或其他任务的优先级。

     

    3.1. 调度代码

      3.1.1. rex_sched()

      *执行实际上的任务切换工作

      *只能被REX内核函数调用,不能由用户调用

      *典型地,在一个REX服务改变了best task指针后被调用

      *rex_sched()首先判断current task和best task是否相同。若相同,则直接返回;否则,将best task赋值给current task。再查看当前处于任务级还是中断级,若是任务级,则保存旧任务的上下文,载入新任务的上下文;若是中断级,则不会执行上下文切换,而是会等到返回到任务级后再执行

    LEAF_NODE rex_sched
    mrs a3, CPSR ; Save the CPSR for later.
    orr a1, a3, #PSR_Irq_Mask:OR:PSR_Fiq_Mask禁止FIQ中断
    msr CPSR_c, a1
    ;---------------------------------------------------------------------------
    ; 如果当前处于中断状态,就返回
    ;---------------------------------------------------------------------------
    and a1, a3, #PSR_Mode_Mask
    cmp a1, #PSR_Supervisor ; If not in Supervisor mode do not swap
    bne rex_sched_exit_1 ; until we revert back to task level
    ;---------------------------------------------------------------------------
    ; 如果任务调度被加锁,也返回
    ;---------------------------------------------------------------------------
    ; test for TASKLOCK
    ldr a2, =rex_sched_allow ; load scheduling flag
    ldr a2, [a2] ; dereference sched. flag
    cmp a2, #0 ; compare with FALSE
    beq rex_sched_exit_1 ; return
    ;---------------------------------------------------------------------------
    ; 只有当rex_best_task不等于rex_curr_task才进行任务切换
    ;---------------------------------------------------------------------------
    ldr a2, =rex_best_task ; load the best task into a2
    ldr a2, [a2] ; dereference best task
    ldr a4, =rex_curr_task ; load the current task into a4
    ldr a1, [a4] ; dereference current task
    cmp a2, a1 ; if current task == best task just return
    beq rex_sched_exit_1
    ;---------------------------------------------------------------------------
    ; Set the curr_task to the new value
    ;---------------------------------------------------------------------------
    str a2, [a4] ; set rex_curr_task=rex_best_task
    mov a4, a1 ; a4 points now to the last (former current) task
    ;---------------------------------------------------------------------------
    ; Increment the slice count.
    ;---------------------------------------------------------------------------
    ldr a1, [a2, #REX_TCB_SLICES_OFFSET] ; load up the slice count
    add a1, a1, #1 ; increment it
    str a1, [a2, #REX_TCB_SLICES_OFFSET] ; store it
    ; --------------------------------------------------------------------
    ; 保存CPU可能被破坏的上下文
    ; --------------------------------------------------------------------
    stmfd sp!, {lr} ; Return address.
    sub sp, sp, #8 ; no need to store r12,r14 in task context.
    stmfd sp!, {r4-r11}
    sub sp, sp, #16 ; Subtract a1-a4 location
    stmfd sp!, {a3} ; First line on rex_sched saves CPSR in a3!!!
    ;---------------------------------------------------------------------------
    ; Save the context on stack
    ;---------------------------------------------------------------------------
    str sp, [a4, #REX_TCB_STACK_POINTER_OFFSET]
    mov a1, a2 ; a1 = the current task
    ;---------------------------------------------------------------------------
    ; rex_start_task_1 是函数void rex_start_task(rex_tcb_type *)的入口地址
    ; 它默认当前a1 为当前任务rex_curr_task的TCB指针
    ;---------------------------------------------------------------------------
    rex_start_task_1
    ; --------------------------------------------------------------------
    ; Restore the user state, note this may not be the state saved above
    ; since the call the rex_sched may have changed which stack the handler isworking on. Note, a context switch will happen here.
    ; --------------------------------------------------------------------
    ldr sp, [a1, #REX_TCB_STACK_POINTER_OFFSET] ; Load thestack pointer
    ldmfd sp!, {a1} ; Restore SPSR (in a1)
    msr SPSR_f, a1 ; Load SPSR
    msr SPSR_c, a1 ; Load SPSR
    mov a1, sp ; Load sp in a1.
    add sp, sp, #REX_CF_SIZE - 4 ; adjust sp
    ldmfd a1, {r0-r12,lr,pc}^ ; Load and return, sp already adjusted.
    ; --------------------------------------------------------------------
    ; 如果没有进行上下文切换,由此处退出
    ; --------------------------------------------------------------------
    rex_sched_exit_1
    msr CPSR_f, a3 ; Restore interrupts as prior to rex_sched
    msr CPSR_c, a3 ; Restore interrupts as prior to rex_sched
    LEAF_NODE_END
    ; END rex_sched

     

    3.2. 设定rex_best_task

      rex_best_task表示当前系统中处于ready状态、优先级最高的任务。 

    void rex_set_best_task(rex_tcb_type *start_tcb)
    {
    rex_tcb_type *candidate_task;
    ASSERT( start_tcb != NULL );
    candidate_task = start_tcb->link.next_ptr;
    ASSERT( candidate_task != NULL );
    /* find first runnable task */
    while ( REX_TASK_RUNNABLE(candidate_task) == FALSE)
    {
    candidate_task = candidate_task->link.next_ptr;
    ASSERT( candidate_task != NULL );
    }
    rex_best_task = candidate_task;
    return;
    } /* END rex_set_best_task */ 

    3.2.1. rex_set_best_task()

      *遍历任务链表,搜索处于ready状态的任务中优先级最高的,将其设为rex_best_task

    3.3. 任务调度加锁/解锁

      3.3.1. rex_task_lock()

      *如果处于IRQ中断模式,禁止加锁,直接推出

      *关中断

      *设置调度允许标志为FALSE,嵌套层数加一

      *开中断

      void rex_task_lock( void )
      {
        if ( !rex_is_in_irq_mode( ) )
        {
          REX_INTLOCK();
          rex_sched_allow = FALSE;
          rex_nest_depth++;
          REX_INTFREE();
        }
      } /* END rex_task_lock */

      相关宏定义:REX_INTLOCK()、INTLOCK()

     

    3.3.2. rex_task_free()

      *如果处于IRQ中断模式,禁止解锁,直接推出

      *关中断

      *嵌套次数减一;若减至0,则重新允许调度,并且立即调用rex_sched()进行调度

      *开中断

      void rex_task_free( void )
      {
        if ( !rex_is_in_irq_mode( ) )
        {
          REX_INTLOCK();
          if (rex_nest_depth > 0)
            rex_nest_depth--;
          if (rex_nest_depth == 0)
          {
            rex_sched_allow = TRUE;
            rex_sched();
          }
          REX_INTFREE();
        }
      } /* END rex_task_free */

      *相关宏定义:REX_INTFREE()、INTFREE()

    3.3.3. rex_tasks_are_locked()

      *若允许任务调度,返回TRUE;否则,返回FALSE

      int rex_tasks_are_locked( void )
      {
        return !rex_sched_allow;
      } /* END rex_tasks_are_locked */

     

    4. 中断(Interrupts)

      REX实现了一个抢先式的内核。从中断处理程序返回时,控制权会交给优先级最高、处于ready状态的任务,而并非一定会返回被中断的任务。

      *Programmable Interrupt Controller(PIC,可编程中断控制器)

      *PIC TRAMPOLINE SERVICES

      4.1. 设置中断向量

      4.1.1. rex_set_interrupt_vector ()

        *设置用户定义的ISR中断服务程序(包括IRQ、FIQ)的入口函数,当指定的中断发生时,该入口函数会被调用

        *用户程序一般不直接调用该接口设置中断,而是通过下面提到的tramp_set_isr()来设置

      void rex_set_interrupt_vector (
          rex_vect_type v, /* Vector */
          void (*fnc_ptr)( void ) /* *function to be installed */
          )
       {
          if (v == P_IRQ_VECT)
          {
            rex_irq_vector = fnc_ptr;
          }
          else
          {
            rex_fiq_vector = fnc_ptr;
          }
      } /* END rex_set_interrupt_vector */

     

    4.2. PIC Trampoline Service(可编程控制弹簧中断服务)

      4.2.1. tramp_init()

      *设置ISR的默认值

      *初始化PIC硬件

      *设置tramp_isr()作为IRQ的用户处理函数入口。中断一发生,该函数就将会被调用

      void tramp_init( void )
      {
        uint8 i;
        /* all isr to default */
        for ( i = 0; i < TRAMP_NUM_INTS; i++ )
        {
          isr_func_tbl[i].isr_ptr = tramp_default_isr;
        }
        /* init hardware */
        tramp_init_hardware();

      #ifdef FEATURE_TRAMP_QUEUED_CALLS
       /* Initialize the interrupt call queue.
        */
        (void) q_init( &tramp_call_q );
      #endif /* FEATURE_TRAMP_QUEUED_CALLS */
        /* 将tramp_isr()设置为IRQ ISR,这样每次IRQ中断发生,该函数都将被调用 */
        /* no FIQ ISR for now */
        rex_set_interrupt_vector( P_IRQ_VECT, tramp_isr );
      } /* end tramp_init */

     

    4.2.2. tramp_set_isr()

      *用于为一个特别的中断源设置相应的ISR(中断服务函数)

      void tramp_set_isr
      (
        /* 要设置的中断类型 */
        tramp_isr_type int_num,
        /* ISR to be installed for this interrupt */
        isr_ptr_type isr_ptr
      )
      {
          /* Address of priority register corresponding to this interrupt */
        uint32 prio_address;
          /* Address of the Enable registers: IRQ_ENABLE_0 and IRQ_ENABLE_1 */
        uint32 mask_reg_address;
          /* Address of the Clear registers: IRQ_ENABLE_0 and IRQ_ENABLE_1 */
        uint32 clear_reg_address;
          /* Interrupt mask to set in the enable register */
        uint32 mask_val;
          /* Mask to clear bit in the CLEAR register */
        uint32 clear_val = 0;
          /* disable interrupts while changing table and PIC registers */
        INTLOCK();
          /* ISR if passed-in ISR is NULL, change it to tramp_default_isr */
        if (isr_ptr == NULL)
        {
          isr_ptr = tramp_default_isr;

        }

        /* load our local table with the function ptr */
    isr_func_tbl[int_num].isr_ptr = isr_ptr;
    /* 将优先级写入PIC的优先级寄存器。优先级寄存器为32位,从PRIO_BASE地址开始,按照中断编号排序。因此,我们可以将中断编号乘以4来获得中断的优先级寄存器地址 */
    prio_address = PRIO_BASE + ((unsigned int)int_num << 2);
    outpdw( prio_address, (uint32) (isr_func_tbl[int_num].priority & MAX_PRIO_VAL));
    /* 确定需要修改的是哪一个中断屏蔽寄存器mask register */
    if ( int_num >= NUM_INT_BITS_IN_REG)
    {
    mask_reg_address = HWIO_ADDR(IRQ_ENABLE_1);
    mask_val = HWIO_IN(IRQ_ENABLE_1);
    clear_reg_address= HWIO_ADDR(IRQ_CLEAR_1);
    /* convert to bit offset in register */
    int_num -= NUM_INT_BITS_IN_REG;
    }
    else
    {
    mask_reg_address = HWIO_ADDR(IRQ_ENABLE_0);
    mask_val = HWIO_IN(IRQ_ENABLE_0);
    clear_reg_address= HWIO_ADDR(IRQ_CLEAR_0);
    }
    /* 将相应的bit置位或复位 */
    if (isr_ptr == tramp_default_isr)
    {
    mask_val &= ~((unsigned int)1 <<int_num); /* set that bit to zero, turning off INT */
    /* clear the status bit of this interrupt */
    clear_val = (1 << int_num);
    }
    else
    {
    mask_val |= ((unsigned int)1 << int_num); /* set bit to one, turning on INT */
    }

    /* 写入中断屏蔽寄存器mask register */
    outpdw( mask_reg_address, mask_val);
    /* 考虑到中断可能在清空状态位时发生,我们在最后清空中断状态寄存器status register。This is because INT STATUS = INT ENABLE MASK & INT_SRC(s) */
      if (isr_ptr == tramp_default_isr)
      {
      /* unset bit in the CLEAR register => clears bit in STATUS register */
      outpdw( clear_reg_address, clear_val );
      }
      INTFREE();
      } /* end tramp_set_isr */

     

    4.2.3. tramp_isr()

      *IRQ中断发生时,该函数将会被调用(可以参见下面的IRQ_handler)

      *程序反复读取PIC中的IRQ_VEC_INDEX_RD,根据寄存器的返回值跳转执行相应的ISR。直到寄存器返回NON_VECTOR,表示已经没有被挂起的中  

       断了

      *在如下两种情况,IRQ_ENABLE寄存器的相应位会被复位:

        1) 在调用ISR前复位

        2) 在ISR调用之后复位。在这种情况下,我们不能仅仅关中断并调用ISR,

        void tramp_isr( )
    {
    /* vector index from PIC register */
    uint32 vect_idx;
    /* for our local tbl */
    uint32 tbl_idx;
    /* Mask of the occurring interrupt */
    uint32 mask_val;
    /* Address of the IRQ_ENABLE register */
    uint32 mask_reg_address;
    /* Address of the IRQ_CLEAR register */
    uint32 clear_reg_address;
    /*注意:有两种情况会读取IRQ_VEC_INDEX_RD寄存器。
    第一种是当ARM中断时,tramp_isr()被调用时会读取;
    第二种是在我们处理完ISR后会读取。这取决于是否还有被挂起的中断*/
    /* 该变量决定我们所读取的状态值,是来自于ARM中断、还是在执行完ISR之后。
    在上述两种模式下,我们读取不同的寄存器来确定发生的是哪一个中断。
    IRQ_VEC_INDEX_RD:ARM中断时读取,返回最高优先级中断的索引;
    IRQ_VEC_INDEX_PEND_RD:处理完中断时读取,返回被挂起的最高优先级的中
    断、或NON_VECTOR表示没有中断被挂起。*/
    boolean irq_from_arm = TRUE;

    #ifdef FEATURE_DEBUG_TRAMP_EXECUTION
    TRAMP_DEBUG_IN_ISR = 1;
    #endif
    /* 假设-这里假设我们在进入前已关了中断-这样的假设确实有效*/
    for (;;) /* forever */
    {
    if (irq_from_arm)
    {
    /* IRQ_VEC_INDEX_RD 包含了当前最高优先级的中断向量的索引值。为了实现
    “smart function”(不用在TASK和IRQ模式间来回切换,就可以处理多个中断),
    我们不停的读取该寄存器,直至返回一个标志值。
    注意读取寄存器会通知PIC我们已经处理完了上一次读取的ISR。所以,我们不能随意
    读取该寄存器-除非我们已经处理完了上一个ISR。*/
    tbl_idx = vect_idx = HWIO_INM(IRQ_VEC_INDEX_RD, IRQ_VEC_INDEX_RD_RMSK );
    irq_from_arm = FALSE;
    #ifdef FEATURE_TRAMP_QUEUED_CALLS
    nested_int_cnt++;
    #endif
    }
    else
    {
    /* Lock interrupt before reading index register */
    (void) rex_int_lock();
    tbl_idx = vect_idx = HWIO_INM(IRQ_VEC_INDEX_PEND_RD,
    IRQ_VEC_INDEX_PEND_RD_RMSK);
    }
    /* 确定是否还有被挂起的中断。如果没有,会返回NON_VECTOR。 */
    if (vect_idx == NON_VECTOR)
    {
    /* No more interrupt to process for the time being.
    */
    #ifdef FEATURE_TRAMP_QUEUED_CALLS
    /* Since everything else is done, handle queued calls
    */
    if (nested_int_cnt == 1)
    {
    tramp_call_ptr_type *call_ptr;
    /* 只有当没有需要处理的中断时,才允许处理队列调用 */
    while ( (call_ptr = (tramp_call_ptr_type *) q_get( &tramp_call_q )) !=

    NULL )
    {
    /* 通过call_ptr结构中的指针来调用函数,并将call_ptr中保存的参数传给它*/
    if (call_ptr->call_ptr)
    {
    #ifdef FEATURE_DEBUG_TRAMP_EXECUTION
    TRAMP_DEBUG_IN_ISR_QUEUED_CALL = 1;
    /*save call for debugging*/
    TRAMP_DEBUG_QUEUED_CALL_FUNC_PTR = (void*) call_ptr->call_ptr;
    #endif
    (void) rex_int_free();
    (*(call_ptr->call_ptr))( call_ptr->arg.arg_int4, call_ptr->arg.arg_ptr );
    (void) rex_int_lock();
    call_ptr->in_use = FALSE;
    #ifdef FEATURE_DEBUG_TRAMP_EXECUTION
    TRAMP_DEBUG_IN_ISR_QUEUED_CALL = 0;
    #endif
    }
    else
    {
    ERR_FATAL("Invalid call_ptr in tramp_handle_int_calls", 0, 0, 0);
    }
    }
    }
    /* 为了避免在我们正处理调用时,另一个中断到来与之发生竞争,需要等到调用处理完成后减一。只需要一个ISR实例来处理调用。 */
    nested_int_cnt--;
    #endif /* FEATURE_TRAMP_QUEUED_CALLS */
    #ifdef FEATURE_DEBUG_TRAMP_EXECUTION
    if (nested_int_cnt==0)
    {
    /*all isrs finished*/
    TRAMP_DEBUG_IN_ISR = 0;
    }
    #endif

    return;
    }
    /* 此时,我们实际上还在执行ISR,即使并不在ISR主函数中。开中断以允许抢先 */
    (void) rex_int_free();
    /* determine which clear register to write to */
    if ( vect_idx >= NUM_INT_BITS_IN_REG)
    {
    clear_reg_address = HWIO_ADDR(IRQ_CLEAR_1);
    mask_reg_address = HWIO_ADDR(IRQ_ENABLE_1);
    /* adjust bit offset if it's in different register */
    vect_idx -= NUM_INT_BITS_IN_REG;
    }
    else
    {
    clear_reg_address = HWIO_ADDR(IRQ_CLEAR_0);
    mask_reg_address = HWIO_ADDR(IRQ_ENABLE_0);
    }
    /* 两种不同的情况,我们清除中断寄存器标志位的时机不同 */
    #ifdef FEATURE_DEBUG_TRAMP_EXECUTION
    TRAMP_DEBUG_ISR_NUM = tbl_idx;
    #endif
    /* Case 1. 在调用ISR之前清除 */
    if ( isr_func_tbl[tbl_idx].clr_when == CLR_BEF)
    {
    outpdw( clear_reg_address, (1 << vect_idx) ); /* clear the int */
    #ifdef TIMETEST
    TIMETEST_ISR_ID( isr_func_tbl[tbl_idx].id );
    #endif
    if (isr_func_tbl[tbl_idx].isr_ptr==NULL)
    {
    ERR_FATAL("NULL ISR ptr",0,0,0);
    }
    (isr_func_tbl[tbl_idx].isr_ptr) (); /* call the registered ISR */
    }
    /* case 2. 在调用ISR之后清除 */
    else

    {
    #ifdef TIMETEST
    TIMETEST_ISR_ID( isr_func_tbl[tbl_idx].id );
    #endif
    if (isr_func_tbl[tbl_idx].isr_ptr==NULL)
    {
    ERR_FATAL("NULL ISR ptr",0,0,0);
    }
    /* 在此,真正调用已注册的ISR开始中断服务 */
    (isr_func_tbl[tbl_idx].isr_ptr) ();
    /* clear the bit */
    outpdw( clear_reg_address, (1 << vect_idx) );
    }
    } /* end for(;;) */
    } /* end tramp_isr */

     

    4.3. IRQ_handler

      IRQ中断服务入口

  • 相关阅读:
    Element学习
    top level element is not completed
    IntelliJ IDEA 使用心得与常用快捷键
    MVC下的DAO接口类和SERVICE接口类区别?
    Emmet初探2
    Servlet和Android网络交互基础(3)
    A-Frame WebVR开发新手教程
    重温java中的String,StringBuffer,StringBuilder类
    Android 常见面试题
    <html>
  • 原文地址:https://www.cnblogs.com/zxc2man/p/7683845.html
Copyright © 2011-2022 走看看