接下来我们就来一起学习进程的创建与执行。也就是说我们希望进行在创建成功之后,立即被操作系统调度并执行。
我们在创建一个进程时,就意味着这个进程需要参与操作系统的调度,可以独立运行。因此,我们需要为此进程分配相应的内存空间,为其执行时所用。这部分内存就是此进程在执行时所用到的栈内存。我们在第一节时已经讲述过,进程在被操作系统调度时需要使用一部分内存空间来保存上下文,这部分内存就可以使用此进程的栈内存来完成。我们将一部分内在分配给进程,并做为此进程的栈内存,这个内存区域中存放了进程调度时所保存的处理器寄存器的值,和进程在执行时所用到的函数栈帧。于是,我们可以这样为进程分配其初始栈内存。先来定义进程控制块PCB,用于存放与此进程相关的所有信息,以便操作系统调度所用:
//进程控制块Process Control Block
typedef struct pcb_s
{
//进程栈顶地址
void *p_stack;
//栈内存地址,释放内存时使用
void *p_stack_mem;
//优先级由高0到低32
uint8_t prio;
//任务状态
uint8_t status;
//任务休眠ticks
uint32_t sleep_tick;
//任务入口函数
void (*task_entry)(void *);
//任务函数参数
void *task_arg;
} pcb_s;
需要说明的是,p_stack和p_statck_mem就是表示这个进程的栈内存地址,其中p_statck_mem为栈内存的起始地址,也就是栈底位置,而p_stack是栈顶位置。进程栈内存的实际地址是由malloc()函数动态分配。关于malloc()函数的实现过程我们将在后续章节学习。有了进程控制块之后,我们再通过malloc()函数为这个进程申请栈内存地址:
uint8_t *stack = malloc(stack_size);
if (stack == NULL)
{
return NULL;
}
//初始化pcb状态
pcbs[prio].status = PCB_ST_INIT;
//初始化栈
pcbs[prio].p_stack = stack_init((uint32_t *)&stack[stack_size], pcb_runner);
//栈内存地址
pcbs[prio].p_stack_mem = stack;
我们需要指出,statck_init()函数,用于为进程的栈内在初始化。当操作系统开始执行任务调度时,我们需要为等待执行的进程指定其要运行的函数及参数,
//初始化进程栈
void *stack_init(uint32_t *stack, void *runner)
{
uint32_t *stk = stack;
*(stk) = (uint32_t)runner; /* 函数进口 */
*(--stk) = (uint32_t)0x0; /* lr */
*(--stk) = 0; /* r12 */
*(--stk) = 0; /* r11 */
*(--stk) = 0; /* r10 */
*(--stk) = 0; /* r9 */
*(--stk) = 0; /* r8 */
*(--stk) = 0; /* r7 */
*(--stk) = 0; /* r6 */
*(--stk) = 0; /* r5 */
*(--stk) = 0; /* r4 */
*(--stk) = 0; /* r3 */
*(--stk) = 0; /* r2 */
*(--stk) = 0; /* r1 */
*(--stk) = 0; /* r0 : 参数 */
/* cpsr */
*(--stk) = SVCMODE;
return stk;
}
可以看到,栈中预置的处理器寄存器的值都为0,不过这只是在初始化的情况,当操作系统挂起一个进程时,会将处理器当前的寄存器保存在这个栈的栈顶部分,而当操作系统恢复此进程运行时,则将这部分内容载入处理器的寄存器中,也就是出栈操作。这部分内容我们已经在第一节中描述过了,这里不再赘述。
之后,我们可以根据自己的需要完成进程的执行、挂起和恢复功能:
//将进程加入就绪队列
void pcb_ready(pcb_s *pcb)
{
pcb_ready_map |= 1 << pcb->prio;
pcb->status = PCB_ST_READ;
}
//将进程由就绪队列挂起
void pcb_block(pcb_s *pcb)
{
pcb_ready_map &= ~(1 << pcb->prio);
pcb->status = PCB_ST_BLOCK;
}
//将进程结束
void pcb_kill(pcb_s *pcb)
{
//关中断
sche_interrupt_disable();
//挂起进程
pcb_block(pcb);
//进程结束
pcb->status = PCB_ST_STOPED;
//开中断
sche_interrupt_enable();
}
当操作系统将某个程序挂起时,调用pcb_block()函数,而当进程就绪时则调用pcb_ready()函数将进程加入到当前执行队列当中,最终再由第二节中所讲述的调度算法,从待执行任务队列中找到一个优先级最高的进程并执行。
Copyright © 2015-2023 问渠网 辽ICP备15013245号