先看看xv6的进程创建和切换机制,再做jos实验lab3

xv6

1. 创建第一个进程

userinit()

1. 从进程结构数组找到一个空闲的进程结构 struct proc
2. 为进程创建页目录pgdir,并在其中对内核区域进行映射(setupkvm, kmap)
3. 申请一个物理页,复制initcode,并将其映射到0地址开始。
4. 设置进程的trapframe数据,进程的栈如下图。
5. 将进程状态设置为RUNNABLE

2. 进程切换

刚刚创建好第一个进程init,那么如何切换到该进程环境中去执行?

scheduler() 
1. 到进程数组中找到状态为RUNNABLE的进程。当前系统只有init这个进程
2. switchuvm()。其中做了几件事
   a. 设置当前任务的TSS段
   b. 设置tss段中的ss0和esp0。用户模式通过异常中断等切换到内核态时,
      特权级提高。处理器会从当前tr寄存器指向的tss段中拿出ss0和esp0设置0特权
      级的栈。把之前的ss和esp值压入内核栈中。
   c. tr寄存器指向刚设置好的tss段描述符
3. 切换到新的页目录。这之后就运行在进程的地址空间了,但还在内核态。

void
switchuvm(struct proc *p)
{pushcli();cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0);cpu->gdt[SEG_TSS].s = 0;cpu->ts.ss0 = SEG_KDATA << 3;cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE;ltr(SEG_TSS << 3);if(p->pgdir == 0)panic("switchuvm: no pgdir");lcr3(v2p(p->pgdir));  // switch to new address spacepopcli();
}

4. 切换到进程的context中去。新建进程context中的eip指向forkret,那么ret指令
之后就跳到forkret中去。注意此时esp指向进程内核栈中trapret这个位置(见上图)。

swtch(&cpu->scheduler, proc->context);

swtch:movl 4(%esp), %eaxmovl 8(%esp), %edx# Save old callee-save registerspushl %ebppushl %ebxpushl %esipushl %edi# Switch stacksmovl %esp, (%eax)movl %edx, %esp# Load new callee-save registerspopl %edipopl %esipopl %ebxpopl %ebpret

5. forkret()的最后一条指令肯定是ret,这个指令从栈中找eip。
此时找到的就是trapret了。

6. 继续见图中,栈指针esp现在指向内核栈中trapframe。
trapret就将trapframe中的数据全部弹出来。

.globl trapret
trapret:popalpopl %gspopl %fspopl %espopl %dsaddl $0x8, %esp  # trapno and errcodeiret

注意最后一条iret弹出了eip,cs,eflags。在userinit()中有设置它们的值。
此时弹出cs后,由于cs的特权级发生了改变,所以要继续在栈中弹出
ss和esp(这两个指定用户态的栈)。

由内核态切到用户态就是这个iret指令完成的。

userinit()中设置进程的trapframe

  p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
  p->tf->ds = (SEG_UDATA << 3) | DPL_USER;p->tf->es = p->tf->ds;p->tf->ss = p->tf->ds;p->tf->eflags = FL_IF;p->tf->esp = PGSIZE;p->tf->eip = 0;  // beginning of initcode.S

3. fork()

fork()是创建一个新进程,与userinit()类似。userinit()根据已知信息创建第一个进程,

fork()则是在当前进程的基础上创建一个新进程,新进程与父进程有相同的页表。

以上分析可见,xv6中的进程切换有使用tss(任务状态段),但并未直接使用它来作为任务切换。其唯一的作用是在用户态切入内核态时,提供进程的内核栈ss0和esp0。

jos lab3

Part A: User Environments and Exception Handling

1. 进程创建

env_create()
  -> env_alloc(&e, 0);
  -> load_icode(e, binary, size);

首先使用env_alloc()分配一个进程,其中要为新进程创建页目录,并与内核共用UTOP以上的虚拟地址。
如何做到共用虚拟地址呢?只要复制页目录上相应的目录项即可,那么进程的页目录和内核页目录指向相同的内核页表。

load_icode()的目的是要在新进程的地址空间内申请到一片空间,然后将要运行的代码binary复制到该空间去。
由于binary是ELF格式的,所以在开辟空间和复制数据的时候要严格参照ELF头信息中地址.

用户程序如何与内核链接在一起?这样做的目的是什么?
参看如下kern/Makefrag中kernel生成过程。其中ld使用了"-b binary"的参数来将一些数据以二进制的形式与内核链接在一起。
在这里是直接将生成的ELF格式的应用程序(例如hello)以二进制的形式与内核链接在一起。
链接之后,会有一系列符号(例如_binary_obj_user_hello_start, _binary_obj_user_hello_end)。
这些符号代表了ELF文件hello在kernel中的地址信息。这样一来,内核中的代码就可以知道hello程序的位置了。
JOS在没有文件系统时,以这样的方式来加载并执行应用程序。

KERN_BINFILES :=  user/hello \user/buggyhello \user/buggyhello2 \user/evilhello \user/testbss \user/divzero \user/breakpoint \user/softint \user/badsegment \user/faultread \user/faultreadkernel \user/faultwrite \user/faultwritekernel$(V)$(LD) -o $@ $(KERN_LDFLAGS) $(KERN_OBJFILES) $(GCC_LIB) -b binary $(KERN_BINFILES)

最后enu_run()会执行某个进程。可以用gdb来跟踪,看看是否成功切换到用户模式,是否成功执行到了hello的代码。
1. 设置断点
break env_pop_tf
2. env_pop_tf里面将栈切换到新进程的trapframe上。此时我们查看栈上的内容是否正确
   特别要注意栈中的 eip(应该是hello的入口地址), cs(代码段且是用户权限), ss(数据段用户权限)
x/5x $esp
3. 继续si单步。执行完iret指令之后,会跳到hello中,而hello的第一条指令在lib/entry.S中。
4. hello中是打印动作。其最后调用sys_cputs()来打印。这是一个系统调用。
   在hello.sym中找到sys_cputs地址并设置断点。单步执行,最后会停在syscall(lib/syscall.c)的int指令上。
   当前的中断系统还没设置好,跟踪到这里就会出错。

经过这一系列的跟踪,可以了解进程的创建过程,以及系统调用的执行过程。

2. 中断和异常处理

中断和异常都属于保护机制里的内容,当它们发生时,处理器会由用户模式切换到
内核模式(CPL=0)。区别是:
  中断是由处理器外部设备产生的异步通知事件,比如外部I/O中断。
  异常是由代码产生的同步事件,比如除0或访问无效内存。

中断异常处理的保护机制:
1. 中断描述符表(IDT: Interrupt Descriptor Table)
x86有256个中断/异常入口,称之为中断向量。那么IDT中就有256个描述符与这些中断向量一一
对应。中断描述符中存放着中断处理程序的eip和cs, 同时cs的低2位表示中断处理程序运行 
的权限。

2. 任务状态段(TSS: Task State Segment)
跳入中断处理程序之前,要保存返回信息(比如cs和eip),这样中断处理完成之后可以返回到
被中断处继续执行。但是将将这些返回信息保存到哪里才能保证不会被用户程序破坏呢?

出于这个原因,当x86发生中断或陷阱(trap)并且引起了权限等级从用户模式切换到内核模式,
此时esp也将切换为内核栈,内核栈的地址由TSS指定。然后,处理器再将之前用户模式的
ss, esp, eflags, cs, eip压入内核栈中,如果是陷阱的话,还会再压入一个错误码。

TSS还有其他的功用,但JOS只用它来获取内核栈地址。

3. 中断/异常嵌套

中断或异常时如果没有发生权限级别的切换,则不会切换栈。比如,当前处理器正在处理一个中断,
此时处于内核模式。如果这时发生了中断或异常,由于没有从低优先级到高优先级切换,此时不会
会仍继续使用之前的ss和esp,并继续向其中压入eflags, cs, eip。

内核需要很好的处理中断/异常嵌套,也要防止嵌套过深而导致内核栈溢出。

中断门和陷阱门的区别在于处理器对于IF标志的处理上:
经过中断门后,处理器会清掉IF位以屏蔽所有中断,防止了中断嵌套。IRET指令又会恢复IF标志。

经过陷阱门时不改变IF位。

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

1. 缺页异常处理

发生缺页异常时,处理器会将产生错误的线性地址存入CR2寄存器中。

2. 断点异常

The processor checks the DPL of the interrupt or trap gate only if an exception or
interrupt is generated with an INT n, INT 3, or INTO instruction. Here, the CPL
must be less than or equal to the DPL of the gate. This restriction prevents
application programs or procedures running at privilege level 3 from using a
software interrupt to access critical exception handlers, such as the page-fault
handler, providing that those handlers are placed in more privileged code
segments (numerically lower privilege level). For hardware-generated interrupts
and processor-detected exceptions, the processor ignores the DPL of interrupt
and trap gates.

先看下上面这段英文说明。当软件使用int指令产生异常时会检查CPL<=DPL,即当前运行环境
必须有权限去访问到IDT中对应的描述符,否则会产生一般保护错误(General Protection)。
所以设置breakpoint的中断向量时,DPL=3

转载于:https://www.cnblogs.com/sammei/archive/2012/11/26/3295607.html

6.828 - lab3相关推荐

  1. 6.S081 Lab3 page tables

    6.S081 Lab3 page tables 未完成 文章目录 6.S081 Lab3 page tables 未完成 1. Print a page table ([easy](https://p ...

  2. OSPF分解试验部分-LAB3:OSPF各种网络类型试验

    LAB3:OSPF各种网络类型试验 1.NBMA试验 试验需求: Frame-relay使用物理接口,OSPF建立后,默认应该是NBMA网络类型.我们验证NBMA中我们应该如何配置OSPF.   配置 ...

  3. MIT 6.828 JOS学习笔记17. Lab 3.1 Part A User Environments

    Introduction 在这个实验中,我们将实现操作系统的一些基本功能,来实现用户环境下的进程的正常运行.你将会加强JOS内核的功能,为它增添一些重要的数据结构,用来记录用户进程环境的一些信息:创建 ...

  4. 2021年速卖通828年中大促活动报名攻略

    [2021年828大促]报名攻略介绍 活动名称: 2021年 828大促,本指引仅针对「2021年828大促」招商有效,内容 包含活动时间轴.招商范围.包邮要求.价格规则.控价规则.活动库存规则.优惠 ...

  5. 余弦信号频谱表达式_2019年清华828信号与系统试题回忆

    此为2019年清华大学828信号与系统考研试题回忆,仅作学习交流使用 一.输入信号 和LTI系统冲激响应 的波形分别如下图所示,系统初始时刻状态为0. (1)写出输出 的表达式: (2)绘制 的波形并 ...

  6. CS144 计算机网络实验 lab3 笔记

    CS144 计算机网络实验 lab3 笔记 介绍 本实验中,我们将会在之前实验的基础上,实现一个TCP sender ----将字节流转换成数据报并发送. TCP协议是一个在不可靠的协议上提供可靠的, ...

  7. php课程 8-28 php如何绘制生成显示图片

    php课程 8-28 php如何绘制生成显示图片 一.总结 一句话总结:gd库轻松解决 1.php图片操作生成的图的两种去向是什么? 一种在页面直接输出,一种存进本地磁盘 2.php操作图片的库有哪些 ...

  8. CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom

    CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上)smoke fizz CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom 栈结构镇楼 这里先给 ...

  9. MIT6.830 Lab3 Query Optimization 实验报告

    一.实验概览 lab3实现的是基于代价的查询优化器,以下是讲义给出的实验的大纲: Recall that the main idea of a cost-based optimizer is to: ...

  10. Lab3 Attack Lab

    Lab3 缓冲区攻击实验 写在前言:这个实验的来源是CSAPP官网:CSAPP Labs ,如果感兴趣的话,可以点击这个链接

最新文章

  1. 哭了,现在看来高校4月中旬能开学算是早的了,非毕业生可能会更晚
  2. IOS设置导航栏返回按钮,并添加事件返回主页面
  3. 盘点物联网常用的八种通信协议
  4. 遍历HashMap的四种方法
  5. (转)向SDE库中写入栅格和矢量数据
  6. 生产环境nginx平滑升级
  7. 温故知新----表单
  8. dubbo面试题-dubbo源码解析
  9. 阿里云服务器远程桌面安装
  10. Selenium+Java - 结合sikuliX操作Flash网页
  11. Gluster (一)安装
  12. ebay免费模板html,ebay免费模板
  13. Python OpenCV 计算机视觉:1~5
  14. 算法时代必读——《算法霸权》数学杀伤性武器的威胁
  15. Code Combat学习心得(Kithgard地牢45关Mightier Than the Sword)
  16. C语言程序输出水仙花数
  17. 软考不通过能不能补考?解答来了
  18. 联想服务器维修单据,联想ThinkServer SR650服务器故障维修
  19. \\wsl.localhost 无法访问
  20. Codeforces 1492D - Genius‘s Gambit (构造)

热门文章

  1. 【node.js】GET/POST请求、Web 模块
  2. 关于指针初始化的问题 - 回复下雨天2
  3. treeview的checkbox展开节点
  4. 《Orange'S:一个操作系统的实现》与上一版之比较
  5. element ui表单处理的简洁方法
  6. 楼道游戏 c语言,C语言基础题!
  7. IntelliJ IDEA集成Maven
  8. html三个div相同高度,Html三个div并排,高度相同
  9. c语言编程下雪,C语言怎么 实现 下雪效果
  10. linux自动化安装集群,Redis自动化安装以及集群实现