Rex操作系统分析
2016-02-02 16:51
190 查看
Rex操作系统分析
在百度文库中搜索标题即可搜索到相关PDF文件
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
都拥有各自的堆栈,优先级,这些共同构成了任务的上下文。每个任务都有一个相关联的?
萁峁梗晌猅CB(任务控制块)。
REX允许在执行任意时刻创建任意数目的task
。实际上,每增加一个任务,由于遍历更长的任务链表,REX
性能会有轻微的下降。需要小心控制任务的数目。
*任务堆栈:
每个任务都有用自己的堆栈,在运行时被使用。当任务挂起时(
如运行其他任务或进行中断服务)
,任务的寄存器会被压入任务对战中,并将栈顶指针保存在任务TCB
里。等到任务被选中再次运行时,从TCB
里获取栈顶指针,将任务的寄存器值从栈顶弹出,任务于是从上次被中断的位置继续运行?
U庑┤挝袂谢坏拇矶杂谌挝窭此凳峭该鞯?可以参考【第三章 调度】)
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] = '\0';
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] = '\0';
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);
}
}
}
/*
为了避免在我们正处理调用时,另一个中断到来与之发生竞争,需要等到调用处理完成后?
跻弧V恍枰桓鯥SR实例来处理调用。 */
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中断服务入口
?? 保存上下文
?? 进入IRQ模式
?? 如果TIMETEST标志置位,则写端口
?? 调用注册的ISR(中断服务程序)
?? 恢复处理器状态
?? 恢复到原来的模式
?? 检查任务调度是否上锁。若上锁,跳过下一段,到no_switch
?? 检查是否需要作上下文切换,若需要,跳至task_swapped程序,否则转到
no_switch
?? no_switch:恢复原来的上下文,返回到原任务
?? task_swapped:进行任务上下文的切换
EXPORT IRQ_Handler
ALIGN
ROUT
IRQ_Handler
sub lr, lr, #4 ; adjust return address (ARM requirement)
; --------------------------------------------------------------------
; 某些寄存器会被中断处理程序(C
程序)所破坏,因此在中断堆栈里保存这些寄存器,也保存SPSR
(它会被嵌套中断所破坏)。
; --------------------------------------------------------------------
stmfd sp!, {r0-r3, r10, r12, r14}
mrs r0, SPSR
stmfd sp!, {r0}
; --------------------------------------------------------------------
; Increment the nesting level counter.
; --------------------------------------------------------------------
ldr r0, =rex_int_nest_level
ldrh r1, [r0]
add r1, r1, #1
strh r1, [r0]
; --------------------------------------------------------------------
; Switch to System Mode and save r14 on stack.
; --------------------------------------------------------------------
msr CPSR_c, #PSR_System:OR:PSR_Irq_Mask
stmfd sp!, {r14}
; --------------------------------------------------------------------
; 调用中断处理
; --------------------------------------------------------------------
ldr r3, =rex_irq_vector ;这里会调用tramp_isr()
ldr r3, [r3]
blatox r3
; --------------------------------------------------------------------
; Pop r14 from stack and switch to IRQ Mode.
; --------------------------------------------------------------------
ldmfd sp!, {r14}
msr CPSR_c, #PSR_Irq:OR:PSR_Irq_Mask
; --------------------------------------------------------------------
; Decrement the nesting level counter.
; --------------------------------------------------------------------
ldr r0, =rex_int_nest_level
ldrh r1, [r0]
subs r1, r1, #1 ; Notice it is subs. Set Z flag if r1==0
strh r1, [r0]
; ----------------------------------------------------------------
; Set timetest_isr_level to the new value
; ----------------------------------------------------------------
#ifdef TIMETEST_PROF
ldr r0, =timetest_isr_level
strb r1, [r0]
#endif
;---------------------------------------------------------------------
; Branch if rex_int_nest_level <> 0.
; The Z flag was set (or not set) by the subs instruction above.
;---------------------------------------------------------------------
bne nested_interrupt
;---------------------------------------------------------------------
; 是否需要任务切换,如果curr task等于best task,则不需要。
;---------------------------------------------------------------------
ldr r0, =rex_curr_task
ldr r0, [r0]
ldr r1, =rex_best_task
ldr r1, [r1]
cmp r0, r1
beq task_swap_not_required
;---------------------------------------------------------------------
; If a TASKLOCK is in effect, then we do not need to even consider
; swapping tasks.
;---------------------------------------------------------------------
; test for TASKLOCK in effect
ldr r2, =rex_sched_allow
ldr r2, [r2]
cmp r2, #0
beq task_swap_not_required
;---------------------------------------------------------------------
; Restore the SPSR from stack.
;---------------------------------------------------------------------
ldmfd sp!, {r0}
msr SPSR_f, r0
msr SPSR_c, r0
;---------------------------------------------------------------------
; Restore the registers from IRQ mode stack.
;---------------------------------------------------------------------
ldmfd sp!, {r0-r3, r10, r12, r14}
;---------------------------------------------------------------------
; 保存即将退出的任务的上下文
;---------------------------------------------------------------------
msr CPSR_c, #PSR_Supervisor:OR:PSR_Irq_Mask
sub sp, sp, #4 ; Space for Return address (pc)
stmfd sp!, {r0-r12,lr} ; Other registers
sub sp, sp, #4 ; Space for SPSR
mov r1, sp
ldr r0, =rex_curr_task ; Save sp of outgoing task into TCB.
ldr r2, [r0] ;
str r1, [r2, #REX_TCB_STACK_POINTER_OFFSET]
msr CPSR_c, #PSR_Irq:OR:PSR_Irq_Mask ; change to IRQ mode
; save Return address into context frame.
str r14, [r1, #REX_INTERRUPT_FRAME_PC_OFFSET]
mrs r3, SPSR ; save SPSR into context frame.
str r3, [r1, #REX_INTERRUPT_FRAME_SPSR_OFFSET]
; Back to Supervisor
msr CPSR_c, #PSR_Supervisor:OR:PSR_Irq_Mask
;---------------------------------------------------------------------
; 恢复新进入任务的上下文
;---------------------------------------------------------------------
; Reminder ... r0 = address of rex_curr_task variable.
;---------------------------------------------------------------------
ldr r1, =rex_best_task
ldr r1, [r1]
str r1, [r0] ; curr_task <- best_task
ldr sp, [r1, #REX_TCB_STACK_POINTER_OFFSET] ; Stack pointer
ldmfd sp!, {r0} ; Get SPSR
msr SPSR_f, r0
msr SPSR_c, r0 ; Restore SPSR.
mov a2, sp
add sp, sp, #REX_CF_SIZE - 4 ; adjust sp
ldmfd a2, {r0-r12, r14, pc}^ ; Load and return
; This will also restore CPSR.
;---------------------------------------------------------------------
; Control comes here when a task swap is not required.
;---------------------------------------------------------------------
task_swap_not_required
;---------------------------------------------------------------------
; Restore the SPSR from stack.
;---------------------------------------------------------------------
ldmfd sp!, {r0}
msr SPSR_f, r0
msr SPSR_c, r0
;-----------------------------------------------------------------
; Load saved registers and return
; This will also restore CPSR.
;-----------------------------------------------------------------
ldmfd sp!, {r0-r3, r10, r12, pc}^
;---------------------------------------------------------------------
; Control comes here when a nested interrupt continues.
;---------------------------------------------------------------------
nested_interrupt
;---------------------------------------------------------------------
; Restore the SPSR from stack.
;---------------------------------------------------------------------
ldmfd sp!, {r0}
msr SPSR_f, r0
msr SPSR_c, r0
;-----------------------------------------------------------------
; Load saved registers and return
; This will also restore CPSR.
;-----------------------------------------------------------------
ldmfd sp!, {r0-r3, r10, r12, pc}^
IRQ_Handler_end
注意:在IRQ_handler返回的时候,会进行必要的任务切换。由于在clk
模块初始化时(driver/clk/clkm2p.c:clk_init()中),设置置了系统的硬件时
钟中断:
//5ms定时
MSM_OUT( SLEEP_TIMER_0_CNT, TS_SLEEP_TMR_0_CNT_5MS );
//设置时钟中断ISR,clk_tick_isr()会调用rex_tick()对rex timers减计数
tramp_set_isr( SLEEP_TIMER_INT0, clk_tick_isr );
这样就确保了至少每5ms,REX会进行一次任务的调度。
5. 信号量(Signals)和定时器(Timers)
5.1. 信号量
每个任务可以与一组信号量(最多32个)相关联,这些信号量是TCB
的一部分,用于通知任务发生了某些事件。任务的信号量可以被任一任务或中断处理函数?
梦换蚯宄?
任务可以随时检查自身信号量的状态,而且,能把自身在自己的一个或多个信号量上?
移稹5牵挝癫豢梢约觳榛蚬移鹪谄渌挝竦男藕帕可稀?
关于信号量,有几点需要牢记:
?? 信号量是一些用途广泛的标志,其含义可以由用户任意指定。
??
任务可以挂起等待特定信号量的置位,可能是由另一个任务或中断。但置位一个并非任务?
却男藕帕浚⒉换嶂苯佑跋斓饺挝竦闹葱凶刺?
?? 一次操作就可以置位或复位与一个任务关联的全部32
个信号量的状态。同样,一个任务同时可以等待32
个信号量,任意一个信号量置位都会唤醒任
务。
5.1.1. rex_wait()
?? 若处于IRQ模式或任务调度加锁,禁止等待信号,直接退出
?? 关中断
?? 将任务挂起等待指定的信号(一个或多个信号)
?? 重新调度任务
?? 开中断
rex_sigs_type rex_wait( rex_sigs_type p_sigs/* sigs to wait on */)
{
rex_sigs_type sigs = 0;
ASSERT( !rex_is_in_irq_mode( ) );
ASSERT( !TASKS_ARE_LOCKED( ) );
REX_INTLOCK( );
/*-------------------------------------------------------
** if no signals are set, wait, and reschedule
**-----------------------------------------------------*/
if( (rex_curr_task->sigs & p_sigs) == 0 )
{
/*-------------------------------------------------------
** Set the sigs the task should wait upon
**-----------------------------------------------------*/
rex_curr_task->wait = p_sigs;
/* Tell Dog to stop monitoring this task.
*/
REX_PAUSE_DOG_MONITOR( rex_curr_task );
/*由于当前任务将要阻塞,寻找下一个最高优先级的ready任务,并重新调度*/
rex_set_best_task( REX_TASK_LIST_FRONT() );
rex_sched();
}
/*-------------------------------------------------------
** return with the signal mask of the current task
**-----------------------------------------------------*/
sigs = rex_curr_task->sigs;
INTFREE( );
return sigs;
} /* END rex_wait */
5.1.2. rex_set_sigs()
?? 关中断
?? 将指定的信号量设置给任务
?? 若任务正在等待该信号,则清除任务的等待信号列表
?? 通知DOG恢复对任务的监视
?? 若任务是最高优先级的ready任务,将其设为rex_best_task,重新调度
?? 重开中断
rex_sigs_type rex_set_sigs(
rex_tcb_type *p_tcb, /* tcb for which the sigs will be set */
rex_sigs_type p_sigs /* the sigs to set */
)
{
rex_sigs_type prev_sigs = 0; /* the sigs before setting */
REX_INTLOCK();
prev_sigs = p_tcb->sigs;
p_tcb->sigs = p_tcb->sigs | p_sigs;
/*检查该任务是否正在等待该信号。若是,清除等待信号。若任务是
rex_best_task,重新进行任务调度*/
if((p_tcb->wait & p_sigs) != 0)
{
p_tcb->wait = 0;
/* 通知DOG恢复对任务的监视 */
REX_RESUME_DOG_MONITOR( p_tcb );
if ((p_tcb->pri > rex_best_task->pri) && REX_TASK_RUNNABLE(p_tcb))
{
rex_best_task = p_tcb;
rex_sched();
}
}
REX_INTFREE();
return prev_sigs;
} /* END rex_set_sigs */
5.1.3. rex_get_sigs()
?? 获得指定任务的信号量
rex_sigs_type rex_get_sigs(
rex_tcb_type *p_tcb /* tcb for which sigs will be returned */
)
{
return p_tcb->sigs;
} /* END rex_get_sigs */
5.1.4. rex_clr_sigs()
?? 清除任务中指定的信号量
rex_sigs_type prev_sigs = 0; /* holds the signals prior to the clear */
REX_INTLOCK();
#ifdef FEATURE_REX_IPC
/* Don't clear IPC Receive Signal if a message is pending */
if ((p_sigs & p_tcb->ipc_info.receive_signal) &&
pq_is_empty(p_tcb->ipc_info.mq_ptr))
{
p_sigs &= ~p_tcb->ipc_info.receive_signal; /* IPCDEFI_RECEIVE_SIG */
}
#endif
p_sigs = ~p_sigs;
prev_sigs = p_tcb->sigs;
p_tcb->sigs = p_tcb->sigs & p_sigs;
REX_INTFREE();
return prev_sigs;
} /* END rex_clr_sigs */
5.2. 定时器
定时器用于实现软件定时计数功能。在使用时,任务需要确定定时器的间隔时间和超?
?
时将触发的信号量。任务既可以循环检查信号量的状态,也可以挂起直至定时器超时?
鹦藕胖梦弧?
定时器还可以用与关联一些设置信号量的事件,使得能够检查这些事件的超时。REX
维护了一个激活的定时器链表(即是说没有超时的)。可以创建任意数目的定时器,如同?
挝褚谎S捎诒槔奔涞脑黾樱饣岫韵低承阅苡星嵛⒌挠跋臁R殉钡亩ㄊ逼鞑换嵩黾?
系统开销。
5.2.1. rex_init_timer_list()
?? 初始化定时器链表rex_timer_list
?? 表头是一个计数值为0的定时器节点
?? rex_null_timer被插入作为末尾节点,计数值为最大值UINT_MAX
void rex_init_timer_list( void )
{
rex_timer_list.cnt = 0;
rex_null_timer.tcb_ptr = NULL;
rex_null_timer.sigs = 0x0;
rex_null_timer.cnt = UINT_MAX;
rex_null_timer.tcb_ptr = NULL;
rex_null_timer.sigs = 0x0;
rex_null_timer.link.next_ptr = NULL;
rex_null_timer.link.prev_ptr = &rex_timer_list;
rex_timer_list.link.next_ptr = &rex_null_timer;
rex_timer_list.link.prev_ptr = NULL;
return;
} /* END rex_init_timer_list */
5.2.2. rex_insert_timer()
?? 将指定的定时器按照计数值,从小到大的顺序插入定时器链表rex_timer_list
?? 任一节点的实际计数值,等于从头节点到该节点的计数值之和
void rex_insert_timer(
rex_timer_type *timer_ptr, /* pointer to a valid timer structure */
rex_timer_cnt_type tmo /* Timeout for the timer */
)
{
rex_timer_type *next_timer_ptr = rex_timer_list.link.next_ptr;
/* 定时器链表按照计数值,从小到大排列。从头节点开始遍历,每遍历一个节点,就减去
相应的计数值*/
while( tmo > next_timer_ptr->cnt )
{
REX_ASSERT( next_timer_ptr != NULL );
tmo -= next_timer_ptr->cnt;
next_timer_ptr = next_timer_ptr->link.next_ptr;
}
timer_ptr->cnt = tmo;
timer_ptr->link.prev_ptr = next_timer_ptr->link.prev_ptr;
timer_ptr->link.next_ptr = next_timer_ptr;
next_timer_ptr->link.prev_ptr->link.next_ptr = timer_ptr;
next_timer_ptr->link.prev_ptr = timer_ptr;
if ( next_timer_ptr != &rex_null_timer)
{
next_timer_ptr->cnt -= timer_ptr->cnt;
}
return;
} /* END rex_insert_timer */
5.2.3. rex_def_timer()
?? 定义了一个定时器,指定了与其相关的TCB,以及定时时间到时将设置的信号
void rex_def_timer(
rex_timer_type *p_timer, /* pointer to a valid timer structure */
rex_tcb_type *p_tcb, /* tcb to associate with the timer */
rex_sigs_type sigs /* sigs to set upon timer expiration */
)
{
p_timer->link.next_ptr = NULL;
p_timer->link.prev_ptr = NULL;
p_timer->cnt = 0;
p_timer->tcb_ptr = p_tcb;
p_timer->sigs = sigs;
#ifdef FEATURE_REX_TIMER_EX
p_timer->cb_ptr = NULL;
p_timer->cb_param = 0;
#endif
return;
} /* END rex_def_timer */
相关API:?? rex_def_timer_ex()
5.2.4. rex_set_timer()
?? 设置定时器的超时时间(毫秒为单位)
?? 首先将定时器从rex_timer_list(激活的定时器链表)中移除
?? 如果新的计数值大于0,将定时器重新插入rex_timer_list
;如果定时器已经存在与rex_timer_list中,新的计数值将会覆盖旧值
?? 如果传入的新计数值为0,定时器会马上超时(设置相应的信号)
rex_timer_cnt_type rex_set_timer(
rex_timer_type *p_timer, /* pointer to timer to set */
rex_timer_cnt_type cnt /* value to set the timer */
)
{
rex_timer_cnt_type prev_value; /* the value before the set */
INTLOCK();
/*-------------------------------------------------------
** Remove the timer from the timer list and insert it back
** if necessary.
**-----------------------------------------------------*/
prev_value = rex_clr_timer( p_timer );
if (cnt > 0)
{
rex_insert_timer( p_timer, cnt );
(void) rex_clr_sigs( p_timer->tcb_ptr, p_timer->sigs );
}
else
{
/*-------------------------------------------------------
** If the user passed in 0, set the signals or call the
** callback function.
**-----------------------------------------------------*/
do {
#ifdef FEATURE_REX_TIMER_EX
/* 如果定时器有关联的回调函数,则将它作为一个APC
排队,让它能够在正确的上下文中被调用 */
if ( p_timer->cb_ptr != NULL )
{
if ( !rex_queue_apc(
p_timer->cb_ptr,
p_timer->tcb_ptr,
p_timer->cb_param
) )
{
/* 如果APC无法被排队,需要重试。所以我们将该定时器重新插入到时钟链表的前端 */
rex_insert_timer( p_timer, 0 );
}
break;
}
#endif /* FEATURE_REX_TIMER_EX */
if ( p_timer->sigs != 0 )
{
(void)rex_set_sigs( p_timer->tcb_ptr , p_timer->sigs );
}
} while (0);
}
INTFREE();
return prev_value;
} /* END rex_set_timer */
5.2.5. rex_timed_wait()
?? 设置一个定时器的计数值,并将指定的任务挂起等待信号
rex_sigs_type rex_timed_wait(
rex_sigs_type sigs, /* sigs to wait on */
rex_timer_type *p_timer, /* timer to set and wait on */
rex_timer_cnt_type cnt /* timer to wait */
)
{
REX_ASSERT( !rex_is_in_irq_mode( ) );
/*-------------------------------------------------------
** A call to rex_set_timer and rex_wait will perform a
** timed wait
**-----------------------------------------------------*/
(void) rex_set_timer(p_timer, cnt);
return rex_wait(sigs);
} /* END rex_timed_wait */
5.2.6. rex_clr_timer()
?? 将指定定时器从定时器链表中移除
?? 将其计数值设为0
rex_timer_cnt_type rex_clr_timer(
rex_timer_type *timer_ptr /* timer to clear */
)
{
rex_timer_cnt_type prev_value = 0; /* previous count */
rex_timer_type *next_timer_ptr = &rex_timer_list;
INTLOCK( );
REX_ASSERT(timer_ptr!=NULL);
if( timer_ptr->link.next_ptr != NULL )
{
while ( next_timer_ptr != timer_ptr )
{
next_timer_ptr = next_timer_ptr->link.next_ptr;
REX_ASSERT( next_timer_ptr != NULL );
prev_value += next_timer_ptr->cnt;
}
timer_ptr->link.prev_ptr->link.next_ptr = timer_ptr->link.next_ptr;
timer_ptr->link.next_ptr->link.prev_ptr = timer_ptr->link.prev_ptr;
if ( timer_ptr->link.next_ptr != &rex_null_timer)
{
timer_ptr->link.next_ptr->cnt += timer_ptr->cnt;
}
timer_ptr->link.next_ptr = NULL;
timer_ptr->cnt = 0;
}
INTFREE( );
return prev_value;
}
5.2.7. rex_get_timer()
?? 获得定时器当前的计数值
?? 若定时器不在链表中,则返回0
rex_timer_cnt_type rex_get_timer(
rex_timer_type *timer_ptr /* pointer to the timer to get */
)
{
rex_timer_cnt_type prev_value = 0; /* previous count */
rex_timer_type *next_timer_ptr = &rex_timer_list;
INTLOCK( );
REX_ASSERT(timer_ptr!=NULL);
if( timer_ptr->link.next_ptr != NULL )
{
while ( next_timer_ptr != timer_ptr )
{
next_timer_ptr = next_timer_ptr->link.next_ptr;
REX_ASSERT( next_timer_ptr != NULL );
prev_value += next_timer_ptr->cnt;
}
}
INTFREE( );
return prev_value;
}
5.2.8. rex_pause_timer()
?? 暂停定时器
void rex_pause_timer(rex_timer_type *timer_ptr)
{
timer_ptr->cnt = rex_clr_timer( timer_ptr );
return;
} /* END rex_pause_timer */
5.2.9. rex_resume_timer()
?? 恢复定时器计数
void rex_resume_timer(rex_timer_type *timer_ptr)
{
rex_set_timer( timer_ptr, timer_ptr->cnt );
return;
} /* END rex_resume_timer */
5.2.10. rex_delete_task_timers()
?? 从定时器链表中将与指定任务关联的所有定时器移除
void rex_delete_task_timers(rex_tcb_type *tcb_ptr)
{
rex_timer_type *timer_ptr;
rex_timer_type *next_timer_ptr;
INTLOCK( );
for( timer_ptr = rex_timer_list.link.next_ptr;
timer_ptr != &rex_null_timer;
timer_ptr = next_timer_ptr )
{
next_timer_ptr = timer_ptr->link.next_ptr;
if ( timer_ptr->tcb_ptr == tcb_ptr )
{
rex_clr_timer( timer_ptr );
}
}
INTFREE( );
return;
} /* END rex_delete_task_timers */
5.2.11. rex_decrement_timers()
?? 将定时器链表中所有有效的定时器计数值扣减
?? 在调用该函数时,必须先关中断
?? 如果定时器时间到,调用rex_set_sigs()
将相应的信号量置位,如果需要,会进行任务调度
INLINE static void rex_decrement_timers(
rex_timer_cnt_type p_ticks /* number of rex ticks to decrement by */
)
{
rex_timer_type *ptr; /* points to current timer */
rex_timer_type *next_ptr; /* points to next timer */
#ifdef FEATURE_REX_TIMER_EX
unsigned long apc_queued;
#endif
/*
遍历时钟链表,扣减每个时钟的计数值。如果定时器超时,将其从链表中移除,并将相应?
挝竦男藕帕恐梦?/
for(ptr = rex_timer_list.link.next_ptr;
ptr != &rex_null_timer;
ptr = next_ptr
)
{
REX_ASSERT(ptr!=NULL);
next_ptr = ptr->link.next_ptr;
if( ptr->cnt <= p_ticks )
{
rex_clr_timer( ptr );
#ifdef FEATURE_REX_TIMER_EX
/* 如果定时器有关联的回调函数,则将它作为一个APC
排队,让它能够在正确的上下文中被调用 */
if ( ptr->cb_ptr != NULL )
{
if ( rex_is_in_irq_mode( ) )
{
apc_queued = rex_queue_apc(
ptr->cb_ptr,
ptr->tcb_ptr,
ptr->cb_param
);
}
else
{
apc_queued = rex_queue_apc_internal(
(unsigned long) ptr->cb_ptr,
(unsigned long) ptr->tcb_ptr,
ptr->cb_param
);
}
if ( !apc_queued )
{
/* 如果APC无法被排队,需要重试。所以我们将该定时器重新插入到时钟链表的前端 */
rex_insert_timer( ptr, 0 );
}
continue;
}
#endif /* FEATURE_REX_TIMER_EX */
/* 将任务的信号量置位。如果该任务是新的优先级最高的ready
任务,这将会引起一次重新调度。
Note:ptr->sigs = 0
是一个特殊情况。这意味着定时器超时时,发生了一个同步的事件。*/
/*扣减时钟链表头节点的计数值*/
ptr->cnt -= p_ticks;
break;
}
}
return;
} /* END rex_decrement_timers */
5.2.12. rex_tick()
?? REX的时间节拍函数
?? 在硬件时钟中断时被调用(clr_tick_isr()函数中,5ms)
?? 扣除定时器的计数值
void rex_tick(
rex_timer_cnt_type p_ticks /* number of rex ticks to decrement everything by */
)
{
{
rex_decrement_timers( p_ticks );
#if defined( FEATURE_REX_PROFILE ) && !defined( FEATURE_HIGH_RES_PROFILE )
rex_self( )->time_samples += p_ticks;
rex_total_time_samples += p_ticks;
#endif
}
return;
} /* END rex_tick */
5.3. Others
5.3.1. rex_sleep()
?? 将当前任务挂起指定的时间
void rex_sleep(unsigned long ms)
{
boolean sleeping = TRUE;
boolean sig_set = FALSE;
unsigned long tmo = ms;
rex_timer_type sleep_timer;
rex_def_timer(
&sleep_timer,
rex_self(),
0x80000000
);
while( sleeping )
{
rex_timed_wait( 0x80000000, &sleep_timer, tmo );
REX_INTLOCK();
if( (tmo = rex_get_timer( &sleep_timer )) == 0 )
{
sleeping = FALSE;
}
else
{
sig_set = TRUE;
}
REX_INTFREE();
}
if( sig_set )
{
rex_set_sigs( rex_self(), 0x80000000 );
}
return;
} /* END rex_sleep */
6. 临界区(Critical Section)
临界区提供了一种互斥机制,以协调多个任务对有限的资源的访问。
只在任务上下文中有效,不能在中断服务程序中调用。
6.1. rex_init_crit_sect()
?? 初始化临界区的数据结构
void rex_init_crit_sect(
rex_crit_sect_type *crit_sect
)
{
crit_sect->lock_count = 0;
crit_sect->tcb_link = REX_CRIT_SECT_FLAG;
crit_sect->orig_pri = -1;
crit_sect->owner = REX_CRIT_SECT_FLAG;
return;
} /* END rex_init_crit_sect */
6.2. rex_enter_crit_sect()
?? 试图进入一个临界区
??
如果该临界区已经被其他任务锁定(即已经有任务正在临界区中),该任务将会被放入一?
龆恿兄校钡搅俳缜皇头攀痹倩叫?
void rex_enter_crit_sect(rex_crit_sect_type *crit_sect)
{
ASSERT( !TASKS_ARE_LOCKED( ) );
ASSERT( !rex_is_in_irq_mode( ) );
REX_INTLOCK();
/* mark the TCB with the critical section that we want */
ASSERT( rex_curr_task->cs_sp <
rex_curr_task->cs_stack + REX_CRIT_SECT_MAX - 1 );
/* 如果当前任务拥有该临界区,这里只是给临界区的嵌套锁计数加一,然后返回 */
if ( crit_sect->lock_count > 0 &&
rex_curr_task == crit_sect->owner)
{
ASSERT( crit_sect->lock_count < 0xff );
crit_sect->lock_count++;
}
else
{
/* 将临界区放入任务的临界区堆栈 */
++rex_curr_task->cs_sp;
*rex_curr_task->cs_sp = crit_sect;
/* 如果临界区没有上锁,则由当前任务给它加锁并使任务继续 */
if ( crit_sect->lock_count == 0 )
{
crit_sect->lock_count = 1;
crit_sect->owner = rex_curr_task;
crit_sect->tcb_link = REX_CRIT_SECT_FLAG;
/* 如果我们之前将任务的优先级进行了反转,在这里恢复它 */
crit_sect->orig_pri = rex_curr_task->pri;
}
else
{
/* 如果该临界区已经上锁,将当前任务加入到其等待链表中的头一个 */
/* previous first element's prev_ptr needs to point to us */
if (crit_sect->tcb_link != REX_CRIT_SECT_FLAG)
{
crit_sect->tcb_link->cs_link.prev_ptr = rex_curr_task;
}
/* set our next ptr to what the crit sect var previously pointed to */
rex_curr_task->cs_link.next_ptr = crit_sect->tcb_link;
/* we are the new head, so make crit sect var point to us, and set our
prev_ptr to the flag value. */
crit_sect->tcb_link = rex_curr_task;
rex_curr_task->cs_link.prev_ptr = REX_CRIT_SECT_FLAG;
/*
如果当前任务的优先级要比持有临界区的任务更高,则要进行优先级反转。暂时将其的优?
燃短嵘降鼻叭挝竦挠畔燃叮敝了头帕俳缜?/
if ( rex_curr_task->pri > crit_sect->owner->pri )
{
rex_task_pri( crit_sect->owner, rex_curr_task->pri );
}
/* Tell Dog to stop monitoring this task.
*/
REX_PAUSE_DOG_MONITOR( rex_curr_task );
/* Schedule the best runnable task.
*/
rex_set_best_task( REX_TASK_LIST_FRONT() );
rex_sched();
}
}
REX_INTFREE();
return;
} /* END rex_enter_crit_sect */
6.3. rex_leave_crit_sect_internals()
?? 释放任务持有的临界区
?? 一般情况下,任务只能释放自己持有的临界区。但假设任务A调用rex_kill_task()
试图关闭任务B。如果B正持有一个临界区,那么rex_kill_task()
必须释放它,否则其他等待该临界区的任务会被永久地阻塞下去
?? 调用rex_kill_task()时,并不希望马上调用rex_sched()
。所以这里提供了一个参数来指定是否需要进行任务调度。
?? 此函数的API版本rex_leave_crit_sect(),只允许任务释放自己持有的临界区
static void rex_leave_crit_sect_internals(
rex_crit_sect_type *crit_sect,
rex_tcb_type *p_tcb,
boolean resched_allowed
)
{
rex_tcb_type *index_ptr = NULL;
rex_tcb_type *save_ptr = NULL;
rex_priority_type best_pri;
/* No INTLOCK - calling function is responsible */
/* 检查任务临界区堆栈 */
if ( (p_tcb->cs_sp < p_tcb->cs_stack) ||
(crit_sect->owner != p_tcb) ||
(p_tcb->cs_link.next_ptr != NULL) )
{
/* calling task is not holding this lock */
ASSERT( 0 );
return;
}
/* 如果曾多次进入该临界区,这里只是将加锁次数减一,然后返回 */
if ( crit_sect->lock_count > 1 )
{
--crit_sect->lock_count;
return;
}
if (*p_tcb->cs_sp != crit_sect)
{
/* Caller is trying to release critical sections in something other
** than LIFO order. We do not support this.
*/
ASSERT( 0 );
return;
}
/* mark that we are no longer holding/waiting-in this crit section */
--p_tcb->cs_sp;
/* 如果曾经做过优先级反转,这里将任务恢复到原有的优先级 */
if ( crit_sect->orig_pri != p_tcb->pri )
{
rex_task_pri( p_tcb, crit_sect->orig_pri );
}
/* If there are no waiting tasks, we can simply unlock and proceed */
if ( crit_sect->tcb_link == REX_CRIT_SECT_FLAG )
{
crit_sect->lock_count = 0;
crit_sect->owner = REX_CRIT_SECT_FLAG;
crit_sect->orig_pri = -1;
}
else
{
/* 在等待链表中寻找优先级最高的任务,让它持有该临界区 */
best_pri = 0;
for( index_ptr = crit_sect->tcb_link;
index_ptr != REX_CRIT_SECT_FLAG;
index_ptr = index_ptr->cs_link.next_ptr
)
{
if ( index_ptr->pri > best_pri )
{
best_pri = index_ptr->pri;
save_ptr = index_ptr;
}
}
/* 将该任务从等待链表中移除*/
/* If found task is the first on the list, fix up list head */
if ( save_ptr->cs_link.prev_ptr == REX_CRIT_SECT_FLAG )
{
crit_sect->tcb_link = save_ptr->cs_link.next_ptr;
}
else /* fix up previous element */
{
save_ptr->cs_link.prev_ptr->cs_link.next_ptr =
save_ptr->cs_link.next_ptr;
}
/* look to next element*/
/* if found task is NOT the last item on the list */
if ( save_ptr->cs_link.next_ptr != REX_CRIT_SECT_FLAG )
{
save_ptr->cs_link.next_ptr->cs_link.prev_ptr =
save_ptr->cs_link.prev_ptr;
}
/* 设置该任务的cs_link
指针,表示它已经不用再等待临界区(在任务调度中,曾检查这一标志来判断任务是否被?
俳缜枞?/
save_ptr->cs_link.next_ptr = save_ptr->cs_link.prev_ptr = NULL;
/* mark the crit section appropriately */
crit_sect->owner = save_ptr;
crit_sect->orig_pri = save_ptr->pri;
/* Tell Dog to resume monitoring the found task.
*/
REX_RESUME_DOG_MONITOR( save_ptr );
/* 判断该任务是否需要调度 */
/* Always compare with REX_BEST_TASK, not REX_CURR_TASK! */
if ( ( resched_allowed ) &&
( save_ptr->pri > rex_best_task->pri ) &&
( !save_ptr->suspended ) )
{
rex_best_task = save_ptr;
rex_sched();
}
}
/* No INTFREE - calling function is responsible */
return;
} /* END rex_leave_crit_sect_internals */
相关API:rex_leave_crit_sect()
7. APC&DPC
?? APC (Asynchronous Procedure Calls)
是一种异步的程序调用机制,任务可以将一个函数调用及其参数放入另一目标任务的APC
队列中,等待该目标任务恢复运行时,就会执行该函数调用。
?? DPC (Deferred Procedure Calls)是一种延迟的程序调用机制,REX默认创建一个
DPC任务,该任务会维护一个DPC
队列,其工作就是循环读取队列中的数据,完成相应的调用工作。在这里,APC
机制最终是通过DPC排队来实现的。
7.1. DPC的实现
7.1.1. rex_dpc_task()
?? DPC任务的函数体
?? 循环等待REX_DPC_SIG,信号到来时,就执行DPC队列中的函数调用
void rex_dpc_task(unsigned long param /*lint -esym(715,param)*/)
{
for(;;)
{
/* Block waiting for a DPC to be ready
*/
(void) rex_wait( REX_DPC_SIG );
rex_clr_sigs( rex_self( ), REX_DPC_SIG );
/* Execute DPC's until the ring buffer is empty
*/
REX_INTLOCK( );
while( rex_execute_next_dpc( ) == TRUE );
INTFREE( );
}
} /* END rex_dpc_task */
7.1.2. rex_execute_next_dpc()
?? 遍历DPC队列,执行其中的函数调用
?? 只能被REX内部调用,调用时需要关中断
static boolean rex_execute_next_dpc( void )
{
rex_dpc_func_type *dpc_ptr;
unsigned long dpc_param1;
unsigned long dpc_param2;
unsigned long dpc_param3;
if( rex_dpc_rd_index != rex_dpc_wr_index )
{
dpc_ptr = rex_dpc_ring[ rex_dpc_rd_index ].dpc_ptr;
dpc_param1 = rex_dpc_ring[ rex_dpc_rd_index ].dpc_param1;
dpc_param2 = rex_dpc_ring[ rex_dpc_rd_index ].dpc_param2;
dpc_param3 = rex_dpc_ring[ rex_dpc_rd_index ].dpc_param3;
rex_dpc_rd_index = (rex_dpc_rd_index+1) & REX_DPC_RING_MASK;
if( dpc_ptr != NULL )
{
dpc_ptr( dpc_param1, dpc_param2, dpc_param3 );
}
return TRUE;
}
return FALSE;
} /* END rex_execute_next_dpc */
7.1.3. rex_queue_dpc()
?? 插入一个DPC调用到队列中
boolean rex_queue_dpc(
rex_dpc_func_type *dpc_ptr,
/* Pointer to the function to call
*/
unsigned long param1,
/* Parameter to pass to the dpc
*/
unsigned long param2,
/* Parameter to pass to the dpc
*/
unsigned long param3
/* Parameter to pass to the dpc
*/
)
{
boolean ret_status = 0;
if ( dpc_ptr == NULL )
{
return 0;
}
REX_INTLOCK( );
/* Make sure there is space in the ring buffer, add the DPC to the
** ring and signal the DPC task.
*/
if( ((rex_dpc_wr_index+1) & REX_DPC_RING_MASK) != rex_dpc_rd_index )
{
rex_dpc_ring[ rex_dpc_wr_index ].dpc_ptr = dpc_ptr;
rex_dpc_ring[ rex_dpc_wr_index ].dpc_param1 = param1;
rex_dpc_ring[ rex_dpc_wr_index ].dpc_param2 = param2;
rex_dpc_ring[ rex_dpc_wr_index ].dpc_param3 = param3;
rex_dpc_wr_index = (rex_dpc_wr_index+1) & REX_DPC_RING_MASK;
rex_set_sigs( &rex_dpc_tcb, REX_DPC_SIG );
ret_status = 1;
}
else
{
ret_status = 0;
}
INTFREE( );
return ret_status;
} /* END rex_queue_dpc */
7.2. APC的实现
7.2.1. rex_queue_apc()
?? 为一个任务排队一个APC调用
?? APC排队会使得该任务进入ready状态(tcb_ptr->num_apcs = 0 ),但只持续到
APC调用完成为止
?? 一个任务可以有多个APC排队,将按照FIFO的顺序被调用
?? 如果一个任务给自己排队一个APC
调用,该函数会被立即执行,就好像直接调用该函数一样
一个示例场景:
A、B、C、D四个任务,pri of A < pri of B < pri of C < pri of D,A、C
当前处于ready状态,C是当前任务。
?? C向B排队了一个APC调用
B进入ready状态。由于C的优先级比B高,B不会马上运行。一旦C被挂起,B
将会运行,排队的APC调用将会在B的上下文环境中被执行,一旦执行完
成。如
果B所等待的信号或事件没有发生,B将重新被挂起,A将被调度执行。
?? C向D排队了一个APC调用
D进入ready状态,而且由于其优先级最高,于是马上开始运行。APC马上在D
的上下文环境中执行,然后D又进入挂起状态,C又重新获得调度。
boolean rex_queue_apc(
rex_apc_func_type apc,
rex_tcb_type *tcb_ptr,
unsigned long param
)
{
if ( ( apc == NULL ) || ( tcb_ptr == NULL ) )
{
return 0;
}
/* 实际上是向DPC队列插入了一个rex_queue_apc_internal
的函数调用,其参数为指定的apc和任务控制块tcb指针 */
return rex_queue_dpc(
rex_queue_apc_internal,
(unsigned long) apc,
(unsigned long) tcb_ptr,
param
);
} /* END rex_queue_apc */
7.2.2. rex_queue_apc_internal()
?? 完成APC的实际排队工作
?? 只供REX内部调用,不可从ISR中断服务中调用
?? 提供用户的接口是rex_queue_apc(),它会在DPC任务的上下文中调用该函数
boolean rex_queue_apc_internal(
unsigned long apc_handle,
unsigned long tcb_handle,
unsigned long param
)
{
boolean ret_status = FALSE;
rex_apc_func_type apc = (rex_apc_func_type) apc_handle;
rex_tcb_type *tcb_ptr = (rex_tcb_type *) tcb_handle;
ASSERT( apc != NULL );
ASSERT( tcb_ptr != NULL );
ASSERT( !rex_is_in_irq_mode( ) );
/*
如果指向的是当前任务,则不进行排队。否则,检查目标任务的堆栈是否有足够空间存放?
肁PC。*/
if ( tcb_ptr == rex_curr_task )
{
/* 如果任务给自己排队了一个APC,就在这里直接调用 */
apc( param );
ret_status = TRUE;
}
else if( rex_apc_stack_avail( tcb_ptr ) )
{
REX_INTLOCK( );
rex_apc_prolog( apc, param, tcb_ptr );
tcb_ptr->num_apcs++;
/* Schedule the task for which the APC is being queued, if
** necessary.
*/
if ( ( tcb_ptr->pri > rex_best_task->pri ) &&
REX_TASK_RUNNABLE( tcb_ptr ) )
{
rex_best_task = tcb_ptr;
rex_sched( );
}
ret_status = TRUE;
INTFREE( );
}
return ret_status;
} /* END rex_queue_apc_internal*/
7.2.3. rex_apc_prolog()
?? 将一个APC的上下文帧推入目标任务的堆栈中
?? 它将填充上下文帧的如下几个域:
r0:APC函数调用的指针
r1:APC函数参数
LR:任务堆栈前一帧的PC值,值为rex_apc_epilog()。这样,当目标任务执行完PC
中的rex_call_apc()调用后,就会返回rex_apc_epilog(),完成
APC调用的后续处理(即从目标任务堆栈中弹出APC上下文帧,并恢复CPU寄存器)
PC:函数rex_call_apc()的地址,r0、r1是其两个参数
?? 只供REX内部调用
?? 不能从ISR中断服务中调用
static void rex_apc_prolog(
rex_apc_func_type apc,
unsigned long param,
rex_tcb_type *tcb_ptr
)
{
char *stack_ptr;
rex_context_frame_type *context_frame_ptr;
ASSERT( apc != NULL );
ASSERT( tcb_ptr != NULL );
ASSERT( tcb_ptr != rex_curr_task );
ASSERT( !rex_is_in_irq_mode( ) );
/* 填充上下文帧,推入相关任务的堆栈。更新TCB中的堆栈指针 */
stack_ptr = (char *) tcb_ptr->sp - sizeof(rex_context_frame_type);
ASSERT( (unsigned long) stack_ptr <
(unsigned long) tcb_ptr->stack_limit +
tcb_ptr->stack_size );
memset( (void *) stack_ptr, 0x00, sizeof(rex_context_frame_type) );
context_frame_ptr = (rex_context_frame_type *) stack_ptr;
context_frame_ptr->spsr.val =
(((rex_context_frame_type *) tcb_ptr->sp)->spsr.val &
PSR_Mode_Mask) | PSR_Thumb ;
/* If spsr_val indicates user mode, set it to supervisor mode.
*/
if ( (context_frame_ptr->spsr.val & PSR_Mode_Mask) == PSR_User )
{
context_frame_ptr->spsr.val &= ~PSR_Mode_Mask;
context_frame_ptr->spsr.val |= PSR_Supervisor;
}
context_frame_ptr->r[0].arg = (unsigned long) apc;
context_frame_ptr->r[1].arg = param;
context_frame_ptr->r[10].val = (unsigned long) tcb_ptr->stack_limit;
context_frame_ptr->lr.arg = (unsigned long) rex_apc_epilog;
context_frame_ptr->pc.arg = (unsigned long) rex_call_apc;
tcb_ptr->sp = stack_ptr;
return;
} /* rex_apc_prolog */
7.2.4. rex_call_apc()
?? 完成实际上的APC调用
?? APC调用被包在该函数中,在完成APC调用后将会作一些后续处理
?? 只能内部调用
?? 该函数在APC调用所指向的目标任务上下文中执行
?? 该函数不是直接从任何地方调用的。它是因为rex_apc_prolog()
将上下文帧推入目标任务堆栈而被调用的。在rex_apc_prolog()中,上下文帧的
PC被设为rex_call_apc()的地址,这样当CPU
寄存器被该上下文帧恢复时,该函数将会被调用。rex_apc_prolog()同时还将上下文帧的
LR值设为
rex_apc_epilog()的地址,这样当该函数返回时,rex_apc_epilog()将会被执行
void rex_call_apc(
rex_apc_func_type apc,
unsigned long param
)
{
ASSERT( apc != NULL );
/* 完成APC函数调用 */
apc( param );
/*设置best task。注意:当前的任务可能即将被挂起(其num_apcs值变为0).
**
** NOTE:我们在返回前关中断。记住程序将返回到rex_apc_epilog()。rex_apc_epilog(
)会用上下文帧的内容恢复CPU
寄存器,这些操作必须关中断。这里没有开中断与关中断相对应,这是因为
rex_apc_epilog()在恢复CPU寄存器时,任务的中断状态也将随之恢复*/
rex_int_lock( );
rex_curr_task->num_apcs--;
ASSERT( rex_curr_task->num_apcs >= 0 );
rex_set_best_task( REX_TASK_LIST_FRONT() );
return;
} /* END rex_call_apc */
7.2.5. rex_apc_epilog
?? 该函数在rex_call_apc()完成后被调用。在APC调用执行完成后,有以下两种情况:
1. 不需要切换上下文,当前任务就是best task
在这种情况下,仅将当前任务堆栈的最顶端的上下文帧弹出,并用这一帧来恢复
CPU的寄存器;
2. 需要切换上下文,当前任务将被挂起
在这种情况下,保存当前任务的堆栈指针到其TCB
中。这时不需要再保存上下文帧,因为当一个APC
排队时,已经压入了一个上下文帧。这里会将
CPU寄存器直接切换到best task
(相当于完成了一次任务调度),该函数返回后,best task开始运行,当前任务被挂起。
LEAF_NODE rex_apc_epilog
ldr a1, =rex_curr_task
ldr a2, [a1] ; Load curr task into a2.
ldr a3, =rex_best_task
ldr a3, [a3] ; Load best task into a3.
cmp a3, a2 ; Check if curr_task = best_task.
beq rex_apc_epilog_after_swap
str sp, [a2, #REX_TCB_STACK_POINTER_OFFSET]
; Update stack pointer in TCB.
str a3, [a1] ; Set curr task to best task.
ldr sp, [a3, #REX_TCB_STACK_POINTER_OFFSET]
; Load the new stack pointer.
rex_apc_epilog_after_swap
ldmfd sp!, {a2} ; Restore SPSR (in a2).
msr SPSR_f, a2 ; Load SPSR.
msr SPSR_c, a2 ; Load SPSR.
mov a2, sp ; Load sp in a2.
add sp, sp, #REX_CF_SIZE - 4 ; Adjust sp.
ldmfd a2, {r0-r12, r14, pc}^ ; Load and return
; sp already adjusted.
LEAF_NODE_END
8. REX操作系统的初始化与退出
手机启动后,引导程序会通过main()(services/task/mobile.c),加载操作系统
8.1. main()
int main(void)
{
setlocale(LC_ALL, "C");
rex_init( (void *)irq_stack, /* Interrupt stack */
/* Interrupt stack size */
IRQ_Stack_Size,
&tmc_tcb, /* Task TCB of the main control task */
(void *)tmc_stack, /* Stack for the main control task */
TMC_STACK_SIZ, /* Main control task stack size */
TMC_PRI, /* Main control task priority */
tmc_task, /* Entry point for the main control task */
0L ); /* Parameter to pass the main control task */
/* Never fall through main.*/
return 0;
}
8.2. rex_init()
?? 初始化REX
?? 它将初始化kernel task(idle task),并调用p_task主任务
void rex_init(
void * p_istack, /* interrupt stack */
rex_stack_size_type p_istksiz, /* interrupt stack size */
rex_tcb_type *p_tcb, /* task control block */
void * p_stack, /* stack */
rex_stack_size_type p_stksiz, /* stack size */
rex_priority_type p_pri, /* task priority */
void (*p_task)( dword ), /* task function */
dword p_param /* task parameter */
)
{
/*-------------------------------------------------------
** Change to Supervisor mode
**-----------------------------------------------------*/
(void)rex_set_cpsr( PSR_Supervisor | PSR_Irq_Mask | PSR_Fiq_Mask );
#if defined ( FEATURE_REX_DYNA_MEM )
rex_mem_init( );
#endif
/*-------------------------------------------------------
** Setup the interrupt stack.
**-----------------------------------------------------*/
REX_INTLOCK();
/* Point to the top of the stack */
rex_int_stack = (rex_stack_word_type *) p_istack;
/* 初始化中断嵌套为0 */
rex_int_nest_level = 0;
/*初始化时钟链表*/
rex_init_timer_list( );
/*初始化任务链表*/
rex_task_list.link.next_ptr = &rex_kernel_tcb;
rex_task_list.link.prev_ptr = NULL;
rex_kernel_tcb.link.next_ptr = NULL;
rex_kernel_tcb.link.prev_ptr = &rex_task_list;
/* rex_curr_task 必须等于 rex_best_task ,以避免在此时进行任务调度*/
rex_curr_task = &rex_kernel_tcb;
rex_best_task = &rex_kernel_tcb;
#ifdef TIMETEST
rex_kernel_tcb.leds = TIMETEST_REX_TASK_ID;
#endif
/* 启动kernel task,即idle task */
rex_def_task_ext(
&rex_kernel_tcb, /* tcb */
(unsigned char *) &rex_kernel_stack[0], /* stack */
REX_KERNEL_STACK_SIZE, /* stack size */
0, /* priority */
rex_idle_task, /* function */
0, /* arguments */
"REX Idle Task", /* task name */
FALSE /* suspended */
);
rex_kernel_tcb.pri = 0xFFFFFFFF;
#ifdef FEATURE_REX_DPC
#ifdef TIMETEST
rex_dpc_tcb.leds = TIMETEST_DPC_TASK_ID;
#endif
/* DPC task */
rex_def_task_ext(
&rex_dpc_tcb,
(unsigned char *) rex_dpc_stack,
REX_DPC_STACK_SIZE,
REX_DPC_THREAD_PRI,
rex_dpc_task,
0,
"REX DPC Task",
FALSE
);
#endif
rex_kernel_tcb.pri = 0;
/* rex_curr_task 必须等于 rex_best_task ,以避免在此时进行任务调度*/
rex_curr_task = p_tcb;
rex_best_task = p_tcb;
/*创建第一个用户任务“Main Task”*/
rex_def_task_ext(
p_tcb, /* tcb */
(unsigned char *)p_stack, /* stack */
p_stksiz*sizeof(rex_stack_word_type), /* stack size */
p_pri, /* priority */
p_task, /* function */
p_param, /* arguments */
"Main Task", /* name */
FALSE
);
INTFREE( );
/* 设置rex_best_task,进行任务调度 */
rex_set_best_task( REX_TASK_LIST_FRONT() );
rex_curr_task = rex_best_task;
rex_start_task( rex_best_task );
} /* END rex_init */
8.3. rex_exit()
?? REX退出,当一个REX断言错误(assertion fails)发生时将会被调用
?? 可以用来关闭操作系统,但现在仅仅用于设置断点以便检查REX断言错误
void rex_exit(int condition)
{
static int err_cond;
err_cond = condition;
return;
} /* END rex_exit */
8.4. rex_get_version_number()
?? 获得REX的版本号
unsigned long rex_get_version_number(void)
{
return REX_VERSION_NUMBER;
} /* END rex_get_version_number */
9. 内存管理
REX的内存管理只是简单地封装了utils下的memheap的接口函数。
9.1. rex_mem_init()
?? 初始化REX用于分配内存的堆内存区
void rex_mem_init( void )
{
mem_init_heap(
&rex_heap,
rex_mem_buffer,
sizeof( rex_mem_buffer ),
NULL
);
return;
} /* END rex_mem_init */
9.2. rex_malloc()
?? REX提供的动态内存分配接口,动态分配一块指定大小的内存
void *rex_malloc(unsigned long num_bytes)
{
ASSERT( !rex_is_in_irq_mode( ) );
return mem_malloc( &rex_heap, num_bytes );
} /* END rex_malloc */
9.3. rex_calloc()
?? 动态分配指定数目、大小的内存块
void *rex_calloc(
unsigned long num_blocks,
unsigned long block_size
)
{
ASSERT( !rex_is_in_irq_mode( ) );
return mem_calloc( &rex_heap, num_blocks, block_size );
} /* END rex_calloc */
9.4. rex_realloc()
?? 重新调整内存块的大小的同时,保存内存块原有内容
?? 重新调整内存块的大小的同时,保存内存块原有内容
9.5. rex_free()
?? 释放通过rex_malloc()分配的内存块
void rex_free(void *mem_ptr)
{
ASSERT( !rex_is_in_irq_mode( ) );
mem_free( &rex_heap, mem_ptr );
return;
} /* END rex_free */
[http://www.cnblogs.com/hongzg1982/articles/2312895.html]
在百度文库中搜索标题即可搜索到相关PDF文件
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
都拥有各自的堆栈,优先级,这些共同构成了任务的上下文。每个任务都有一个相关联的?
萁峁梗晌猅CB(任务控制块)。
REX允许在执行任意时刻创建任意数目的task
。实际上,每增加一个任务,由于遍历更长的任务链表,REX
性能会有轻微的下降。需要小心控制任务的数目。
*任务堆栈:
每个任务都有用自己的堆栈,在运行时被使用。当任务挂起时(
如运行其他任务或进行中断服务)
,任务的寄存器会被压入任务对战中,并将栈顶指针保存在任务TCB
里。等到任务被选中再次运行时,从TCB
里获取栈顶指针,将任务的寄存器值从栈顶弹出,任务于是从上次被中断的位置继续运行?
U庑┤挝袂谢坏拇矶杂谌挝窭此凳峭该鞯?可以参考【第三章 调度】)
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] = '\0';
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] = '\0';
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);
}
}
}
/*
为了避免在我们正处理调用时,另一个中断到来与之发生竞争,需要等到调用处理完成后?
跻弧V恍枰桓鯥SR实例来处理调用。 */
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中断服务入口
?? 保存上下文
?? 进入IRQ模式
?? 如果TIMETEST标志置位,则写端口
?? 调用注册的ISR(中断服务程序)
?? 恢复处理器状态
?? 恢复到原来的模式
?? 检查任务调度是否上锁。若上锁,跳过下一段,到no_switch
?? 检查是否需要作上下文切换,若需要,跳至task_swapped程序,否则转到
no_switch
?? no_switch:恢复原来的上下文,返回到原任务
?? task_swapped:进行任务上下文的切换
EXPORT IRQ_Handler
ALIGN
ROUT
IRQ_Handler
sub lr, lr, #4 ; adjust return address (ARM requirement)
; --------------------------------------------------------------------
; 某些寄存器会被中断处理程序(C
程序)所破坏,因此在中断堆栈里保存这些寄存器,也保存SPSR
(它会被嵌套中断所破坏)。
; --------------------------------------------------------------------
stmfd sp!, {r0-r3, r10, r12, r14}
mrs r0, SPSR
stmfd sp!, {r0}
; --------------------------------------------------------------------
; Increment the nesting level counter.
; --------------------------------------------------------------------
ldr r0, =rex_int_nest_level
ldrh r1, [r0]
add r1, r1, #1
strh r1, [r0]
; --------------------------------------------------------------------
; Switch to System Mode and save r14 on stack.
; --------------------------------------------------------------------
msr CPSR_c, #PSR_System:OR:PSR_Irq_Mask
stmfd sp!, {r14}
; --------------------------------------------------------------------
; 调用中断处理
; --------------------------------------------------------------------
ldr r3, =rex_irq_vector ;这里会调用tramp_isr()
ldr r3, [r3]
blatox r3
; --------------------------------------------------------------------
; Pop r14 from stack and switch to IRQ Mode.
; --------------------------------------------------------------------
ldmfd sp!, {r14}
msr CPSR_c, #PSR_Irq:OR:PSR_Irq_Mask
; --------------------------------------------------------------------
; Decrement the nesting level counter.
; --------------------------------------------------------------------
ldr r0, =rex_int_nest_level
ldrh r1, [r0]
subs r1, r1, #1 ; Notice it is subs. Set Z flag if r1==0
strh r1, [r0]
; ----------------------------------------------------------------
; Set timetest_isr_level to the new value
; ----------------------------------------------------------------
#ifdef TIMETEST_PROF
ldr r0, =timetest_isr_level
strb r1, [r0]
#endif
;---------------------------------------------------------------------
; Branch if rex_int_nest_level <> 0.
; The Z flag was set (or not set) by the subs instruction above.
;---------------------------------------------------------------------
bne nested_interrupt
;---------------------------------------------------------------------
; 是否需要任务切换,如果curr task等于best task,则不需要。
;---------------------------------------------------------------------
ldr r0, =rex_curr_task
ldr r0, [r0]
ldr r1, =rex_best_task
ldr r1, [r1]
cmp r0, r1
beq task_swap_not_required
;---------------------------------------------------------------------
; If a TASKLOCK is in effect, then we do not need to even consider
; swapping tasks.
;---------------------------------------------------------------------
; test for TASKLOCK in effect
ldr r2, =rex_sched_allow
ldr r2, [r2]
cmp r2, #0
beq task_swap_not_required
;---------------------------------------------------------------------
; Restore the SPSR from stack.
;---------------------------------------------------------------------
ldmfd sp!, {r0}
msr SPSR_f, r0
msr SPSR_c, r0
;---------------------------------------------------------------------
; Restore the registers from IRQ mode stack.
;---------------------------------------------------------------------
ldmfd sp!, {r0-r3, r10, r12, r14}
;---------------------------------------------------------------------
; 保存即将退出的任务的上下文
;---------------------------------------------------------------------
msr CPSR_c, #PSR_Supervisor:OR:PSR_Irq_Mask
sub sp, sp, #4 ; Space for Return address (pc)
stmfd sp!, {r0-r12,lr} ; Other registers
sub sp, sp, #4 ; Space for SPSR
mov r1, sp
ldr r0, =rex_curr_task ; Save sp of outgoing task into TCB.
ldr r2, [r0] ;
str r1, [r2, #REX_TCB_STACK_POINTER_OFFSET]
msr CPSR_c, #PSR_Irq:OR:PSR_Irq_Mask ; change to IRQ mode
; save Return address into context frame.
str r14, [r1, #REX_INTERRUPT_FRAME_PC_OFFSET]
mrs r3, SPSR ; save SPSR into context frame.
str r3, [r1, #REX_INTERRUPT_FRAME_SPSR_OFFSET]
; Back to Supervisor
msr CPSR_c, #PSR_Supervisor:OR:PSR_Irq_Mask
;---------------------------------------------------------------------
; 恢复新进入任务的上下文
;---------------------------------------------------------------------
; Reminder ... r0 = address of rex_curr_task variable.
;---------------------------------------------------------------------
ldr r1, =rex_best_task
ldr r1, [r1]
str r1, [r0] ; curr_task <- best_task
ldr sp, [r1, #REX_TCB_STACK_POINTER_OFFSET] ; Stack pointer
ldmfd sp!, {r0} ; Get SPSR
msr SPSR_f, r0
msr SPSR_c, r0 ; Restore SPSR.
mov a2, sp
add sp, sp, #REX_CF_SIZE - 4 ; adjust sp
ldmfd a2, {r0-r12, r14, pc}^ ; Load and return
; This will also restore CPSR.
;---------------------------------------------------------------------
; Control comes here when a task swap is not required.
;---------------------------------------------------------------------
task_swap_not_required
;---------------------------------------------------------------------
; Restore the SPSR from stack.
;---------------------------------------------------------------------
ldmfd sp!, {r0}
msr SPSR_f, r0
msr SPSR_c, r0
;-----------------------------------------------------------------
; Load saved registers and return
; This will also restore CPSR.
;-----------------------------------------------------------------
ldmfd sp!, {r0-r3, r10, r12, pc}^
;---------------------------------------------------------------------
; Control comes here when a nested interrupt continues.
;---------------------------------------------------------------------
nested_interrupt
;---------------------------------------------------------------------
; Restore the SPSR from stack.
;---------------------------------------------------------------------
ldmfd sp!, {r0}
msr SPSR_f, r0
msr SPSR_c, r0
;-----------------------------------------------------------------
; Load saved registers and return
; This will also restore CPSR.
;-----------------------------------------------------------------
ldmfd sp!, {r0-r3, r10, r12, pc}^
IRQ_Handler_end
注意:在IRQ_handler返回的时候,会进行必要的任务切换。由于在clk
模块初始化时(driver/clk/clkm2p.c:clk_init()中),设置置了系统的硬件时
钟中断:
//5ms定时
MSM_OUT( SLEEP_TIMER_0_CNT, TS_SLEEP_TMR_0_CNT_5MS );
//设置时钟中断ISR,clk_tick_isr()会调用rex_tick()对rex timers减计数
tramp_set_isr( SLEEP_TIMER_INT0, clk_tick_isr );
这样就确保了至少每5ms,REX会进行一次任务的调度。
5. 信号量(Signals)和定时器(Timers)
5.1. 信号量
每个任务可以与一组信号量(最多32个)相关联,这些信号量是TCB
的一部分,用于通知任务发生了某些事件。任务的信号量可以被任一任务或中断处理函数?
梦换蚯宄?
任务可以随时检查自身信号量的状态,而且,能把自身在自己的一个或多个信号量上?
移稹5牵挝癫豢梢约觳榛蚬移鹪谄渌挝竦男藕帕可稀?
关于信号量,有几点需要牢记:
?? 信号量是一些用途广泛的标志,其含义可以由用户任意指定。
??
任务可以挂起等待特定信号量的置位,可能是由另一个任务或中断。但置位一个并非任务?
却男藕帕浚⒉换嶂苯佑跋斓饺挝竦闹葱凶刺?
?? 一次操作就可以置位或复位与一个任务关联的全部32
个信号量的状态。同样,一个任务同时可以等待32
个信号量,任意一个信号量置位都会唤醒任
务。
5.1.1. rex_wait()
?? 若处于IRQ模式或任务调度加锁,禁止等待信号,直接退出
?? 关中断
?? 将任务挂起等待指定的信号(一个或多个信号)
?? 重新调度任务
?? 开中断
rex_sigs_type rex_wait( rex_sigs_type p_sigs/* sigs to wait on */)
{
rex_sigs_type sigs = 0;
ASSERT( !rex_is_in_irq_mode( ) );
ASSERT( !TASKS_ARE_LOCKED( ) );
REX_INTLOCK( );
/*-------------------------------------------------------
** if no signals are set, wait, and reschedule
**-----------------------------------------------------*/
if( (rex_curr_task->sigs & p_sigs) == 0 )
{
/*-------------------------------------------------------
** Set the sigs the task should wait upon
**-----------------------------------------------------*/
rex_curr_task->wait = p_sigs;
/* Tell Dog to stop monitoring this task.
*/
REX_PAUSE_DOG_MONITOR( rex_curr_task );
/*由于当前任务将要阻塞,寻找下一个最高优先级的ready任务,并重新调度*/
rex_set_best_task( REX_TASK_LIST_FRONT() );
rex_sched();
}
/*-------------------------------------------------------
** return with the signal mask of the current task
**-----------------------------------------------------*/
sigs = rex_curr_task->sigs;
INTFREE( );
return sigs;
} /* END rex_wait */
5.1.2. rex_set_sigs()
?? 关中断
?? 将指定的信号量设置给任务
?? 若任务正在等待该信号,则清除任务的等待信号列表
?? 通知DOG恢复对任务的监视
?? 若任务是最高优先级的ready任务,将其设为rex_best_task,重新调度
?? 重开中断
rex_sigs_type rex_set_sigs(
rex_tcb_type *p_tcb, /* tcb for which the sigs will be set */
rex_sigs_type p_sigs /* the sigs to set */
)
{
rex_sigs_type prev_sigs = 0; /* the sigs before setting */
REX_INTLOCK();
prev_sigs = p_tcb->sigs;
p_tcb->sigs = p_tcb->sigs | p_sigs;
/*检查该任务是否正在等待该信号。若是,清除等待信号。若任务是
rex_best_task,重新进行任务调度*/
if((p_tcb->wait & p_sigs) != 0)
{
p_tcb->wait = 0;
/* 通知DOG恢复对任务的监视 */
REX_RESUME_DOG_MONITOR( p_tcb );
if ((p_tcb->pri > rex_best_task->pri) && REX_TASK_RUNNABLE(p_tcb))
{
rex_best_task = p_tcb;
rex_sched();
}
}
REX_INTFREE();
return prev_sigs;
} /* END rex_set_sigs */
5.1.3. rex_get_sigs()
?? 获得指定任务的信号量
rex_sigs_type rex_get_sigs(
rex_tcb_type *p_tcb /* tcb for which sigs will be returned */
)
{
return p_tcb->sigs;
} /* END rex_get_sigs */
5.1.4. rex_clr_sigs()
?? 清除任务中指定的信号量
rex_sigs_type prev_sigs = 0; /* holds the signals prior to the clear */
REX_INTLOCK();
#ifdef FEATURE_REX_IPC
/* Don't clear IPC Receive Signal if a message is pending */
if ((p_sigs & p_tcb->ipc_info.receive_signal) &&
pq_is_empty(p_tcb->ipc_info.mq_ptr))
{
p_sigs &= ~p_tcb->ipc_info.receive_signal; /* IPCDEFI_RECEIVE_SIG */
}
#endif
p_sigs = ~p_sigs;
prev_sigs = p_tcb->sigs;
p_tcb->sigs = p_tcb->sigs & p_sigs;
REX_INTFREE();
return prev_sigs;
} /* END rex_clr_sigs */
5.2. 定时器
定时器用于实现软件定时计数功能。在使用时,任务需要确定定时器的间隔时间和超?
?
时将触发的信号量。任务既可以循环检查信号量的状态,也可以挂起直至定时器超时?
鹦藕胖梦弧?
定时器还可以用与关联一些设置信号量的事件,使得能够检查这些事件的超时。REX
维护了一个激活的定时器链表(即是说没有超时的)。可以创建任意数目的定时器,如同?
挝褚谎S捎诒槔奔涞脑黾樱饣岫韵低承阅苡星嵛⒌挠跋臁R殉钡亩ㄊ逼鞑换嵩黾?
系统开销。
5.2.1. rex_init_timer_list()
?? 初始化定时器链表rex_timer_list
?? 表头是一个计数值为0的定时器节点
?? rex_null_timer被插入作为末尾节点,计数值为最大值UINT_MAX
void rex_init_timer_list( void )
{
rex_timer_list.cnt = 0;
rex_null_timer.tcb_ptr = NULL;
rex_null_timer.sigs = 0x0;
rex_null_timer.cnt = UINT_MAX;
rex_null_timer.tcb_ptr = NULL;
rex_null_timer.sigs = 0x0;
rex_null_timer.link.next_ptr = NULL;
rex_null_timer.link.prev_ptr = &rex_timer_list;
rex_timer_list.link.next_ptr = &rex_null_timer;
rex_timer_list.link.prev_ptr = NULL;
return;
} /* END rex_init_timer_list */
5.2.2. rex_insert_timer()
?? 将指定的定时器按照计数值,从小到大的顺序插入定时器链表rex_timer_list
?? 任一节点的实际计数值,等于从头节点到该节点的计数值之和
void rex_insert_timer(
rex_timer_type *timer_ptr, /* pointer to a valid timer structure */
rex_timer_cnt_type tmo /* Timeout for the timer */
)
{
rex_timer_type *next_timer_ptr = rex_timer_list.link.next_ptr;
/* 定时器链表按照计数值,从小到大排列。从头节点开始遍历,每遍历一个节点,就减去
相应的计数值*/
while( tmo > next_timer_ptr->cnt )
{
REX_ASSERT( next_timer_ptr != NULL );
tmo -= next_timer_ptr->cnt;
next_timer_ptr = next_timer_ptr->link.next_ptr;
}
timer_ptr->cnt = tmo;
timer_ptr->link.prev_ptr = next_timer_ptr->link.prev_ptr;
timer_ptr->link.next_ptr = next_timer_ptr;
next_timer_ptr->link.prev_ptr->link.next_ptr = timer_ptr;
next_timer_ptr->link.prev_ptr = timer_ptr;
if ( next_timer_ptr != &rex_null_timer)
{
next_timer_ptr->cnt -= timer_ptr->cnt;
}
return;
} /* END rex_insert_timer */
5.2.3. rex_def_timer()
?? 定义了一个定时器,指定了与其相关的TCB,以及定时时间到时将设置的信号
void rex_def_timer(
rex_timer_type *p_timer, /* pointer to a valid timer structure */
rex_tcb_type *p_tcb, /* tcb to associate with the timer */
rex_sigs_type sigs /* sigs to set upon timer expiration */
)
{
p_timer->link.next_ptr = NULL;
p_timer->link.prev_ptr = NULL;
p_timer->cnt = 0;
p_timer->tcb_ptr = p_tcb;
p_timer->sigs = sigs;
#ifdef FEATURE_REX_TIMER_EX
p_timer->cb_ptr = NULL;
p_timer->cb_param = 0;
#endif
return;
} /* END rex_def_timer */
相关API:?? rex_def_timer_ex()
5.2.4. rex_set_timer()
?? 设置定时器的超时时间(毫秒为单位)
?? 首先将定时器从rex_timer_list(激活的定时器链表)中移除
?? 如果新的计数值大于0,将定时器重新插入rex_timer_list
;如果定时器已经存在与rex_timer_list中,新的计数值将会覆盖旧值
?? 如果传入的新计数值为0,定时器会马上超时(设置相应的信号)
rex_timer_cnt_type rex_set_timer(
rex_timer_type *p_timer, /* pointer to timer to set */
rex_timer_cnt_type cnt /* value to set the timer */
)
{
rex_timer_cnt_type prev_value; /* the value before the set */
INTLOCK();
/*-------------------------------------------------------
** Remove the timer from the timer list and insert it back
** if necessary.
**-----------------------------------------------------*/
prev_value = rex_clr_timer( p_timer );
if (cnt > 0)
{
rex_insert_timer( p_timer, cnt );
(void) rex_clr_sigs( p_timer->tcb_ptr, p_timer->sigs );
}
else
{
/*-------------------------------------------------------
** If the user passed in 0, set the signals or call the
** callback function.
**-----------------------------------------------------*/
do {
#ifdef FEATURE_REX_TIMER_EX
/* 如果定时器有关联的回调函数,则将它作为一个APC
排队,让它能够在正确的上下文中被调用 */
if ( p_timer->cb_ptr != NULL )
{
if ( !rex_queue_apc(
p_timer->cb_ptr,
p_timer->tcb_ptr,
p_timer->cb_param
) )
{
/* 如果APC无法被排队,需要重试。所以我们将该定时器重新插入到时钟链表的前端 */
rex_insert_timer( p_timer, 0 );
}
break;
}
#endif /* FEATURE_REX_TIMER_EX */
if ( p_timer->sigs != 0 )
{
(void)rex_set_sigs( p_timer->tcb_ptr , p_timer->sigs );
}
} while (0);
}
INTFREE();
return prev_value;
} /* END rex_set_timer */
5.2.5. rex_timed_wait()
?? 设置一个定时器的计数值,并将指定的任务挂起等待信号
rex_sigs_type rex_timed_wait(
rex_sigs_type sigs, /* sigs to wait on */
rex_timer_type *p_timer, /* timer to set and wait on */
rex_timer_cnt_type cnt /* timer to wait */
)
{
REX_ASSERT( !rex_is_in_irq_mode( ) );
/*-------------------------------------------------------
** A call to rex_set_timer and rex_wait will perform a
** timed wait
**-----------------------------------------------------*/
(void) rex_set_timer(p_timer, cnt);
return rex_wait(sigs);
} /* END rex_timed_wait */
5.2.6. rex_clr_timer()
?? 将指定定时器从定时器链表中移除
?? 将其计数值设为0
rex_timer_cnt_type rex_clr_timer(
rex_timer_type *timer_ptr /* timer to clear */
)
{
rex_timer_cnt_type prev_value = 0; /* previous count */
rex_timer_type *next_timer_ptr = &rex_timer_list;
INTLOCK( );
REX_ASSERT(timer_ptr!=NULL);
if( timer_ptr->link.next_ptr != NULL )
{
while ( next_timer_ptr != timer_ptr )
{
next_timer_ptr = next_timer_ptr->link.next_ptr;
REX_ASSERT( next_timer_ptr != NULL );
prev_value += next_timer_ptr->cnt;
}
timer_ptr->link.prev_ptr->link.next_ptr = timer_ptr->link.next_ptr;
timer_ptr->link.next_ptr->link.prev_ptr = timer_ptr->link.prev_ptr;
if ( timer_ptr->link.next_ptr != &rex_null_timer)
{
timer_ptr->link.next_ptr->cnt += timer_ptr->cnt;
}
timer_ptr->link.next_ptr = NULL;
timer_ptr->cnt = 0;
}
INTFREE( );
return prev_value;
}
5.2.7. rex_get_timer()
?? 获得定时器当前的计数值
?? 若定时器不在链表中,则返回0
rex_timer_cnt_type rex_get_timer(
rex_timer_type *timer_ptr /* pointer to the timer to get */
)
{
rex_timer_cnt_type prev_value = 0; /* previous count */
rex_timer_type *next_timer_ptr = &rex_timer_list;
INTLOCK( );
REX_ASSERT(timer_ptr!=NULL);
if( timer_ptr->link.next_ptr != NULL )
{
while ( next_timer_ptr != timer_ptr )
{
next_timer_ptr = next_timer_ptr->link.next_ptr;
REX_ASSERT( next_timer_ptr != NULL );
prev_value += next_timer_ptr->cnt;
}
}
INTFREE( );
return prev_value;
}
5.2.8. rex_pause_timer()
?? 暂停定时器
void rex_pause_timer(rex_timer_type *timer_ptr)
{
timer_ptr->cnt = rex_clr_timer( timer_ptr );
return;
} /* END rex_pause_timer */
5.2.9. rex_resume_timer()
?? 恢复定时器计数
void rex_resume_timer(rex_timer_type *timer_ptr)
{
rex_set_timer( timer_ptr, timer_ptr->cnt );
return;
} /* END rex_resume_timer */
5.2.10. rex_delete_task_timers()
?? 从定时器链表中将与指定任务关联的所有定时器移除
void rex_delete_task_timers(rex_tcb_type *tcb_ptr)
{
rex_timer_type *timer_ptr;
rex_timer_type *next_timer_ptr;
INTLOCK( );
for( timer_ptr = rex_timer_list.link.next_ptr;
timer_ptr != &rex_null_timer;
timer_ptr = next_timer_ptr )
{
next_timer_ptr = timer_ptr->link.next_ptr;
if ( timer_ptr->tcb_ptr == tcb_ptr )
{
rex_clr_timer( timer_ptr );
}
}
INTFREE( );
return;
} /* END rex_delete_task_timers */
5.2.11. rex_decrement_timers()
?? 将定时器链表中所有有效的定时器计数值扣减
?? 在调用该函数时,必须先关中断
?? 如果定时器时间到,调用rex_set_sigs()
将相应的信号量置位,如果需要,会进行任务调度
INLINE static void rex_decrement_timers(
rex_timer_cnt_type p_ticks /* number of rex ticks to decrement by */
)
{
rex_timer_type *ptr; /* points to current timer */
rex_timer_type *next_ptr; /* points to next timer */
#ifdef FEATURE_REX_TIMER_EX
unsigned long apc_queued;
#endif
/*
遍历时钟链表,扣减每个时钟的计数值。如果定时器超时,将其从链表中移除,并将相应?
挝竦男藕帕恐梦?/
for(ptr = rex_timer_list.link.next_ptr;
ptr != &rex_null_timer;
ptr = next_ptr
)
{
REX_ASSERT(ptr!=NULL);
next_ptr = ptr->link.next_ptr;
if( ptr->cnt <= p_ticks )
{
rex_clr_timer( ptr );
#ifdef FEATURE_REX_TIMER_EX
/* 如果定时器有关联的回调函数,则将它作为一个APC
排队,让它能够在正确的上下文中被调用 */
if ( ptr->cb_ptr != NULL )
{
if ( rex_is_in_irq_mode( ) )
{
apc_queued = rex_queue_apc(
ptr->cb_ptr,
ptr->tcb_ptr,
ptr->cb_param
);
}
else
{
apc_queued = rex_queue_apc_internal(
(unsigned long) ptr->cb_ptr,
(unsigned long) ptr->tcb_ptr,
ptr->cb_param
);
}
if ( !apc_queued )
{
/* 如果APC无法被排队,需要重试。所以我们将该定时器重新插入到时钟链表的前端 */
rex_insert_timer( ptr, 0 );
}
continue;
}
#endif /* FEATURE_REX_TIMER_EX */
/* 将任务的信号量置位。如果该任务是新的优先级最高的ready
任务,这将会引起一次重新调度。
Note:ptr->sigs = 0
是一个特殊情况。这意味着定时器超时时,发生了一个同步的事件。*/
/*扣减时钟链表头节点的计数值*/
ptr->cnt -= p_ticks;
break;
}
}
return;
} /* END rex_decrement_timers */
5.2.12. rex_tick()
?? REX的时间节拍函数
?? 在硬件时钟中断时被调用(clr_tick_isr()函数中,5ms)
?? 扣除定时器的计数值
void rex_tick(
rex_timer_cnt_type p_ticks /* number of rex ticks to decrement everything by */
)
{
{
rex_decrement_timers( p_ticks );
#if defined( FEATURE_REX_PROFILE ) && !defined( FEATURE_HIGH_RES_PROFILE )
rex_self( )->time_samples += p_ticks;
rex_total_time_samples += p_ticks;
#endif
}
return;
} /* END rex_tick */
5.3. Others
5.3.1. rex_sleep()
?? 将当前任务挂起指定的时间
void rex_sleep(unsigned long ms)
{
boolean sleeping = TRUE;
boolean sig_set = FALSE;
unsigned long tmo = ms;
rex_timer_type sleep_timer;
rex_def_timer(
&sleep_timer,
rex_self(),
0x80000000
);
while( sleeping )
{
rex_timed_wait( 0x80000000, &sleep_timer, tmo );
REX_INTLOCK();
if( (tmo = rex_get_timer( &sleep_timer )) == 0 )
{
sleeping = FALSE;
}
else
{
sig_set = TRUE;
}
REX_INTFREE();
}
if( sig_set )
{
rex_set_sigs( rex_self(), 0x80000000 );
}
return;
} /* END rex_sleep */
6. 临界区(Critical Section)
临界区提供了一种互斥机制,以协调多个任务对有限的资源的访问。
只在任务上下文中有效,不能在中断服务程序中调用。
6.1. rex_init_crit_sect()
?? 初始化临界区的数据结构
void rex_init_crit_sect(
rex_crit_sect_type *crit_sect
)
{
crit_sect->lock_count = 0;
crit_sect->tcb_link = REX_CRIT_SECT_FLAG;
crit_sect->orig_pri = -1;
crit_sect->owner = REX_CRIT_SECT_FLAG;
return;
} /* END rex_init_crit_sect */
6.2. rex_enter_crit_sect()
?? 试图进入一个临界区
??
如果该临界区已经被其他任务锁定(即已经有任务正在临界区中),该任务将会被放入一?
龆恿兄校钡搅俳缜皇头攀痹倩叫?
void rex_enter_crit_sect(rex_crit_sect_type *crit_sect)
{
ASSERT( !TASKS_ARE_LOCKED( ) );
ASSERT( !rex_is_in_irq_mode( ) );
REX_INTLOCK();
/* mark the TCB with the critical section that we want */
ASSERT( rex_curr_task->cs_sp <
rex_curr_task->cs_stack + REX_CRIT_SECT_MAX - 1 );
/* 如果当前任务拥有该临界区,这里只是给临界区的嵌套锁计数加一,然后返回 */
if ( crit_sect->lock_count > 0 &&
rex_curr_task == crit_sect->owner)
{
ASSERT( crit_sect->lock_count < 0xff );
crit_sect->lock_count++;
}
else
{
/* 将临界区放入任务的临界区堆栈 */
++rex_curr_task->cs_sp;
*rex_curr_task->cs_sp = crit_sect;
/* 如果临界区没有上锁,则由当前任务给它加锁并使任务继续 */
if ( crit_sect->lock_count == 0 )
{
crit_sect->lock_count = 1;
crit_sect->owner = rex_curr_task;
crit_sect->tcb_link = REX_CRIT_SECT_FLAG;
/* 如果我们之前将任务的优先级进行了反转,在这里恢复它 */
crit_sect->orig_pri = rex_curr_task->pri;
}
else
{
/* 如果该临界区已经上锁,将当前任务加入到其等待链表中的头一个 */
/* previous first element's prev_ptr needs to point to us */
if (crit_sect->tcb_link != REX_CRIT_SECT_FLAG)
{
crit_sect->tcb_link->cs_link.prev_ptr = rex_curr_task;
}
/* set our next ptr to what the crit sect var previously pointed to */
rex_curr_task->cs_link.next_ptr = crit_sect->tcb_link;
/* we are the new head, so make crit sect var point to us, and set our
prev_ptr to the flag value. */
crit_sect->tcb_link = rex_curr_task;
rex_curr_task->cs_link.prev_ptr = REX_CRIT_SECT_FLAG;
/*
如果当前任务的优先级要比持有临界区的任务更高,则要进行优先级反转。暂时将其的优?
燃短嵘降鼻叭挝竦挠畔燃叮敝了头帕俳缜?/
if ( rex_curr_task->pri > crit_sect->owner->pri )
{
rex_task_pri( crit_sect->owner, rex_curr_task->pri );
}
/* Tell Dog to stop monitoring this task.
*/
REX_PAUSE_DOG_MONITOR( rex_curr_task );
/* Schedule the best runnable task.
*/
rex_set_best_task( REX_TASK_LIST_FRONT() );
rex_sched();
}
}
REX_INTFREE();
return;
} /* END rex_enter_crit_sect */
6.3. rex_leave_crit_sect_internals()
?? 释放任务持有的临界区
?? 一般情况下,任务只能释放自己持有的临界区。但假设任务A调用rex_kill_task()
试图关闭任务B。如果B正持有一个临界区,那么rex_kill_task()
必须释放它,否则其他等待该临界区的任务会被永久地阻塞下去
?? 调用rex_kill_task()时,并不希望马上调用rex_sched()
。所以这里提供了一个参数来指定是否需要进行任务调度。
?? 此函数的API版本rex_leave_crit_sect(),只允许任务释放自己持有的临界区
static void rex_leave_crit_sect_internals(
rex_crit_sect_type *crit_sect,
rex_tcb_type *p_tcb,
boolean resched_allowed
)
{
rex_tcb_type *index_ptr = NULL;
rex_tcb_type *save_ptr = NULL;
rex_priority_type best_pri;
/* No INTLOCK - calling function is responsible */
/* 检查任务临界区堆栈 */
if ( (p_tcb->cs_sp < p_tcb->cs_stack) ||
(crit_sect->owner != p_tcb) ||
(p_tcb->cs_link.next_ptr != NULL) )
{
/* calling task is not holding this lock */
ASSERT( 0 );
return;
}
/* 如果曾多次进入该临界区,这里只是将加锁次数减一,然后返回 */
if ( crit_sect->lock_count > 1 )
{
--crit_sect->lock_count;
return;
}
if (*p_tcb->cs_sp != crit_sect)
{
/* Caller is trying to release critical sections in something other
** than LIFO order. We do not support this.
*/
ASSERT( 0 );
return;
}
/* mark that we are no longer holding/waiting-in this crit section */
--p_tcb->cs_sp;
/* 如果曾经做过优先级反转,这里将任务恢复到原有的优先级 */
if ( crit_sect->orig_pri != p_tcb->pri )
{
rex_task_pri( p_tcb, crit_sect->orig_pri );
}
/* If there are no waiting tasks, we can simply unlock and proceed */
if ( crit_sect->tcb_link == REX_CRIT_SECT_FLAG )
{
crit_sect->lock_count = 0;
crit_sect->owner = REX_CRIT_SECT_FLAG;
crit_sect->orig_pri = -1;
}
else
{
/* 在等待链表中寻找优先级最高的任务,让它持有该临界区 */
best_pri = 0;
for( index_ptr = crit_sect->tcb_link;
index_ptr != REX_CRIT_SECT_FLAG;
index_ptr = index_ptr->cs_link.next_ptr
)
{
if ( index_ptr->pri > best_pri )
{
best_pri = index_ptr->pri;
save_ptr = index_ptr;
}
}
/* 将该任务从等待链表中移除*/
/* If found task is the first on the list, fix up list head */
if ( save_ptr->cs_link.prev_ptr == REX_CRIT_SECT_FLAG )
{
crit_sect->tcb_link = save_ptr->cs_link.next_ptr;
}
else /* fix up previous element */
{
save_ptr->cs_link.prev_ptr->cs_link.next_ptr =
save_ptr->cs_link.next_ptr;
}
/* look to next element*/
/* if found task is NOT the last item on the list */
if ( save_ptr->cs_link.next_ptr != REX_CRIT_SECT_FLAG )
{
save_ptr->cs_link.next_ptr->cs_link.prev_ptr =
save_ptr->cs_link.prev_ptr;
}
/* 设置该任务的cs_link
指针,表示它已经不用再等待临界区(在任务调度中,曾检查这一标志来判断任务是否被?
俳缜枞?/
save_ptr->cs_link.next_ptr = save_ptr->cs_link.prev_ptr = NULL;
/* mark the crit section appropriately */
crit_sect->owner = save_ptr;
crit_sect->orig_pri = save_ptr->pri;
/* Tell Dog to resume monitoring the found task.
*/
REX_RESUME_DOG_MONITOR( save_ptr );
/* 判断该任务是否需要调度 */
/* Always compare with REX_BEST_TASK, not REX_CURR_TASK! */
if ( ( resched_allowed ) &&
( save_ptr->pri > rex_best_task->pri ) &&
( !save_ptr->suspended ) )
{
rex_best_task = save_ptr;
rex_sched();
}
}
/* No INTFREE - calling function is responsible */
return;
} /* END rex_leave_crit_sect_internals */
相关API:rex_leave_crit_sect()
7. APC&DPC
?? APC (Asynchronous Procedure Calls)
是一种异步的程序调用机制,任务可以将一个函数调用及其参数放入另一目标任务的APC
队列中,等待该目标任务恢复运行时,就会执行该函数调用。
?? DPC (Deferred Procedure Calls)是一种延迟的程序调用机制,REX默认创建一个
DPC任务,该任务会维护一个DPC
队列,其工作就是循环读取队列中的数据,完成相应的调用工作。在这里,APC
机制最终是通过DPC排队来实现的。
7.1. DPC的实现
7.1.1. rex_dpc_task()
?? DPC任务的函数体
?? 循环等待REX_DPC_SIG,信号到来时,就执行DPC队列中的函数调用
void rex_dpc_task(unsigned long param /*lint -esym(715,param)*/)
{
for(;;)
{
/* Block waiting for a DPC to be ready
*/
(void) rex_wait( REX_DPC_SIG );
rex_clr_sigs( rex_self( ), REX_DPC_SIG );
/* Execute DPC's until the ring buffer is empty
*/
REX_INTLOCK( );
while( rex_execute_next_dpc( ) == TRUE );
INTFREE( );
}
} /* END rex_dpc_task */
7.1.2. rex_execute_next_dpc()
?? 遍历DPC队列,执行其中的函数调用
?? 只能被REX内部调用,调用时需要关中断
static boolean rex_execute_next_dpc( void )
{
rex_dpc_func_type *dpc_ptr;
unsigned long dpc_param1;
unsigned long dpc_param2;
unsigned long dpc_param3;
if( rex_dpc_rd_index != rex_dpc_wr_index )
{
dpc_ptr = rex_dpc_ring[ rex_dpc_rd_index ].dpc_ptr;
dpc_param1 = rex_dpc_ring[ rex_dpc_rd_index ].dpc_param1;
dpc_param2 = rex_dpc_ring[ rex_dpc_rd_index ].dpc_param2;
dpc_param3 = rex_dpc_ring[ rex_dpc_rd_index ].dpc_param3;
rex_dpc_rd_index = (rex_dpc_rd_index+1) & REX_DPC_RING_MASK;
if( dpc_ptr != NULL )
{
dpc_ptr( dpc_param1, dpc_param2, dpc_param3 );
}
return TRUE;
}
return FALSE;
} /* END rex_execute_next_dpc */
7.1.3. rex_queue_dpc()
?? 插入一个DPC调用到队列中
boolean rex_queue_dpc(
rex_dpc_func_type *dpc_ptr,
/* Pointer to the function to call
*/
unsigned long param1,
/* Parameter to pass to the dpc
*/
unsigned long param2,
/* Parameter to pass to the dpc
*/
unsigned long param3
/* Parameter to pass to the dpc
*/
)
{
boolean ret_status = 0;
if ( dpc_ptr == NULL )
{
return 0;
}
REX_INTLOCK( );
/* Make sure there is space in the ring buffer, add the DPC to the
** ring and signal the DPC task.
*/
if( ((rex_dpc_wr_index+1) & REX_DPC_RING_MASK) != rex_dpc_rd_index )
{
rex_dpc_ring[ rex_dpc_wr_index ].dpc_ptr = dpc_ptr;
rex_dpc_ring[ rex_dpc_wr_index ].dpc_param1 = param1;
rex_dpc_ring[ rex_dpc_wr_index ].dpc_param2 = param2;
rex_dpc_ring[ rex_dpc_wr_index ].dpc_param3 = param3;
rex_dpc_wr_index = (rex_dpc_wr_index+1) & REX_DPC_RING_MASK;
rex_set_sigs( &rex_dpc_tcb, REX_DPC_SIG );
ret_status = 1;
}
else
{
ret_status = 0;
}
INTFREE( );
return ret_status;
} /* END rex_queue_dpc */
7.2. APC的实现
7.2.1. rex_queue_apc()
?? 为一个任务排队一个APC调用
?? APC排队会使得该任务进入ready状态(tcb_ptr->num_apcs = 0 ),但只持续到
APC调用完成为止
?? 一个任务可以有多个APC排队,将按照FIFO的顺序被调用
?? 如果一个任务给自己排队一个APC
调用,该函数会被立即执行,就好像直接调用该函数一样
一个示例场景:
A、B、C、D四个任务,pri of A < pri of B < pri of C < pri of D,A、C
当前处于ready状态,C是当前任务。
?? C向B排队了一个APC调用
B进入ready状态。由于C的优先级比B高,B不会马上运行。一旦C被挂起,B
将会运行,排队的APC调用将会在B的上下文环境中被执行,一旦执行完
成。如
果B所等待的信号或事件没有发生,B将重新被挂起,A将被调度执行。
?? C向D排队了一个APC调用
D进入ready状态,而且由于其优先级最高,于是马上开始运行。APC马上在D
的上下文环境中执行,然后D又进入挂起状态,C又重新获得调度。
boolean rex_queue_apc(
rex_apc_func_type apc,
rex_tcb_type *tcb_ptr,
unsigned long param
)
{
if ( ( apc == NULL ) || ( tcb_ptr == NULL ) )
{
return 0;
}
/* 实际上是向DPC队列插入了一个rex_queue_apc_internal
的函数调用,其参数为指定的apc和任务控制块tcb指针 */
return rex_queue_dpc(
rex_queue_apc_internal,
(unsigned long) apc,
(unsigned long) tcb_ptr,
param
);
} /* END rex_queue_apc */
7.2.2. rex_queue_apc_internal()
?? 完成APC的实际排队工作
?? 只供REX内部调用,不可从ISR中断服务中调用
?? 提供用户的接口是rex_queue_apc(),它会在DPC任务的上下文中调用该函数
boolean rex_queue_apc_internal(
unsigned long apc_handle,
unsigned long tcb_handle,
unsigned long param
)
{
boolean ret_status = FALSE;
rex_apc_func_type apc = (rex_apc_func_type) apc_handle;
rex_tcb_type *tcb_ptr = (rex_tcb_type *) tcb_handle;
ASSERT( apc != NULL );
ASSERT( tcb_ptr != NULL );
ASSERT( !rex_is_in_irq_mode( ) );
/*
如果指向的是当前任务,则不进行排队。否则,检查目标任务的堆栈是否有足够空间存放?
肁PC。*/
if ( tcb_ptr == rex_curr_task )
{
/* 如果任务给自己排队了一个APC,就在这里直接调用 */
apc( param );
ret_status = TRUE;
}
else if( rex_apc_stack_avail( tcb_ptr ) )
{
REX_INTLOCK( );
rex_apc_prolog( apc, param, tcb_ptr );
tcb_ptr->num_apcs++;
/* Schedule the task for which the APC is being queued, if
** necessary.
*/
if ( ( tcb_ptr->pri > rex_best_task->pri ) &&
REX_TASK_RUNNABLE( tcb_ptr ) )
{
rex_best_task = tcb_ptr;
rex_sched( );
}
ret_status = TRUE;
INTFREE( );
}
return ret_status;
} /* END rex_queue_apc_internal*/
7.2.3. rex_apc_prolog()
?? 将一个APC的上下文帧推入目标任务的堆栈中
?? 它将填充上下文帧的如下几个域:
r0:APC函数调用的指针
r1:APC函数参数
LR:任务堆栈前一帧的PC值,值为rex_apc_epilog()。这样,当目标任务执行完PC
中的rex_call_apc()调用后,就会返回rex_apc_epilog(),完成
APC调用的后续处理(即从目标任务堆栈中弹出APC上下文帧,并恢复CPU寄存器)
PC:函数rex_call_apc()的地址,r0、r1是其两个参数
?? 只供REX内部调用
?? 不能从ISR中断服务中调用
static void rex_apc_prolog(
rex_apc_func_type apc,
unsigned long param,
rex_tcb_type *tcb_ptr
)
{
char *stack_ptr;
rex_context_frame_type *context_frame_ptr;
ASSERT( apc != NULL );
ASSERT( tcb_ptr != NULL );
ASSERT( tcb_ptr != rex_curr_task );
ASSERT( !rex_is_in_irq_mode( ) );
/* 填充上下文帧,推入相关任务的堆栈。更新TCB中的堆栈指针 */
stack_ptr = (char *) tcb_ptr->sp - sizeof(rex_context_frame_type);
ASSERT( (unsigned long) stack_ptr <
(unsigned long) tcb_ptr->stack_limit +
tcb_ptr->stack_size );
memset( (void *) stack_ptr, 0x00, sizeof(rex_context_frame_type) );
context_frame_ptr = (rex_context_frame_type *) stack_ptr;
context_frame_ptr->spsr.val =
(((rex_context_frame_type *) tcb_ptr->sp)->spsr.val &
PSR_Mode_Mask) | PSR_Thumb ;
/* If spsr_val indicates user mode, set it to supervisor mode.
*/
if ( (context_frame_ptr->spsr.val & PSR_Mode_Mask) == PSR_User )
{
context_frame_ptr->spsr.val &= ~PSR_Mode_Mask;
context_frame_ptr->spsr.val |= PSR_Supervisor;
}
context_frame_ptr->r[0].arg = (unsigned long) apc;
context_frame_ptr->r[1].arg = param;
context_frame_ptr->r[10].val = (unsigned long) tcb_ptr->stack_limit;
context_frame_ptr->lr.arg = (unsigned long) rex_apc_epilog;
context_frame_ptr->pc.arg = (unsigned long) rex_call_apc;
tcb_ptr->sp = stack_ptr;
return;
} /* rex_apc_prolog */
7.2.4. rex_call_apc()
?? 完成实际上的APC调用
?? APC调用被包在该函数中,在完成APC调用后将会作一些后续处理
?? 只能内部调用
?? 该函数在APC调用所指向的目标任务上下文中执行
?? 该函数不是直接从任何地方调用的。它是因为rex_apc_prolog()
将上下文帧推入目标任务堆栈而被调用的。在rex_apc_prolog()中,上下文帧的
PC被设为rex_call_apc()的地址,这样当CPU
寄存器被该上下文帧恢复时,该函数将会被调用。rex_apc_prolog()同时还将上下文帧的
LR值设为
rex_apc_epilog()的地址,这样当该函数返回时,rex_apc_epilog()将会被执行
void rex_call_apc(
rex_apc_func_type apc,
unsigned long param
)
{
ASSERT( apc != NULL );
/* 完成APC函数调用 */
apc( param );
/*设置best task。注意:当前的任务可能即将被挂起(其num_apcs值变为0).
**
** NOTE:我们在返回前关中断。记住程序将返回到rex_apc_epilog()。rex_apc_epilog(
)会用上下文帧的内容恢复CPU
寄存器,这些操作必须关中断。这里没有开中断与关中断相对应,这是因为
rex_apc_epilog()在恢复CPU寄存器时,任务的中断状态也将随之恢复*/
rex_int_lock( );
rex_curr_task->num_apcs--;
ASSERT( rex_curr_task->num_apcs >= 0 );
rex_set_best_task( REX_TASK_LIST_FRONT() );
return;
} /* END rex_call_apc */
7.2.5. rex_apc_epilog
?? 该函数在rex_call_apc()完成后被调用。在APC调用执行完成后,有以下两种情况:
1. 不需要切换上下文,当前任务就是best task
在这种情况下,仅将当前任务堆栈的最顶端的上下文帧弹出,并用这一帧来恢复
CPU的寄存器;
2. 需要切换上下文,当前任务将被挂起
在这种情况下,保存当前任务的堆栈指针到其TCB
中。这时不需要再保存上下文帧,因为当一个APC
排队时,已经压入了一个上下文帧。这里会将
CPU寄存器直接切换到best task
(相当于完成了一次任务调度),该函数返回后,best task开始运行,当前任务被挂起。
LEAF_NODE rex_apc_epilog
ldr a1, =rex_curr_task
ldr a2, [a1] ; Load curr task into a2.
ldr a3, =rex_best_task
ldr a3, [a3] ; Load best task into a3.
cmp a3, a2 ; Check if curr_task = best_task.
beq rex_apc_epilog_after_swap
str sp, [a2, #REX_TCB_STACK_POINTER_OFFSET]
; Update stack pointer in TCB.
str a3, [a1] ; Set curr task to best task.
ldr sp, [a3, #REX_TCB_STACK_POINTER_OFFSET]
; Load the new stack pointer.
rex_apc_epilog_after_swap
ldmfd sp!, {a2} ; Restore SPSR (in a2).
msr SPSR_f, a2 ; Load SPSR.
msr SPSR_c, a2 ; Load SPSR.
mov a2, sp ; Load sp in a2.
add sp, sp, #REX_CF_SIZE - 4 ; Adjust sp.
ldmfd a2, {r0-r12, r14, pc}^ ; Load and return
; sp already adjusted.
LEAF_NODE_END
8. REX操作系统的初始化与退出
手机启动后,引导程序会通过main()(services/task/mobile.c),加载操作系统
8.1. main()
int main(void)
{
setlocale(LC_ALL, "C");
rex_init( (void *)irq_stack, /* Interrupt stack */
/* Interrupt stack size */
IRQ_Stack_Size,
&tmc_tcb, /* Task TCB of the main control task */
(void *)tmc_stack, /* Stack for the main control task */
TMC_STACK_SIZ, /* Main control task stack size */
TMC_PRI, /* Main control task priority */
tmc_task, /* Entry point for the main control task */
0L ); /* Parameter to pass the main control task */
/* Never fall through main.*/
return 0;
}
8.2. rex_init()
?? 初始化REX
?? 它将初始化kernel task(idle task),并调用p_task主任务
void rex_init(
void * p_istack, /* interrupt stack */
rex_stack_size_type p_istksiz, /* interrupt stack size */
rex_tcb_type *p_tcb, /* task control block */
void * p_stack, /* stack */
rex_stack_size_type p_stksiz, /* stack size */
rex_priority_type p_pri, /* task priority */
void (*p_task)( dword ), /* task function */
dword p_param /* task parameter */
)
{
/*-------------------------------------------------------
** Change to Supervisor mode
**-----------------------------------------------------*/
(void)rex_set_cpsr( PSR_Supervisor | PSR_Irq_Mask | PSR_Fiq_Mask );
#if defined ( FEATURE_REX_DYNA_MEM )
rex_mem_init( );
#endif
/*-------------------------------------------------------
** Setup the interrupt stack.
**-----------------------------------------------------*/
REX_INTLOCK();
/* Point to the top of the stack */
rex_int_stack = (rex_stack_word_type *) p_istack;
/* 初始化中断嵌套为0 */
rex_int_nest_level = 0;
/*初始化时钟链表*/
rex_init_timer_list( );
/*初始化任务链表*/
rex_task_list.link.next_ptr = &rex_kernel_tcb;
rex_task_list.link.prev_ptr = NULL;
rex_kernel_tcb.link.next_ptr = NULL;
rex_kernel_tcb.link.prev_ptr = &rex_task_list;
/* rex_curr_task 必须等于 rex_best_task ,以避免在此时进行任务调度*/
rex_curr_task = &rex_kernel_tcb;
rex_best_task = &rex_kernel_tcb;
#ifdef TIMETEST
rex_kernel_tcb.leds = TIMETEST_REX_TASK_ID;
#endif
/* 启动kernel task,即idle task */
rex_def_task_ext(
&rex_kernel_tcb, /* tcb */
(unsigned char *) &rex_kernel_stack[0], /* stack */
REX_KERNEL_STACK_SIZE, /* stack size */
0, /* priority */
rex_idle_task, /* function */
0, /* arguments */
"REX Idle Task", /* task name */
FALSE /* suspended */
);
rex_kernel_tcb.pri = 0xFFFFFFFF;
#ifdef FEATURE_REX_DPC
#ifdef TIMETEST
rex_dpc_tcb.leds = TIMETEST_DPC_TASK_ID;
#endif
/* DPC task */
rex_def_task_ext(
&rex_dpc_tcb,
(unsigned char *) rex_dpc_stack,
REX_DPC_STACK_SIZE,
REX_DPC_THREAD_PRI,
rex_dpc_task,
0,
"REX DPC Task",
FALSE
);
#endif
rex_kernel_tcb.pri = 0;
/* rex_curr_task 必须等于 rex_best_task ,以避免在此时进行任务调度*/
rex_curr_task = p_tcb;
rex_best_task = p_tcb;
/*创建第一个用户任务“Main Task”*/
rex_def_task_ext(
p_tcb, /* tcb */
(unsigned char *)p_stack, /* stack */
p_stksiz*sizeof(rex_stack_word_type), /* stack size */
p_pri, /* priority */
p_task, /* function */
p_param, /* arguments */
"Main Task", /* name */
FALSE
);
INTFREE( );
/* 设置rex_best_task,进行任务调度 */
rex_set_best_task( REX_TASK_LIST_FRONT() );
rex_curr_task = rex_best_task;
rex_start_task( rex_best_task );
} /* END rex_init */
8.3. rex_exit()
?? REX退出,当一个REX断言错误(assertion fails)发生时将会被调用
?? 可以用来关闭操作系统,但现在仅仅用于设置断点以便检查REX断言错误
void rex_exit(int condition)
{
static int err_cond;
err_cond = condition;
return;
} /* END rex_exit */
8.4. rex_get_version_number()
?? 获得REX的版本号
unsigned long rex_get_version_number(void)
{
return REX_VERSION_NUMBER;
} /* END rex_get_version_number */
9. 内存管理
REX的内存管理只是简单地封装了utils下的memheap的接口函数。
9.1. rex_mem_init()
?? 初始化REX用于分配内存的堆内存区
void rex_mem_init( void )
{
mem_init_heap(
&rex_heap,
rex_mem_buffer,
sizeof( rex_mem_buffer ),
NULL
);
return;
} /* END rex_mem_init */
9.2. rex_malloc()
?? REX提供的动态内存分配接口,动态分配一块指定大小的内存
void *rex_malloc(unsigned long num_bytes)
{
ASSERT( !rex_is_in_irq_mode( ) );
return mem_malloc( &rex_heap, num_bytes );
} /* END rex_malloc */
9.3. rex_calloc()
?? 动态分配指定数目、大小的内存块
void *rex_calloc(
unsigned long num_blocks,
unsigned long block_size
)
{
ASSERT( !rex_is_in_irq_mode( ) );
return mem_calloc( &rex_heap, num_blocks, block_size );
} /* END rex_calloc */
9.4. rex_realloc()
?? 重新调整内存块的大小的同时,保存内存块原有内容
?? 重新调整内存块的大小的同时,保存内存块原有内容
9.5. rex_free()
?? 释放通过rex_malloc()分配的内存块
void rex_free(void *mem_ptr)
{
ASSERT( !rex_is_in_irq_mode( ) );
mem_free( &rex_heap, mem_ptr );
return;
} /* END rex_free */
[http://www.cnblogs.com/hongzg1982/articles/2312895.html]
相关文章推荐
- onDestory清除webview报错
- ON_COMMAND和ON_UPDATE_COMMAND_UI
- Solr Dataimporthandler 导入MySQL 内存溢出。
- DISPO与EKGRP 的关系(MRP控制者和采购组)
- cordova插件编写
- webwxgetcontact 获取的是通讯录里的群或好友的信息
- jquery移除追加元素
- 纯css打造下拉菜单
- 关于Linux的内存(free -m)
- 使用bower时报错
- 利用F10五步读懂一个公司
- 简介Python设计模式中的代理模式与模板方法模式编程
- linux 用户空间与内核空间——高端内存详解
- 上帝的归上帝 凯撒的归凯撒
- Android应用性能优化之使用SparseArray替代HashMap
- Unity android Texture Opengl ES 数据通信
- 竞价猎马技术具体操作
- Liunx 和 Win中的软链接详解
- python格式化字符串Type Error: Format Requires Mapping 的问题
- 面向对象基础加强