自制嵌入式实时操作系统

    返回首页    发表留言
本文作者:李德强
          第三节 进程的创建与执行
 
 

        接下来我们就来一起学习进程的创建与执行。也就是说我们希望进行在创建成功之后,立即被操作系统调度并执行。

        我们在创建一个进程时,就意味着这个进程需要参与操作系统的调度,可以独立运行。因此,我们需要为此进程分配相应的内存空间,为其执行时所用。这部分内存就是此进程在执行时所用到的栈内存。我们在第一节时已经讲述过,进程在被操作系统调度时需要使用一部分内存空间来保存上下文,这部分内存就可以使用此进程的栈内存来完成。我们将一部分内在分配给进程,并做为此进程的栈内存,这个内存区域中存放了进程调度时所保存的处理器寄存器的值,和进程在执行时所用到的函数栈帧。于是,我们可以这样为进程分配其初始栈内存。先来定义进程控制块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号