// Mark all environments in 'envs' as free, set their env_ids to 0,
// and insert them into the env_free_list.
// Make sure the environments are in the free list in the same order
// they are in the envs array (i.e., so that the first call to
// env_alloc() returns envs[0]).
void
env_init(void)
{
// Set up envs array
// LAB 3: Your code here.
env_free_list = NULL;
int i;
for (i = NENV -1; i >= 0; i--){
envs[i].env_id = 0;
envs[i].env_parent_id = 0;
envs[i].env_type = ENV_TYPE_USER;
envs[i].env_status = ENV_FREE;
envs[i].env_runs = 0;
envs[i].env_pgdir = NULL;
envs[i].env_link = env_free_list;
env_free_list = &envs[i];
}
cprintf("env_free_list : 0x%08x, & envs[i]: 0x%08x\n", env_free_list, &envs[i]);
// Per-CPU part of the initialization
env_init_percpu();
}
这个函数是用来初始化所有环境中的envs结构,并把他们全部插入到空闲队列env_free_list中。
在文章开头部分提到了在 inc/env.h 中可以查看 envs的结构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
};
//
// Initialize the kernel virtual memory layout for environment e.
// Allocate a page directory, set e->env_pgdir accordingly,
// and initialize the kernel portion of the new environment's address space.
// Do NOT (yet) map anything into the user portion
// of the environment's virtual address space.
//
// Returns 0 on success, < 0 on error. Errors include:
// -E_NO_MEM if page directory or table could not be allocated.
//
static int
env_setup_vm(struct Env *e)
{
int i;
struct PageInfo *p = NULL;
// Allocate a page for the page directory
if (!(p = page_alloc(ALLOC_ZERO)))
return -E_NO_MEM;
// Now, set e->env_pgdir and initialize the page directory.
//
// Hint:
// - The VA space of all envs is identical above UTOP
// (except at UVPT, which we've set below).
// See inc/memlayout.h for permissions and layout.
// Can you use kern_pgdir as a template? Hint: Yes.
// (Make sure you got the permissions right in Lab 2.)
// - The initial VA below UTOP is empty.
// - You do not need to make any more calls to page_alloc.
// - Note: In general, pp_ref is not maintained for
// physical pages mapped only above UTOP, but env_pgdir
// is an exception -- you need to increment env_pgdir's
// pp_ref for env_free to work correctly.
// - The functions in kern/pmap.h are handy.
// LAB 3: Your code here.
(p->pp_ref)++;
pde_t* page_dir = page2kva(p);
memcpy(page_dir, kern_pgdir, PGSIZE);
e->env_pgdir = page_dir;
// UVPT maps the env's own page table read-only.
// Permissions: kernel R, user R
e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U;
return 0;
}
为新环境初始化目录页,并初始化内和部分新环境的地址空间。不同的进程有不同的虚拟地址空间,所以就会有自己的页目录和页表,该函数的任务就是初始化页目录。
首先分配一个空闲页面当页目录。然后将这个页目录映射内核地址空间,不需要映射页表因为可以和内核共用页表。接着添加对此虚拟页面的物理页面引用:将此页面的虚拟地址赋值给进程的pgdir,然后使得进程有权限操作自己的pgdir。
p是一个Page结构,分配完后为一个指针类型,想要将一个指针返回他的虚拟地址那么就要用到page2kva函数,将其转换为地址。Page结构中的 pp_ref 指的是页面的引用次数。这里要加一是因为 env_pgdir 对应的 Page 被某一用户进程使用,会出现释放掉当前未结束的用户进程的资源的情况,导致用户进程崩溃。
void
bootmain(void)
{
struct Proghdr *ph, *eph;
// read 1st page off disk
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
// is this a valid ELF?
if (ELFHDR->e_magic != ELF_MAGIC)
goto bad;
// load each program segment (ignores ph flags)
ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
eph = ph + ELFHDR->e_phnum;
for (; ph < eph; ph++)
// p_pa is the load address of this segment (as well
// as the physical address)
readseg(ph->p_pa, ph->p_memsz, ph->p_offset);
// call the entry point from the ELF header
// note: does not return!
((void (*)(void)) (ELFHDR->e_entry))();
bad:
outw(0x8A00, 0x8A00);
outw(0x8A00, 0x8E00);
while (1)
/* do nothing */;
}