Mit os Lab 3. User Environmen

Part A: User Environments and Exception Handling

As you can see in kern/env.c, the kernel maintains three main global variables pertaining to environments:

struct Env *envs = NULL;		// All environments
struct Env *curenv = NULL;		// The current env
static struct Env *env_free_list;	// Free environment list

Once JOS gets up and running, the envs pointer points to an array of Env structures representing all the environments in the system. In our design, the JOS kernel will support a maximum of NENV simultaneously active environments, although there will typically be far fewer running environments at any given time. (NENV is a constant #define‘d in inc/env.h.) Once it is allocated, the envs array will contain a single instance of the Env data structure for each of theNENV possible environments.

ENV结构体定义:

struct Env {
	struct Trapframe env_tf;	// Saved registers
	struct Env *env_link;		// Next free Env
	envid_t env_id;			// Unique environment identifier
	envid_t env_parent_id;		// env_id of this env‘s parent
	enum EnvType env_type;		// Indicates special system environments
	unsigned env_status;		// Status of the environment
	uint32_t env_runs;		// Number of times environment has run

	// Address space
	pde_t *env_pgdir;		// Kernel virtual address of page dir
};

Here‘s what the Env fields are for:

env_tf:
This structure, defined in inc/trap.h, holds the saved register values for the environment while that environment is not running: i.e., when the kernel or a different environment is running. The kernel saves these when switching from user to kernel mode, so that the environment can later be resumed where it left off.
env_link:
This is a link to the next Env on the env_free_listenv_free_list points to the first free environment on the list.
env_id:
The kernel stores here a value that uniquely identifiers the environment currently using this Env structure (i.e., using this particular slot in the envs array). After a user environment terminates, the kernel may re-allocate the same Env structure to a different environment - but the new environment will have a different env_id from the old one even though the new environment is re-using the same slot in the envs array.
env_parent_id:
The kernel stores here the env_id of the environment that created this environment. In this way the environments can form a “family tree,” which will be useful for making security decisions about which environments are allowed to do what to whom.
env_type:
This is used to distinguish special environments. For most environments, it will be ENV_TYPE_USER. We‘ll introduce a few more types for special system service environments in later labs.
env_status:
This variable holds one of the following values:

ENV_FREE:
Indicates that the Env structure is inactive, and therefore on the env_free_list.
ENV_RUNNABLE:
Indicates that the Env structure represents an environment that is waiting to run on the processor.
ENV_RUNNING:
Indicates that the Env structure represents the currently running environment.
ENV_NOT_RUNNABLE:
Indicates that the Env structure represents a currently active environment, but it is not currently ready to run: for example, because it is waiting for an interprocess communication (IPC) from another environment.
ENV_DYING:
Indicates that the Env structure represents a zombie environment. A zombie environment will be freed the next time it traps to the kernel. We will not use this flag until Lab 4.
env_pgdir:
This variable holds the kernel virtual address of this environment‘s page directory.
1 Exercise 1. Modify mem_init() in kern/pmap.c to allocate and map the envs array. This array consists of exactly NENV instances of the Env structure allocated much like how you allocated the pages array. Also like the pages array, the memory backing envs should also be mapped user read-only at UENVS (defined in inc/memlayout.h) so user processes can read from this array.
You should run your code and make sure check_kern_pgdir() succeeds.

1) 为env申请内存

1     // Make ‘envs‘ point to an array of size ‘NENV‘ of ‘struct Env‘.
2     // LAB 3: Your code here.
3     envs = (struct Env*)boot_alloc(ROUNDUP(NENV * sizeof(struct Env), PGSIZE));
4     memset(envs, 0, sizeof(struct Env) * NENV);
5     cprintf("alloc envs buffer: start=%08x, len=%08x\n", envs, NENV * sizeof(struct Env));

2) 将逻辑地址UENVS映射到envs的物理地址

 1     //////////////////////////////////////////////////////////////////////
 2     // Map the ‘envs‘ array read-only by the user at linear address UENVS
 3     // (ie. perm = PTE_U | PTE_P).
 4     // Permissions:
 5     //    - the new image at UENVS  -- kernel R, user R
 6     //    - envs itself -- kernel RW, user NONE
 7     // LAB 3: Your code here.
 8     boot_map_region(kern_pgdir,
 9                 UENVS,
10                 ROUNDUP((sizeof(struct Env) * NENV) , PGSIZE),
11                 PADDR(envs),
12                 (PTE_U | PTE_P));
Exercise 2. In the file env.c, finish coding the following functions:

env_init()
Initialize all of the Env structures in the envs array and add them to the env_free_list. Also calls env_init_percpu, which configures the segmentation hardware with separate segments for privilege level 0 (kernel) and privilege level 3 (user).
env_setup_vm()
Allocate a page directory for a new environment and initialize the kernel portion of the new environment‘s address space.
region_alloc()
Allocates and maps physical memory for an environment
load_icode()
You will need to parse an ELF binary image, much like the boot loader already does, and load its contents into the user address space of a new environment.
env_create()
Allocate an environment with env_alloc and call load_icode load an ELF binary into it.
env_run()
Start a given environment running in user mode.
As you write these functions, you might find the new cprintf verb %e useful -- it prints a description corresponding to an error code. For example,

    r = -E_NO_MEM;
    panic("env_alloc: %e", r);
will panic with the message "env_alloc: out of memory".

1) env_init

初始化env,最终env_free_list指向env[0], 然后通过env_link链接剩余env,链表。

 1 // Mark all environments in ‘envs‘ as free, set their env_ids to 0,
 2 // and insert them into the env_free_list.
 3 // Make sure the environments are in the free list in the same order
 4 // they are in the envs array (i.e., so that the first call to
 5 // env_alloc() returns envs[0]).
 6 //
 7 void
 8 env_init(void)
 9 {
10     // Set up envs array
11     // LAB 3: Your code here.
12
13     int i = 0;
14     env_free_list = NULL;
15     cprintf("NENV -1 : %u\n", NENV -1);
16
17     for (i = NENV -1; i >= 0; i--)
18     {
19         envs[i].env_id = 0;
20         envs[i].env_parent_id = 0;
21         envs[i].env_type = ENV_TYPE_USER;
22         envs[i].env_status = ENV_FREE;
23         envs[i].env_runs = 0;
24         envs[i].env_pgdir = NULL;
25         envs[i].env_link = env_free_list;
26         env_free_list = &envs[i];
27     }
28
29     cprintf("env_free_list : 0x%08x, & envs[i]: 0x%08x\n", env_free_list, &envs[i]);
30
31     // Per-CPU part of the initialization
32     env_init_percpu();
33 }

2) env_setup_vm

env_setup_vm为进程创建进程地址空间,先通过page_alloc分配一页地址作为页目录pagedir,然后pagedir复制内核页目录表,

然后将页表项env_pgdir[PDX(UVPT)]映射到pagedir。

 1 static int
 2 env_setup_vm(struct Env *e)
 3 {
 4     int i;
 5     struct PageInfo *p = NULL;
 6
 7     // Allocate a page for the page directory
 8     if (!(p = page_alloc(ALLOC_ZERO)))
 9         return -E_NO_MEM;
10
11     // Now, set e->env_pgdir and initialize the page directory.
12     //
13     // Hint:
14     //    - The VA space of all envs is identical above UTOP
15     //    (except at UVPT, which we‘ve set below).
16     //    See inc/memlayout.h for permissions and layout.
17     //    Can you use kern_pgdir as a template?  Hint: Yes.
18     //    (Make sure you got the permissions right in Lab 2.)
19     //    - The initial VA below UTOP is empty.
20     //    - You do not need to make any more calls to page_alloc.
21     //    - Note: In general, pp_ref is not maintained for
22     //    physical pages mapped only above UTOP, but env_pgdir
23     //    is an exception -- you need to increment env_pgdir‘s
24     //    pp_ref for env_free to work correctly.
25     //    - The functions in kern/pmap.h are handy.
26
27     // LAB 3: Your code here.
28     (p->pp_ref)++;
29     pde_t* page_dir = page2kva(p);
30     memcpy(page_dir, kern_pgdir, PGSIZE);
31     e->env_pgdir = page_dir;
32
33     // UVPT maps the env‘s own page table read-only.
34     // Permissions: kernel R, user R
35     e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U;
36
37     return 0;
38 }

3) region_alloc()

region_alloc 为environment *e 开辟len byte大小的物理空间,并将va虚拟地址开始的len长度大小的空间和物理空间建立映射关系。

 1 static void
 2 region_alloc(struct Env *e, void *va, size_t len)
 3 {
 4     // LAB 3: Your code here.
 5     // (But only if you need it for load_icode.)
 6     //
 7     // Hint: It is easier to use region_alloc if the caller can pass
 8     //   ‘va‘ and ‘len‘ values that are not page-aligned.
 9     //   You should round va down, and round (va + len) up.
10     //   (Watch out for corner-cases!)
11
12     va = ROUNDDOWN(va, PGSIZE);
13     len = ROUNDUP(len, PGSIZE);
14
15     struct PageInfo *pp;
16     int ret = 0;
17
18     for(; len > 0; len -= PGSIZE, va += PGSIZE)
19     {
20         pp = page_alloc(0);
21
22         if(!pp)
23         {
24             panic("region_alloc failed\n");
25         }
26
27         ret = page_insert(e->env_pgdir, pp, va, PTE_U | PTE_W | PTE_P);
28
29         if(ret)
30         {
31             panic("region_alloc failed\n");
32         }
33     }
34 }

4) load_icode

load_icode将elf格式的二进制文件load到env中。

 1 static void
 2 load_icode(struct Env *e, uint8_t * binary)
 3 {
 4     // Hints:
 5     //  Load each program segment into virtual memory
 6     //  at the address specified in the ELF section header.
 7     //  You should only load segments with ph->p_type == ELF_PROG_LOAD.
 8     //  Each segment‘s virtual address can be found in ph->p_va
 9     //  and its size in memory can be found in ph->p_memsz.
10     //  The ph->p_filesz bytes from the ELF binary, starting at
11     //  ‘binary + ph->p_offset‘, should be copied to virtual address
12     //  ph->p_va.  Any remaining memory bytes should be cleared to zero.
13     //  (The ELF header should have ph->p_filesz <= ph->p_memsz.)
14     //  Use functions from the previous lab to allocate and map pages.
15     //
16     //  All page protection bits should be user read/write for now.
17     //  ELF segments are not necessarily page-aligned, but you can
18     //  assume for this function that no two segments will touch
19     //  the same virtual page.
20     //
21     //  You may find a function like region_alloc useful.
22     //
23     //  Loading the segments is much simpler if you can move data
24     //  directly into the virtual addresses stored in the ELF binary.
25     //  So which page directory should be in force during
26     //  this function?
27     //
28     //  You must also do something with the program‘s entry point,
29     //  to make sure that the environment starts executing there.
30     //  What?  (See env_run() and env_pop_tf() below.)
31
32     // LAB 3: Your code here.
33
34     struct Elf* elfhdr = (struct Elf *)binary;
35     struct Proghdr *ph, *eph;
36
37     if(elfhdr->e_magic != ELF_MAGIC)
38     {
39         panic("elf header‘s magic is not correct\n");
40     }
41
42     ph = (struct Proghdr *)((uint8_t *)elfhdr + elfhdr->e_phoff);
43
44     eph = ph + elfhdr->e_phnum;
45
46     lcr3(PADDR(e->env_pgdir));
47
48     for(;ph < eph; ph++)
49     {
50         if(ph->p_type != ELF_PROG_LOAD)
51         {
52             continue;
53         }
54
55         if(ph->p_filesz > ph->p_memsz)
56         {
57             panic("file size is great than memory size\n");
58         }
59
60         region_alloc(e, (void *)ph->p_va, ph->p_memsz);
61         memmove((void *)ph->p_va, binary + ph->p_offset, ph->p_filesz);
62
63         memset((void *)ph->p_va + ph->p_filesz, 0, (ph->p_memsz - ph->p_filesz));
64     }
65
66     // Now map one page for the program‘s initial stack
67     // at virtual address USTACKTOP - PGSIZE.
68
69     // LAB 3: Your code here.
70
71     lcr3(PADDR(kern_pgdir));
72
73     e->env_tf.tf_eip = elfhdr->e_entry;
74
75     region_alloc(e, (void *)(USTACKTOP - PGSIZE), PGSIZE);
76 }

5) env_create

env_create 申请新的env并将elf文件load到env中.

 1 void
 2 env_create(uint8_t *binary, enum EnvType type)
 3 {
 4     // LAB 3: Your code here.
 5     int ret = 0;
 6     struct Env *e = NULL;
 7     ret = env_alloc(&e, 0);
 8
 9     if(ret < 0)
10     {
11         panic("env_create: %e\n", ret);
12     }
13
14     load_icode(e, binary);
15     e->env_type = type;
16 }

6) env_run

env_run实现进程切换,先将当前进程状态置为RUNNABLE,然后通过lcr3切换进程空间,最后使用env_pop_tf切换到新的进程。

 1 void
 2 env_run(struct Env *e)
 3 {
 4     // Step 1: If this is a context switch (a new environment is running):
 5     //       1. Set the current environment (if any) back to
 6     //          ENV_RUNNABLE if it is ENV_RUNNING (think about
 7     //          what other states it can be in),
 8     //       2. Set ‘curenv‘ to the new environment,
 9     //       3. Set its status to ENV_RUNNING,
10     //       4. Update its ‘env_runs‘ counter,
11     //       5. Use lcr3() to switch to its address space.
12     // Step 2: Use env_pop_tf() to restore the environment‘s
13     //       registers and drop into user mode in the
14     //       environment.
15
16     // Hint: This function loads the new environment‘s state from
17     //    e->env_tf.  Go back through the code you wrote above
18     //    and make sure you have set the relevant parts of
19     //    e->env_tf to sensible values.
20
21     // LAB 3: Your code here.
22
23     //panic("env_run not yet implemented");
24
25     if(curenv && curenv->env_status == ENV_RUNNING)
26     {
27         curenv->env_status = ENV_RUNNABLE;
28     }
29
30     curenv = e;
31     e->env_status = ENV_RUNNING;
32     e->env_runs++;
33
34     lcr3(PADDR(e->env_pgdir));
35
36     env_pop_tf(&(e->env_tf));
37 }

Part B: Page Faults, Breakpoints Exceptions, and System Calls

时间: 2024-10-01 08:06:42

Mit os Lab 3. User Environmen的相关文章

MIT OS lab Makefile 分析

lab1 中的Makefile主要是根目录下的GNUMakefile, kern/Makefrag, boot/Makefrag, 后两者通过include直接包含到GNUMakefile中. 1 # 2 # This makefile system follows the structuring conventions 3 # recommended by Peter Miller in his excellent paper: 4 # 5 # Recursive Make Considere

Mit os Lab 2. Memory Management

Part 1: Physical Page Management Exercise 1. In the file kern/pmap.c, you must implement code for the following functions (probably in the order given). boot_alloc() mem_init() (only up to the call to check_page_free_list(1)) page_init() page_alloc()

如何做研究?(From: MIT AI LAB)

简评: 这是一篇关于如何做研究的经验总结,全面阐述了研究过程中可能遇到的诸多问题,并给出了切实可行的建议!对于刚进入研究生阶段的研究生来说,我觉得可以为他们未来的研究生活提供很多有益的指导! 摘要: 本文的主旨是解释如何做研究.我们提供的这些建议,对做研究本身(阅读.写作和程序设计),理解研究过程以及开始热爱研究(方法论.选题.选导师和情感因素),都是极 具价值的. 本文背景: 麻省理工学院 人工智能实验室 AI Working Paper 316 1988年10月来自MIT人工智能实验室:如何

MIT OS lab1

Lab 1: Booting a PC 1.Introduction Software setup Get jos code: athena% mkdir ~/6.828 athena% cd ~/6.828 athena% add git athena% git clone http://pdos.csail.mit.edu/6.828/2014-jos.git lab Cloning into lab... athena% cd lab Part 1: PC Bootstrap 1.1  

Python 批量获取Google用户动态 (分页)

CODE: #!/usr/bin/python # -*- coding: utf-8 -*- ''' Created on 2014-9-7 @author: guaguastd @name: user_activity_loop.py ''' import json from login import google_api_request from html import cleanHtml import os MAX_RESULTS = 40 while True: query = raw

操作系统内核的绝佳学习材料——JOS

操作系统内核的绝佳学习材料--JOS 前言:关于JOS和一些经验之谈 这一学期的操作系统课使用的是MIT用于教学的JOS操作系统,并且StonyBrook在其基础上做了大量改动,最重要的变化就是从32位移植到了64位.因为个人之前曾系统学习过Linux 0.11内核(<操作系统内核Hack:(四)内核雏形>,实现到时钟中断部分停下了),深知自己从零开始实现内核的工作量.即便是如我个人实现的MiniOS这种简单的不能再简单的,也是需要花费很多时间和精力的.虽然这些付出非常值得(在上这门课时给我带

历史上最知名的15位计算机科学家

麻省理工学院媒体实验室推出了一个名为"Pantheon"的项目试图量化历史人物对文化的影响. 基于维基百科上超过 11,000 位历史人物的数据,他们创建出了一种名为"历史人气指数(HPI)"的参数.这一参数根据语言数量.页面访问量等数据得出.相较演员.运动员和艺术家,著 名的技术人员仍普遍得分较低.以下列出了 15 个历史上最知名的计算机科学家,其中许多都是开源界的名人.我们来看一下他们的"HPI"分数. 14. Ward Cunningham

从马文到AlphaGo AI走过了怎样的70年?

(原标题:从马文·明斯基到AlphaGo,人工智能走过了怎样的70年?) [编者按]从19世纪中叶人工智能的萌芽时期,到现今人工智能的重生,从马文·明斯基到AlphaGo,历史上发生了哪些激动人心的故事?本文以此铺展人工智能发展近70年来背后发生的故事.作者@沐阳浸月,中科院自动化所复杂系统国家重点实验室研究生,主攻机器人与人工智能. 前不久,在人工智能领域发生了两件大事,一个就是是伟大的人工智能先驱马文·明斯基教授逝世,一个是谷歌AlphaGo击败欧洲围棋冠军,职业围棋二段樊麾. 马文·明斯基

模拟电路学习入门的建议(综合整理)

转载自:http://bbs.eetop.cn/viewthread.php?tid=170164 ”模拟电路学习入门“ "如何才算学好模电,数电"一帖引来大家的关注.这里把各位DX的意见整理了一下,便于大家参考.真正掌握一门技术不容易,大家从不同的侧面和经验谈谈自己的体会,对从事这行业的工程师会有点启迪. 找些實用線路集錦或電子製作書看看,有感興趣的就找找理論資料,然後動手練練,很快就是門內漢(or女)了! 先看基本书,再动手练习练习 Razav 如果有耐心看大头英文最好,翻译也很不