操作系统 ucore lab5实验报告

ucore lab5(用户进程管理)

一、实验目的

1.1了解第一个用户进程创建过程
1.2了解系统调用框架的实现机制
1.3了解ucore如何实现系统调用sys_fork/sys_exec/sys_exit/sys_wait来进行进程管理

二、实验内容

实验4完成了内核线程,但到目前为止,所有的运行都在内核态执行。实验5将创建用户进程,让用户进程在用户态执行,且在需要ucore支持时,可通过系统调用来让ucore提供服务。为此需要构造出第一个用户进程,并通过系统调用sys_fork/sys_exec/sys_exit/sys_wait来支持运行不同的应用程序,完成对用户进程的执行过程的基本管理。相关原理介绍可看附录B。

三、实验步骤及流程

3.0 练习0:填写已有实验

3.0.1实验要求

本实验依赖实验1/2/3/4。请把你做的实验1/2/3/4的代码填入本实验中代码中有“LAB1”/“LAB2”/“LAB3”/“LAB4”的注释相应部分。注意:为了能够正确执行lab5的测试应用程序,可能需对已完成的实验1/2/3/4的代码进行进一步改进。

3.0.2代码改进

(1)alloc_proc函数(kern/process/proc.c)
观察到实验四中的PCB部分(kern/process/proc.h)中对于内核线程的结构体声明多出了两行,新加入的两个属性,第一个是进程等待状态,第二个是进程之间的相关指针初始化,在lab5中,涉及到了用户进程,自然需要涉及到调度的问题,所以进程等待状态和各种指针需要被初始化。新增加的两行代码如下(最后两行),主要是初始化进程等待状态、和进程的相关指针,例如父进程、子进程、同胞等等。其中的wait_state是进程控制块中新增的条目。

proc->state = PROC_UNINIT;//给进程设置为未初始化状态
        proc->pid = -1;//未初始化的进程,其pid为-1
        proc->runs = 0;//初始化时间片,刚刚初始化的进程,运行时间一定为零	
        proc->kstack = 0;//内核栈地址,该进程分配的地址为0,因为还没有执行,也没有被重定位,因为默认地址都是从0开始的。
        proc->need_resched = 0;//不需要调度
        proc->parent = NULL;//父进程为空
        proc->mm = NULL;//虚拟内存为空
        memset(&(proc->context), 0, sizeof(struct context));//初始化上下文
        proc->tf = NULL;//中断帧指针为空
        proc->cr3 = boot_cr3;//页目录为内核页目录表的基址
        proc->flags = 0; //标志位为0
        memset(proc->name, 0, PROC_NAME_LEN);//进程名为0
        proc->wait_state = 0;//PCB新增的条目,初始化进程等待状态
        proc->cptr = proc->optr = proc->yptr = NULL;//指针初始化 

(2)do_fork函数(kern/process/proc.c)
增加两行代码分别为:
①assert(current->wait_state == 0):对应第一步中对于proc->wait_state属性的初始分配,需要确保当前进程正在等待。
②set_links(proc):这里调用了一个set links函数来设置进程之间的连接。查看set links函数代码(kern/process/proc.c),可以看出此函数的作用就是设置当前进程的process relations。同样,它进行一些插入进程、调度、更改“当前进程数量”等对于共享数据的访问操作,因此它还在lab4中定义的互斥锁中。
增加代码具体位置如下:

 proc->parent = current;//将子进程的父节点设置为当前进程
    assert(current->wait_state == 0);//确保当前进程正在等待
    if (setup_kstack(proc) != 0) {
   
   //2.调用setup_stack()函数为进程分配一个内核栈
        goto bad_fork_cleanup_proc;
    }
    if (copy_mm(clone_flags, proc) != 0) {
   
   //3.调用copy_mm()函数复制父进程的内存信息到子进程
        goto bad_fork_cleanup_kstack;
    }
    copy_thread(proc, stack, tf);//4.调用copy_thread()函数复制父进程的中断帧和上下文信息
    //5.将新进程添加到进程的(hash)列表中
    bool intr_flag;
    local_intr_save(intr_flag);//屏蔽中断,intr_flag置为1
    {
   
   
        proc->pid = get_pid();//获取当前进程PID
        hash_proc(proc); //建立hash映射
        set_links(proc);//设置进程链接
    }

(3)idt_init函数(kern/trap/trap.c)
增加一行代码,主要是设置相关中断门:

extern uintptr_t __vectors[];
    int i;
    for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {
   
   
        SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
    }
    SETGATE(idt[T_SYSCALL], 1, GD_KTEXT, __vectors[T_SYSCALL], DPL_USER);//设置相关中断门

(4)trap_dispatch函数(kern/trap/trap.c)
增加一行代码将时间片设置为需要调度,说明当前进程的时间片已经用完了。

ticks ++;
        if (ticks % TICK_NUM == 0) {
   
   
            assert(current != NULL);
            current->need_resched = 1;//将时间片设置为需要调度
        }

3.1 练习1:加载应用程序并执行(需要编码)

3.1.1实验要求

do_execv函数调用load_icode(位于kern/process/proc.c中)来加载并解析一个处于内存中的ELF执行文件格式的应用程序,建立相应的用户内存空间来放置应用程序的代码段、数据段等,且要设置好proc_struct结构中的成员变量trapframe中的内容,确保在执行此进程后,能够从应用程序设定的起始执行地址开始执行。需设置正确的trapframe内容。

请在实验报告中简要说明你的设计实现过程。

请在实验报告中描述当创建一个用户态进程并加载了应用程序后,CPU是如何让这个应用程序最终在用户态执行起来的。即这个用户态进程被ucore选择占用CPU执行(RUNNING态)到具体执行应用程序第一条指令的整个经过。

3.1.2关键数据结构及知识点

(1)do_exceve函数(kern/process/proc.c):
它调用了load_icode去加载ELF二进制格式文件到内存并执行:

do_execve(const char *name, size_t len, unsigned char *binary, size_t size) {
   
   
    struct mm_struct *mm = current->mm;//获取当前进程的内存地址
    if (!user_mem_check(mm, (uintptr_t)name, len, 0)) {
   
   
        return -E_INVAL;
    }
    if (len > PROC_NAME_LEN) {
   <
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值