3.4:进程调度-创建

一:0号进程的创建

1:PCB初始化

static union task_union init_task = {INIT_TASK,};
#define INIT_TASK \
/* state etc */ { 0,15,15, \
/* signals */   0,{{},},0, \
/* ec,brk... */ 0,0,0,0,0,0, \
/* pid etc.. */ 0,-1,0,0,0, \ 
/* uid etc */   0,0,0,0,0,0, \
/* alarm */     0,0,0,0,0,0, \
/* math */      0, \ 
/* fs info */   -1,0022,NULL,NULL,NULL,0, \
/* filp */      {NULL,}, \
        { \
                {0,0}, \
/* ldt */       {0x9f,0xc0fa00}, \
                {0x9f,0xc0f200}, \
        }, \
/*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\
         0,0,0,0,0,0,0,0, \
         0,0,0x17,0x17,0x17,0x17,0x17,0x17, \
         _LDT(0),0x80000000, \
                {} \
        }, \
}

2:初始化内核栈,并完成和PCB的关联

当需要使用0号进程的内核栈时,CPU就会找到0号进程的TSS数据结构,会根据其中的内容将内核栈寄存器SS设置位0X10,将内核段栈顶寄存器ESP设置为PAGE_SIZE + (long)&init_task,其中PAGE_SIZE=4K

/*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\
         0,0,0,0,0,0,0,0, \
         0,0,0x17,0x17,0x17,0x17,0x17,0x17, \
         _LDT(0),0x80000000, \
                {} \
        }, \

3:创建用户栈

long user_stack [ PAGE_SIZE>>2 ] ;

struct {
        long * a;
        short b;
        } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };

4:完成用户栈和内核栈的关联

栈顶开始的五项:0X17,用户栈位置,EFLAGS,0X0F,标号1

#define move_to_user_mode() \
__asm__ ("movl %%esp,%%eax\n\t" \
        "pushl $0x17\n\t" \
        "pushl %%eax\n\t" \
        "pushfl\n\t" \
        "pushl $0x0f\n\t" \
        "pushl $1f\n\t" \
        "iret\n" \
        "1:\tmovl $0x17,%%eax\n\t" \
        "movw %%ax,%%ds\n\t" \
        "movw %%ax,%%es\n\t" \
        "movw %%ax,%%fs\n\t" \
        "movw %%ax,%%gs" \
        :::"ax")

5:执行用户态程序

1)调用fork创建一号进程
2)自己进入死循环,不停调用pause,让出CPU给其他进程

		if (!fork()) {          /* we count on this going ok */
                init();
        }
        for(;;) pause();

二:1号进程的创建

1:fork

1)fork的系统调用,最终执行sys_fork函数

sys_fork:
        call find_empty_process
        testl %eax,%eax
        js 1f
        push %gs
        pushl %esi
        pushl %edi
        pushl %ebp
        pushl %eax
        call copy_process
        addl $20,%esp
1:      ret

2)find_empty_process找到不重复的进程号last_pid,并返回该pid

int find_empty_process(void)
{
        int i;

        repeat:
                if ((++last_pid)<0) last_pid=1;
                for(i=0 ; i<NR_TASKS ; i++)
                        if (task[i] && task[i]->pid == last_pid) goto repeat;   
        for(i=1 ; i<NR_TASKS ; i++)	//进程0排除在外
                if (!task[i])
                        return i;
        return -EAGAIN;
}     

3)copy_process
1: 复制系统进程并且设置必要的寄存器

   struct task_struct *p;
    int i;
    struct file *f;

    p = (struct task_struct *) get_free_page();
    if (!p)
            return -EAGAIN;
    task[nr] = p;
    *p = *current;  /* NOTE! this doesn't copy the supervisor stack */
    p->state = TASK_UNINTERRUPTIBLE;
    p->pid = last_pid;
    p->father = current->pid;
    p->counter = p->priority;
    p->signal = 0;
    p->alarm = 0;
    p->leader = 0;          /* process leadership doesn't inherit */
    p->utime = p->stime = 0;
    p->cutime = p->cstime = 0;
    p->start_time = jiffies;
    p->tss.back_link = 0;
    p->tss.esp0 = PAGE_SIZE + (long) p;
    p->tss.ss0 = 0x10;
    p->tss.eip = eip;
    p->tss.eflags = eflags;
    p->tss.eax = 0;
    p->tss.ecx = ecx;
    p->tss.edx = edx;
    p->tss.ebx = ebx;
    p->tss.esp = esp;
    p->tss.ebp = ebp;
    p->tss.esi = esi;
    p->tss.edi = edi;
    p->tss.es = es & 0xffff;
    p->tss.cs = cs & 0xffff;
    p->tss.ss = ss & 0xffff;
    p->tss.ds = ds & 0xffff;
    p->tss.fs = fs & 0xffff;
    p->tss.gs = gs & 0xffff;
    p->tss.ldt = _LDT(nr);
    p->tss.trace_bitmap = 0x80000000;
    if (last_task_used_math == current)
            __asm__("clts ; fnsave %0"::"m" (p->tss.i387));
   if (copy_mem(nr,p)) {
                task[nr] = NULL;
                free_page((long) p);
                return -EAGAIN;
        }

2:设置新任务代码和数据段基址,限长并复制页面。

int copy_mem(int nr,struct task_struct * p)
{
        unsigned long old_data_base,new_data_base,data_limit;
        unsigned long old_code_base,new_code_base,code_limit;

        code_limit=get_limit(0x0f);
        data_limit=get_limit(0x17);
        old_code_base = get_base(current->ldt[1]);
        old_data_base = get_base(current->ldt[2]);
        if (old_data_base != old_code_base)
                panic("We don't support separate I&D");
        if (data_limit < code_limit)
                panic("Bad data_limit");
        new_data_base = new_code_base = nr * 0x4000000;
        p->start_code = new_code_base;
        set_base(p->ldt[1],new_code_base);
        set_base(p->ldt[2],new_data_base);
        if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
                printk("free_page_tables: from copy_mem\n");
                free_page_tables(new_data_base,data_limit);
                return -ENOMEM;
        }
        return 0;
}

3:设置父进程相关的一些操作标志,在GDT中设置新任务的TSS和LDT描述符项,并将状态设置位running

for (i=0; i<NR_OPEN;i++)
        if ((f=p->filp[i]))
                f->f_count++;
if (current->pwd)
        current->pwd->i_count++;
if (current->root)
        current->root->i_count++;
if (current->executable)
        current->executable->i_count++;
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->state = TASK_RUNNING;        /* do this last, just in case */
return last_pid;

2:init()

1)执行execve,执行程序“/bin/sh”,即是shell程序

if (!(pid=fork())) {
        close(0);
        if (open("/etc/rc",O_RDONLY,0))
                _exit(1);
        execve("/bin/sh",argv_rc,envp_rc);
        _exit(2);
}

2)父进程等待子进程结束

if (pid>0)
        while (pid != wait(&i))

至此,整个系统启动到现在变成了我们使用的状态,shell不停的等待用户输入命令,并且用fork创建一个进程,用exec执行用户命令对应的程序。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值