标签:
Allocating the Environments Array
In lab 2, you allocated memory in mem_init() for the pages[] array, which is a table the kernel uses to
keep track of which pages are free and which are not. You will now need to modify mem_init() further to
allocate a similar array of Env structures, called envs .
Creating and Running Environments
要知道JOS是没有文件系统的,那么如果在JOS上运行用户程序怎么办?——直接和内核镜像结合在一起,在编译时期就做好....希望以后自己完成JOS所有实验之后能够对其改进,照猫画虎的把Minix的文件系统添加进去
补全env.c缺失的部分.
inc/env.h里面又关于env.c要用到的结构体或者宏定义的相关信息
这里进程运行的状态还可以联想linux做对比,linux中进程状态又TASK_RUNNING,TASK_INTERRUPTIBLE,TASK_UNINTERRUPTIBLE,TASK_STOPPED等状态
env_init部分 对envs指向的 struct Env数组做初始化操作,注意这里temp--,这种初始化的方向是故意的,
为了确保最后env_free_list指向最开始的结构体即envs[0]。
The JOS kernel keeps all of the inactive Env structures on the env_free_list . This design allows easy
allocation and deallocation of environments, as they merely have to be added to or removed from the free list.
void env_init(void) { // Set up envs array // LAB 3: Your code here. int temp = 0; env_free_list = NULL; cprintf("NENV -1 : %u\n",NENV -1); for(temp = NENV -1;temp >= 0;temp--) { envs[temp].env_id = 0; envs[temp].env_parent_id = 0; envs[temp].env_type = ENV_TYPE_USER; envs[temp].env_status = 0; envs[temp].env_runs = 0; envs[temp].env_pgdir = NULL; envs[temp].env_link = env_free_list; env_free_list = &envs[temp]; } cprintf("env_free_list : 0x%08x, &envs[temp]: 0x%08x\n",env_free_list,&envs[temp]); // Per-CPU part of the initialization env_init_percpu(); }
env_setup_vm部分为进程分配内存空间!单独的分配page directory—— p
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; // 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; }
region_alloc 为environment *e 开辟len byte大小的物理空间,并将va虚拟地址开始的len长度大小的空间和物理空间建立映射关系(其实干活儿的还是page_insert 哈哈~).
static void region_alloc(struct Env *e, void *va, size_t len) { // LAB 3: Your code here. // (But only if you need it for load_icode.) // // Hint: It is easier to use region_alloc if the caller can pass // 'va' and 'len' values that are not page-aligned. // You should round va down, and round (va + len) up. // (Watch out for corner-cases!) va = ROUNDDOWN(va,PGSIZE); len = ROUNDUP(len,PGSIZE); struct PageInfo *pp; int ret = 0; for(;len > 0; len -= PGSIZE, va += PGSIZE) { pp = page_alloc(0); if(!pp) { panic("region_alloc failed!\n"); } ret = page_insert(e->env_pgdir,pp,va,PTE_U | PTE_W); if(ret) { panic("region_alloc failed!\n"); } } }
static void load_icode(struct Env *e, uint8_t *binary) { // LAB 3: Your code here. struct Elf* elfhdr = (struct Elf*)binary; struct Proghdr* ph,*eph; if(elfhdr->e_magic != ELF_MAGIC) { panic("elf header's magic is not correct\n"); } ph = (struct Proghdr*)((uint8_t*)elfhdr + elfhdr->e_phoff); eph = ph + elfhdr->e_phnum; lcr3(PADDR(e->env_pgdir)); for(;ph < eph; ph++) { if(ph->p_type != ELF_PROG_LOAD) { continue; } if(ph->p_filesz > ph->p_memsz) { panic("file size is great than memory size\n"); } region_alloc(e,(void*)ph->p_va,ph->p_memsz); memmove((void*)ph->p_va,binary+ph->p_offset,ph->p_filesz); memset((void*)ph->p_va + ph->p_filesz,0,(ph->p_memsz - ph->p_filesz)); } e->env_tf.tf_eip = elfhdr->e_entry; // Now map one page for the program's initial stack // at virtual address USTACKTOP - PGSIZE. // LAB 3: Your code here. lcr3(PADDR(kern_pgdir)); region_alloc(e,(void*)USTACKTOP - PGSIZE,PGSIZE); }
env_create 函数
void env_create(uint8_t *binary, enum EnvType type) { // LAB 3: Your code here. int ret = 0; struct Env *e = NULL; ret = env_alloc(&e,0); if(ret < 0) { panic("env_create: %e\n",r); } load_icode(e,binary); e->env_type = type; }
env_run 函数是正真进程切换的地方.
To run an environment, the kernel must set up the CPU with both the saved registers and the appropriate address space.
void env_run(struct Env *e) { // LAB 3: Your code here. // panic("env_run not yet implemented"); if(curenv && curenv->env_status == ENV_RUNNING) { curenv->env_status = ENV_RUNNABLE; } curenv = e; e->env_status = ENV_RUNNING; e->env_runs++; lcr3(PADDR(e->env_pgdir)); env_pop_tf(&(e->env_tf)); }
If all goes well, your system should enter user space and execute the hello binary until it makes a system call with the int instruction. At that point there will be trouble, since JOS has not set up the hardware
to allow any kind of transition from user space into the kernel. When the CPU discovers that it is not set up to handle this
system call interrupt, it will generate a general protection exception, find that it can‘t handle that, generate a double fault exception, find that it can‘t handle that either, and finally give up with what‘s known as a "triple fault". Usually,you
would then see the CPU reset and the system reboot.
So .如果完成了以上部分,JOS启动时回不断的重启重启,这是正常现象....表慌,没错,接着做就是了.
如果没有出现这种现象引起的重启... 骚年, debug吧!... 你代码有问题.
在lab3中, 在kern/Makefrag里面会看到-b 的链接选项
The -b binary option on the linker command line causes these files to be linked in as "raw" uninterpreted binary files rather than as regular .o files produced by the compile
编译成功的时候,会发现在 ./lab/obj/kern/kernel.sym里面会有这样一段:
我不会说我这这破问题这儿折腾了两天了, 问候一下神兽.
在0x800b56这个地方(sys_cputs)的入口地方
当触发这个中断的时候系统就会重启. 如果你不能执行到这一步,就说明之前的地址空间配置还没有做好....
好的, 下一关!
Basics of Protected Control Transfer
Exceptions and interrupts are both "protected control transfers," which cause the processor to switch from user to kernel mode (CPL=0) without giving the user-mode code any opportunity to interfere
with the functioning of the kernel or other environments. In Intel‘s terminology, an interrupt is a protected control transfer that is caused by an asynchronous event usually external to the processor, such as notification of external device I/O activity.
An exception, in contrast, is a protected control transfer caused synchronously by the currently running code, for example due to a divide by zero or an invalid memory access.
如果不知道啥是CPL, 可以移步这里,看看 http://blog.csdn.net/cinmyheart/article/details/40075257
Handling Interrupts and Exceptions
At this point, the first int $0x30 system call instruction in user space is a dead end: once the processor gets into user mode, there is no way to get back out.
前面那个 int 0x30中断挂掉, 进入了用户空间就回不来鸟...所以现在要想办法让他回来...
这时候我听到某个歌声 "你快回来.....我的..."
Types of Exceptions and Interrupts (这里讨论了异常和中断的区别)
All of the synchronous exceptions that the x86 processor can generate internally use interrupt vectors between 0 and 31, and therefore map to IDT entries 0-31. For example, a page fault always causes an exception through vector 14. Interrupt vectors
greater than 31 are only used by software interrupts, which can be generated by the int instruction, or asynchronous hardware interrupts, caused by external devices when they need attention.
同步的是异常, 异步的是中断. 异常的中断向量在0~31之间, 而中断的中断向量大于31.
在kern/trap.c 里面的trapname()函数中我们可以对应的看到各种trap名字
补全好函数, 代码有点小多, 不贴出来了, github传送门. 去看lab3的分支中对应文件就可以了.
https://github.com/jasonleaster
后面还有坑... 先抛粗来了再填....
2015年2月 <<扁担和竹篮的生活>>
标签:
原文地址:http://blog.csdn.net/cinmyheart/article/details/40023965