Lenix进程管理源代码分析(第一阶段)

由于进程会随着功能的增加而变大,也会导致进程管理行为发生变化。要说明进程管理,并不是功能越多越好,而应该在具备基本功能的最小集合下进行,如果有太多的其他内容,反而会造成理解上的困难。

首先,Lenix定义了一个全局进程数组,这个数组就是用来分配进程对象的,也就是PCB

proc_t                  *   proc_current;

static proc_t               proc_pool[PROC_MAX];
static proc_t           *   proc_idle;

proc_current,表示当前进程,用于记录当前在CPU上运行的进程,这样可以简化访问当前进程的操作。

proc_idle,表示系统空转进程

proc_pool,进程池,Lenix就在这里面分配进程对象。

 

Lenix首先要对进程管理模块进行初始化。

void        Proc_initial(void)
{
    memset(proc_pool,0,PROC_MAX*sizeof(proc_t));
    proc_current    = proc_pool;
    proc_idle       = proc_current;
    
    strcpy(proc_idle->proc_name,"lenix");
    proc_idle->proc_pid     = 0;
    proc_idle->proc_stat    = PROC_STAT_RUN;
}

 

进程管理模块初始化的主要工作有2个,1是将进程池用0进程初始化,第二个是创建系统进程,也就是0号进程。这个0号进程是手工创建的,现在由于进程对象包含的信息不多,初始化所需要做的工作不多,仅仅是设置了进程名,pid和进程状态。

创建进程

proc_t  *   Proc_create(const char * name,void * entry,void * param,void * stack)
{
    int                     i;
    proc_t              *   proc;
    uint_t              *   sp;
    
    proc = NULL;
    
    for( i = 1 ; i < PROC_MAX ; i++)
    {
        if( PROC_STAT_FREE == proc_pool[i].proc_stat )
        {
            proc = proc_pool + i;
            proc->proc_stat = -1;
            break;
        } 
    }
    
    if( NULL == proc )
    {
        printf("no proc space\n");
        return NULL;
    }
    /*  设置运行环境    */
    sp      = stack;
    
    sp--;
    *sp--   = 0;                                    /*  进程退出代码            */
    *sp--   = (uint_t)param;                        /*  进程入口参数            */
    *sp--   = (uint_t)Proc_exit;                    /*  进程退出函数入口        */
    *sp--   = (uint_t)entry;                        /*  进程入口                */
    
    *sp--   = 0x200;                                /*  进程对应的机器状态字    */
    
    *sp--   = 0;                                    /*  以下为寄存器环境        */
    *sp--   = 0;
    *sp--   = 0;
    *sp--   = 0;
    
    *sp--   = 0;
    *sp--   = 0;
    *sp--   = 0;
    *sp     = 0;/**/
    
    strcpy(proc->proc_name,name);
    
    proc->proc_stack        = sp;
    proc->proc_entry        = entry;
    proc->proc_cpu_time     = CPU_TIME;
    proc->proc_stat         = PROC_STAT_RUN;
            
    return proc;
}

 

在这个函数中,首先扫描线程池,查看线程池中是否有空闲的进程对象可用,判断的依据是状态是否为PROC_STAT_FREE,进程状态的定义在proc.h中进行定义

#define PROC_STAT_FREE              0
#define PROC_STAT_RUN               1
#define PROC_STAT_WAIT              2
#define PROC_STAT_SLEEP             3

如果没有空闲对象,则返回。用返回NULL表示无可用进程对象可用。

在分配到进程对象后,就对进程运行环境进程初始化,将寄存器初始值、进程入口地址、进程参数、进程退出程序入口以及退出代码。
设置完环境后,记录下栈指针以及进程状态等信息后,到此,创建就完成了。当然,这只是具备了基本功能,还没有处理其他问题,例如没有处理互斥问题,以及生成PID。但是已经可以演示。

注意,这里并没有所谓的加入就绪队列的代码。其实,已经加入,因为进程池就是就绪队列,因为调度程序直接在进程池中查找可以运行的进程。

进程退出

void        Proc_exit(int code)
{
    proc_current->proc_exit_code    = code;
    proc_current->proc_stat         = PROC_STAT_FREE;
    Proc_sched();
}

实现其实很简单,就是将进程状态置为PROC_STAT_FREE即可完成,因为将状态置为PROC_STAT_FREE后,就可以重新进入分配,而且不会参与调度。在改变状态后,立即执行调度,就可以完成进程的退出。

进程切换

void    *   Proc_switch_prepare(void * sp,proc_t * next)
{
    proc_current->proc_stack = sp;
    proc_current = next;
    //printf("current proc; %s\n",proc_current->proc_name);
    return proc_current->proc_stack;
}

void        Proc_sched(void)
{
    int i;
    proc_t              *   proc;
    
    proc = NULL;
    
    for( i = 1 ; i < PROC_MAX ; i++)
    {
        if( PROC_STAT_RUN != proc_pool[i].proc_stat || proc_pool[i].proc_alarm )
            continue;
        
        if( NULL == proc )
            proc = proc_pool + i;
            
        if( proc_pool[i].proc_sched_factor > proc->proc_sched_factor )
            proc = proc_pool + i;
    }
    
    if( NULL == proc )
        proc = proc_idle;
        
    //printf("proc name: %s\n",proc->proc_name);
    
    proc->proc_sched_factor = 0;
    proc->proc_cpu_time     = CPU_TIME;
    
    if( proc == proc_current )
    {
        return ;
    }
    
    //printf("switch to :%s %p\n",proc->proc_name,proc->proc_stack);
    Proc_switch_to(proc);
}

调度程序首先挑选出可以运行的进程,在进程池中扫描,跳过不是运行态的进程,然后挑选出调度因子最大的进程。如果没有运行的进程,则将空转进程作为挑选到的进程。挑选出进程后,就分配CPU时间,并将调度因子置0,这样可以保证下次调度的时候,进程调度因子很小,从而减少得到CPU的机会。分配好CPU时间后,如果挑选出来的进程就是自身,不做切换,如果不是自身,就要进程CPU切换。

Proc_switch_to比较特别,这是一个汇编程序,因为需要直接操作栈环境

; void	Proc_switch_to(proc_t * next)
_Proc_switch_to:
	pushf
    push	ax
    push	cx
    push	bx
    push	dx
    
    push	sp
    push	bp
    push	si
    push	di
    
    mov		bx,sp
    mov		ax,[bx + 20]
    push	ax				;  在这里传递参数要注意,
    push	bx				;  这里是要传递保存环境的栈指针,而不是当前栈指针
    call	_Proc_switch_prepare
    mov		sp,ax
    
    pop		di
    pop		si
    pop		bp
    pop		ax;
    
    pop		dx
    pop		bx
    pop		cx
    pop		ax
    popf
	ret

这个函数的任务是完成进程切换,具体工作是保存当前CPU寄存器到进程栈中,然后调用Proc_switch_prepare做切换前的准备,主要是维护proc_current,然后Proc_switch_prepare返回新进程的栈指针,这样就可以通过栈恢复进程的寄存器环境。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值