在alios things启动过程中,krhino_init中,做完memory堆初始化,就会初始化进程相关的东西。在分析的过程中,默认RHINO_CONFIG_KOBJ_LIST,RHINO_CONFIG_KOBJ_DYN_ALLOC,RHINO_CONFIG_SCHED_RR都使能,RHINO_CONFIG_CPU_NUM为1。RHINO_CONFIG_SCHED_RR表示高优先级会抢占低优先级,高优先级运行期间,低优先级没法抢占,只能等到高优先级主动退出,对于同等优先级的进程,各个进程会轮流运行一定的时间片。
我们先看下krhino_init初始化过程:
kstat_t krhino_init(void)
{
/*设置系统为RHINO_STOPPED状态*/
g_sys_stat = RHINO_STOPPED;
/*RHINO_CONFIG_CPU_NUM = 1跳过g_sys_lock和g_task_del_head初始化*/
#if (RHINO_CONFIG_CPU_NUM > 1)
krhino_spin_lock_init(&g_sys_lock);
klist_init(&g_task_del_head);
#endif
/*初始化就绪队列g_ready_queue*/
runqueue_init(&g_ready_queue);
/*初始化tick链表,这个也和进程切换相关*/
tick_list_init();
#if (RHINO_CONFIG_KOBJ_LIST > 0)
/*初始化kobj链表*/
kobj_list_init();
#endif
/*RHINO_CONFIG_USER_HOOK 当前先不开启,后续章节分析*/
#if (RHINO_CONFIG_USER_HOOK > 0)
krhino_init_hook();
#endif
/*堆内存初始化,前两章已经分析过*/
#if (RHINO_CONFIG_MM_TLF > 0)
k_mm_init();
#endif
#if (RHINO_CONFIG_KOBJ_DYN_ALLOC > 0)
/*初始化进程资源释放相关的链表, 信号量以及释放管理进程*/
klist_init(&g_res_list);
krhino_sem_create(&g_res_sem, "res_sem", 0);
/*dyn_mem_proc_task_start会创建一个dyn_mem_proc_task,调度器开始后就启动这个用来释放进程资源的进程
这是创建的第一个task*/
dyn_mem_proc_task_start();
#endif
/*RHINO_CONFIG_CPU_NUM = 1*/
#if (RHINO_CONFIG_CPU_NUM > 1)
for (uint8_t i = 0; i < RHINO_CONFIG_CPU_NUM; i++) {
krhino_task_cpu_create(&g_idle_task[i], "idle_task", NULL, RHINO_IDLE_PRI, 0,
&g_idle_task_stack[i][0], RHINO_CONFIG_IDLE_TASK_STACK_SIZE,
idle_task, i, 1u);
}
#else
/*创建idle进程,调度器开始后正式启动idle进程,这是创建的第二个task*/
krhino_task_create(&g_idle_task[0], "idle_task", NULL, RHINO_IDLE_PRI, 0,
&g_idle_task_stack[0][0], RHINO_CONFIG_IDLE_TASK_STACK_SIZE,
idle_task, 1u);
#endif
#if (RHINO_CONFIG_WORKQUEUE > 0)
/*初始化workqueue,这是创建的第三个task*/
workqueue_init();
#endif
#if (RHINO_CONFIG_TIMER > 0)
/*初始化时钟,比较重要,后面分析,这是创建的第四个task*/
ktimer_init();
#endif
/*用来检查栈溢出,目前没使能不分析*/
rhino_stack_check_init();
return RHINO_SUCCESS;
}
void runqueue_init(runqueue_t *rq)
{
uint8_t prio;
rq->highest_pri = RHINO_CONFIG_PRI_MAX;
for (prio = 0; prio < RHINO_CONFIG_PRI_MAX; prio++) {
rq->cur_list_item[prio] = NULL; //所有优先级的链表先置空
}
}
#define RHINO_CONFIG_PRI_MAX 62
#define NUM_WORDS ((RHINO_CONFIG_PRI_MAX + 31) / 32)
typedef struct {
klist_t *cur_list_item[RHINO_CONFIG_PRI_MAX]; //一共有62个进程链表, 每个链表挂不同优先级的进程
uint32_t task_bit_map[NUM_WORDS]; //每个bit位置1,代表一种优先级的进程存在。
uint8_t highest_pri; //队列上进程的最高优先级
} runqueue_t;
这里我们单独看下dyn_mem_proc_task这个进程:
void dyn_mem_proc_task(void *arg)
{
/*申明cpsr,后边RHINO_CRITICAL_ENTER/RHINO_CRITICAL_EXIT调用需要*/
CPSR_ALLOC();
size_t i;
kstat_t ret;
res_free_t *res_free;
res_free_t tmp;
(void)arg;
while (1) {
/*无限睡眠等待g_res_sem资源被释放*/
ret = krhino_sem_take(&g_res_sem, RHINO_WAIT_FOREVER);
if (ret != RHINO_SUCCESS) {
k_err_proc(RHINO_DYN_MEM_PROC_ERR);
}
while (1) {
/*保存当前中断状态,并关中断*/
RHINO_CRITICAL_ENTER();
/*查看g_res_list是否为空, 为空则跳出while(1),否则直到释放完g_res_list上的资源*/
if (!is_klist_empty(&g_res_list)) {
/*不为空,则将g_res_list上的对象取下来,放到res_free上*/
res_free = krhino_list_entry(g_res_list.next, res_free_t, res_list);
klist_rm(&res_free->res_list);
/*恢复之前的中断状态*/
RHINO_CRITICAL_EXIT();
/*释放res_free资源*/
#if (RHINO_CONFIG_USER_SPACE > 0)
proc_free((ktask_t *)(res_free->res[1]));
#endif
memcpy(&tmp, res_free, sizeof(res_free_t));
for (i = 0; i < tmp.cnt; i++) {
krhino_mm_free(tmp.res[i]);
}
} else {
/*恢复之前的中断状态*/
RHINO_CRITICAL_EXIT();
break;
}
}
}
}
#define RHINO_CPU_INTRPT_DISABLE() do{cpsr = cpu_intrpt_save();}while(0)
#define RHINO_CPU_INTRPT_ENABLE() do{cpu_intrpt_restore(cpsr);}while(0)
; Functions:
; size_t cpu_intrpt_save(void);
; void cpu_intrpt_restore(size_t cpsr);
;******************************************************************************
cpu_intrpt_save
/*加载特殊寄存器的值(当前中断状态)到通用寄存器R0保存*/
MRS R0, PRIMASK
/*关中断*/
CPSID I
BX LR
cpu_intrpt_restore
/*将通用寄存器R0(之前的中断状态)恢复到特殊寄存器*/
MSR PRIMASK, R0
/*之所以这边没有CPSIE I来开启中断,是因为这里要的是中断状态恢复原状,
由于R0存的是之前的中断状态,如果之前是开启的,那么现在恢复到开启状态,
如果之前是关闭的,那么现在恢复后依然关闭*/
BX LR
在上面krhino_init整个过程中,一共调用了四次task_create,分别创建了dyn_mem_proc_task, idle_task, DEFAULT-WORKQUEUE,timer_task,我们先看下这个重要的task_create:
/*
ktask_t *task: 出参,用来记录task初始化后的各种task参数
const name_t *name:入参,设定的task名
void *arg:入参:task entry函数执行时的入参
uint8_t prio:入参:task优先级
tick_t ticks:入参:当设置SCHED_RR调度方式,同一优先级进程的时间片执行时长。
cpu_stack_t *stack_buf:入参: task自己可用的栈buffer地址(已分配)
size_t stack_size:入参: task自己可用的栈长度(已分配)
task_entry_t entry:入参:task执行的函数
uint8_t autorun:入参:是否自动执行,设置1的话,task初始化后进入就绪K_RDY状态,设置0的话,task初始化后进入K_SUSPENDED状态。
uint8_t mm_alloc_flag:入参:K_OBJ_DYN_ALLOC/K_OBJ_STATIC_ALLOC,设置栈是动态申请的还是静态分配的,涉及到是否回收(系统task的堆栈一般都静态分配)
uint8_t cpu_num: 入参: CPU亲缘性,指定哪个cpu
uint8_t cpu_binded:入参:是否绑定cpu
*/
static kstat_t task_create(ktask_t *task, const name_t *name, void *arg,
uint8_t prio, tick_t ticks, cpu_stack_t *stack_buf,
size_t stack_size, task_entry_t entry, uint8_t autorun,
uint8_t mm_alloc_flag, uint8_t cpu_num, uint8_t cpu_binded)
{
CPSR_ALLOC();
cpu_stack_t *tmp;
uint8_t i = 0;
NULL_PARA_CHK(task);
NULL_PARA_CHK(name);
NULL_PARA_CHK(entry);
NULL_PARA_CHK(stack_buf);
if (stack_size == 0u) {
return RHINO_TASK_INV_STACK_SIZE;
}
if (prio >= RHINO_CONFIG_PRI_MAX) {
return RHINO_BEYOND_MAX_PRI;
}
RHINO_CRITICAL_ENTER();
INTRPT_NESTED_LEVEL_CHK();
/* idle task is only allowed to create once */
/*设定某一个CPU的idle task,对于每个CPU只能设定一个idle task*/
if (prio == RHINO_IDLE_PRI) {
if (g_idle_task_spawned[cpu_num] > 0u) {
RHINO_CRITICAL_EXIT();
return RHINO_IDLE_TASK_EXIST;
}
g_idle_task_spawned[cpu_num] = 1u;
}
RHINO_CRITICAL_EXIT();
memset(task, 0, sizeof(ktask_t));
#if (RHINO_CONFIG_SCHED_RR > 0)
/*设定每个task的可用时间片总时长*/
if (ticks > 0u) {
task->time_total = ticks;
} else {
task->time_total = RHINO_CONFIG_TIME_SLICE_DEFAULT;
}
/*设定每个task的时间片个数,以及调度策略*/
task->time_slice = task->time_total;
task->sched_policy = KSCHED_RR;
#endif
/*如果autorun>0,设置成就绪状态,否则先挂起*/
if (autorun > 0u) {
task->task_state = K_RDY;
} else {
task->task_state = K_SUSPENDED;
task->suspend_count = 1u;
}
/*设置堆栈地址*/
/* init all the stack element to 0 */
task->task_stack_base = stack_buf;
tmp = stack_buf;
/*清空堆栈*/
memset(tmp, 0, stack_size * sizeof(cpu_stack_t));
task->task_name = name;
task->prio = prio;
task->b_prio = prio;
task->stack_size = stack_size;
task->mm_alloc_flag = mm_alloc_flag;
task->cpu_num = cpu_num;
#if (RHINO_CONFIG_USER_SPACE > 0)
task->mode = 0;
task->pid = 0;
task->task_ustack_base = 0;
task->task_group = 0;
#endif
/*冗余代码,不知道做什么??*/
cpu_binded = cpu_binded;
i = i;
#if (RHINO_CONFIG_CPU_NUM > 1)
task->cpu_binded = cpu_binded;
#endif
/*检查task堆栈是否会有溢出问题*/
#if (RHINO_CONFIG_TASK_STACK_OVF_CHECK > 0)
/*task堆栈由高地址往低地址增长*/
#if (RHINO_CONFIG_CPU_STACK_DOWN > 0)
tmp = task->task_stack_base;
/*将堆栈最低的地址打上堆栈溢出magic标记0xdeadbeafu*/
for (i = 0; i < RHINO_CONFIG_STK_CHK_WORDS; i++) {
*tmp++ = RHINO_TASK_STACK_OVF_MAGIC;
}
#else
tmp = (cpu_stack_t *)(task->task_stack_base) + task->stack_size - RHINO_CONFIG_STK_CHK_WORDS;
for (i = 0; i < RHINO_CONFIG_STK_CHK_WORDS; i++) {
*tmp++ = RHINO_TASK_STACK_OVF_MAGIC;
}
#endif
#endif
/*初始化堆栈,并设置到task_stack上, cpu_task_stack_init分析见下面*/
task->task_stack = cpu_task_stack_init(stack_buf, stack_size, arg, entry);
#if (RHINO_CONFIG_USER_HOOK > 0)
krhino_task_create_hook(task);
#endif
/*目前没有支持*/
TRACE_TASK_CREATE(task);
RHINO_CRITICAL_ENTER();
#if (RHINO_CONFIG_KOBJ_LIST > 0)
/*将task->task_stats_item挂到g_kobj_list链表的task_head上管理,g_kobj_list结构里管理着所有task,mutex等资源*/
klist_insert(&(g_kobj_list.task_head), &task->task_stats_item);
#endif
/*autorun 为正数,表示初始化完后直接设置就绪,可以运行。*/
if (autorun > 0u) {
/*将task加到就绪队列上*/
ready_list_add_tail(&g_ready_queue, task);
/* if system is not start,not call core_sched */
/*如果当前系统为RHINO_STOPPED状态,也就是调度器未启动状态,先不做调度。*/
if (g_sys_stat == RHINO_RUNNING) {
RHINO_CRITICAL_EXIT_SCHED();
return RHINO_SUCCESS;
}
}
RHINO_CRITICAL_EXIT();
return RHINO_SUCCESS;
}
RHINO_INLINE void _ready_list_add_tail(runqueue_t *rq, ktask_t *task)
{
/*判断这个task设定的优先级,在就绪队列上这个优先级的链表是否为空,为空就初始化这个队列*/
if (is_ready_list_empty(task->prio)) {
ready_list_init(rq, task);
return;
}
/*不为空的话,将这个task直接放到queue上的该优先级的列表就行*/
klist_insert(rq->cur_list_item[task->prio], &task->task_list);
}
RHINO_INLINE void ready_list_init(runqueue_t *rq, ktask_t *task)
{
rq->cur_list_item[task->prio] = &task->task_list;
klist_init(rq->cur_list_item[task->prio]);
krhino_bitmap_set(rq->task_bit_map, task->prio);
/*如果这个新增的优先级高于队列原来已有的最高优先级,那么将优先级赋值给rq->highest_pri*/
if ((task->prio) < (rq->highest_pri)) {
rq->highest_pri = task->prio;
}
}
void *cpu_task_stack_init(cpu_stack_t *stack_base, size_t stack_size,
void *arg, task_entry_t entry)
{
cpu_stack_t *stk;
/*temp指向栈顶部(最高地址处)*/
uint32_t temp = (uint32_t)(stack_base + stack_size);
/* stack aligned by 8 byte */
temp &= 0xfffffff8;
stk = (cpu_stack_t *)temp;
/*由高地址到低地址,初始化以下寄存器*/
/* task context saved & restore by hardware: */
*(--stk) = (cpu_stack_t)0x01000000L; /* xPSR: EPSR.T = 1, thumb mode */
*(--stk) = (cpu_stack_t)entry; /* Entry Point */
*(--stk) = (cpu_stack_t)krhino_task_deathbed; /* R14 (LR) entry函数执行完返回后的处理 */
*(--stk) = (cpu_stack_t)0x12121212L; /* R12 */
*(--stk) = (cpu_stack_t)0x03030303L; /* R3 */
*(--stk) = (cpu_stack_t)0x02020202L; /* R2 */
*(--stk) = (cpu_stack_t)0x01010101L; /* R1 */
*(--stk) = (cpu_stack_t)arg; /* R0 : argument entry的入参 */
/* task context saved & restore by software: */
/* EXC_RETURN = 0xFFFFFFFDL
Task begin state: Thread mode + non-floating-point state + PSP */
*(--stk) = (cpu_stack_t)0xFFFFFFFDL;
*(--stk) = (cpu_stack_t)0x11111111L; /* R11 */
*(--stk) = (cpu_stack_t)0x10101010L; /* R10 */
*(--stk) = (cpu_stack_t)0x09090909L; /* R9 */
*(--stk) = (cpu_stack_t)0x08080808L; /* R8 */
*(--stk) = (cpu_stack_t)0x07070707L; /* R7 */
*(--stk) = (cpu_stack_t)0x06060606L; /* R6 */
*(--stk) = (cpu_stack_t)0x05050505L; /* R5 */
*(--stk) = (cpu_stack_t)0x04040404L; /* R4 */
return stk;
}
至此,task_create创建完成,如果在系统没有初始化完,未running状态,则新建的task不会走调度流程,如果在系统初始化完成后,task才会有机会按照优先级判定,执行一次调度。
由于之前分析过dyn_mem_proc_task,我们接着看另外三个idle_task, DEFAULT-WORKQUEUE,timer_task的task实体函数如何执行的。
idle_task在RHINO_CONFIG_CPU_NUM =1, RHINO_CONFIG_USER_HOOK=0,RHINO_CONFIG_PWRMGMT=0的情况下,基本就是个while(1)的死循环,我们在多核的时候分析。
DEFAULT-WORKQUEUE的创建和普通的workqueu并没有太大区别,后续workqueue章节再分析。
下面粗略看timer_task也就是定时器相关的初始化:
void ktimer_init(void)
{
/*首先初始化g_timer_head*/
klist_init(&g_timer_head);
/*初始化timer_queue的buf queue,alios中的buf queu相当于freertos中的msg queue,用来做消息队列*/
krhino_fix_buf_queue_create(&g_timer_queue, "timer_queue", timer_queue_cb,
sizeof(k_timer_queue_cb), RHINO_CONFIG_TIMER_MSG_NUM);
/*定时器处理程序,这部分逻辑单独拿出一章做分析。*/
krhino_task_create(&g_timer_task, "timer_task", NULL,
RHINO_CONFIG_TIMER_TASK_PRI, 0u, g_timer_task_stack,
RHINO_CONFIG_TIMER_TASK_STACK_SIZE, timer_task, 1u);
}
至此, krhino_init流程差不多了,后面再初始化了一个main_task,用来直接运行用户态的应用程序,之后就进入krhino_start()启动调度器,开始做进程调度。
int main(void)
{
/*irq initialized is approved here.But irq triggering is forbidden, which will enter CPU scheduling.
Put them in sys_init which will be called after aos_start.
Irq for task schedule should be enabled here, such as PendSV for cortex-M4.
*/
board_init(); //including aos_heap_set(); flash_partition_init();
/*kernel init, malloc can use after this!*/
krhino_init();
/*main task to run */
krhino_task_dyn_create(&g_main_task, "main_task", 0, OS_MAIN_TASK_PRI, 0, OS_MAIN_TASK_STACK, (task_entry_t)sys_init, 1);
/*kernel start schedule!*/
krhino_start();
/*never run here*/
return 0;
}
kstat_t krhino_start(void)
{
ktask_t *preferred_task;
/*调度器未启动时,g_sys_stat 为RHINO_STOPPED状态*/
if (g_sys_stat == RHINO_STOPPED) {
#if (RHINO_CONFIG_CPU_NUM > 1)
for (uint8_t i = 0; i < RHINO_CONFIG_CPU_NUM; i++) {
preferred_task = preferred_cpu_ready_task_get(&g_ready_queue, i);
preferred_task->cpu_num = i;
preferred_task->cur_exc = 1;
g_preferred_ready_task[i] = preferred_task;
g_active_task[i] = g_preferred_ready_task[i];
g_active_task[i]->cur_exc = 1;
}
#else
/*preferred_task取出就绪队列上优先级最高的任务*/
preferred_task = preferred_cpu_ready_task_get(&g_ready_queue, 0);
/*赋值给当前CPU0的优先级最高的任务和运行任务。*/
g_preferred_ready_task[0] = preferred_task;
g_active_task[0] = preferred_task;
#endif
#if (RHINO_CONFIG_USER_HOOK > 0)
krhino_start_hook();
#endif
/*g_sys_stat 设置为运行状态,表示调度器正式启动*/
g_sys_stat = RHINO_RUNNING;
/*开始调度*/
cpu_first_task_start();
/* should not be here */
return RHINO_SYS_FATAL_ERR;
}
return RHINO_RUNNING;
}
;******************************************************************************
; Functions:
; void cpu_first_task_start(void);
;******************************************************************************
cpu_first_task_start
;set PendSV prority to the lowest
LDR R0, =SHPR3_PRI_14 //0xE000ED22
LDR R1, =PRI_LVL_PENDSV //0xFF
STRB R1, [R0]
;set Systick prority to the lowest
LDR R0, =SHPR3_PRI_15 //0xE000ED23
LDR R1, =PRI_LVL_SYSTICK //0xFF
STRB R1, [R0]
;indicate PendSV_Handler branch to _pendsv_handler_nosave
MOVS R0, #0
MSR PSP, R0 /*将堆栈指针PSP赋值0*/
;make PendSV exception pending
LDR R0, =SCB_ICSR /*0xE000ED04*/
LDR R1, =ICSR_PENDSVSET /*0x10000000*/
STR R1, [R0] /*执行PendSV 中断*/
;goto PendSV_Handler
CPSIE I
B .
所以接下来我们看PendSV_Handler
PendSV_Handler
CPSID I
MRS R0, PSP
;branch if cpu_first_task_start
CMP R0, #0 /*由于之前PSP被我们设置成0,所以跳转到_first_task_restore*/
BEQ _first_task_restore
...
_first_task_restore
;set MSP to the base of system stack
LDR R0, =SCB_VTOR //0xE000ED08
LDR R0, [R0] //R0取值为0xE000ED08上的值,为0x8000000(在SystemInit中,设置过SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;)
LDR R0, [R0] //R0取值为0x8000000上的值,为0x200017E0 ,这个是内存上向量表的起始地址,赋给MSP作为栈的首地址(栈向下增长)
MSR MSP, R0 //赋给MSP堆栈寄存器
B _pendsv_handler_nosave
ALIGN
END
_pendsv_handler_nosave
LDR R0, =g_active_task
LDR R1, =g_preferred_ready_task
LDR R2, [R1]
STR R2, [R0] //将g_preferred_ready_task上的要运行的task赋给g_active_task
;R0 = g_active_task->task_stack = context region
LDR R0, [R2] //R0获得运行的task上task_stack的地址
;restore context
LDM R0, {R4-R11, LR} //将运行的task的task_stack内容还原到CPU寄存器上
ADDS R0, R0, #0x24 //看回上面的cpu_task_stack_init函数,0x24=36,也就是R0跳过9个寄存器,
//将R0 直接指向到 (cpu_stack_t)arg位置。
;return stack = PSP
MSR PSP, R0 //将R0赋给PSP
CPSIE I //使能中断
;hardware restore R0~R3,R12,LR,PC,xPSR
BX LR
/*跳转到LR,这里我们只恢复r4-r11寄存器,因为其它寄存器会,在BX LR时,
根据返回线程模式的过程,根据上面R0的位置,自动恢复(r0-r3、r12、lr、pc和xpsr寄存器),
LR最终指向task的entry函数,完成跳转。*/
那么到现在开始, 调度器开始运作,并将之前优先级最高的timer_task先运行了起来,那么当进程由于时间片用完或者让出CPU等的时候, 进程如何切换的呢?我们下一章再分析。