Chinaunix首页 | 论坛 | 博客
  • 博客访问: 31873
  • 博文数量: 7
  • 博客积分: 250
  • 博客等级: 二等列兵
  • 技术积分: 82
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-10 08:44
文章分类

全部博文(7)

文章存档

2011年(2)

2008年(5)

我的朋友
最近访客

分类: LINUX

2008-10-13 16:16:32

  • GDT:
      每个进程的局部段描述符LDT都作为一个独立的段而存在,在全局描述表GDT中要有一个表项指向这个段的起始地址,并说明该段的长度和其它一些参数,此外,每个进程还有一个 TSS 结构也是一样。所以,每个进程都要在全局段描述表GDT中占据两个表项。在段寄存器中用作GDT表下标的位段宽度是13位,所以GDT中可以有(2 ** 13) 8192个描述项。除去一些系统开销(如GDT中的
      第1项永远为0,
      第2, 3项分别用于__KERNEL_CS, __KERNEL_DS;
      第4, 5项永远用于当前进程的__USER_CS, __USER_DS)
以外,尚有8180个表项可供使用,所以理论上系统中最大的进程数量为 8180/2 = 4090项.
  • 为什么DMA物理页面要单独加以管理?
      当DMA所需的缓冲区超过一个物理页面的大小时,就要求两个页面在物理上连续,因为此时DMA控制器不能依靠在CPU内部的MMU将连续的虚存页面映射到物理上不连续的页面,而在i386cpu中,页式存储管理的硬件支持是在CPU内部实现的,而不像另有些CPU那样由一个单独的MMU提供,所以DMA不经过MMU提供的地址映射。
  • error_code:
      bit 0 == 0: 没有找到page, 1: 保护错误;
      bit 1 == 0: 读;          1: 写;
      bit 2 == 0: 内核模式;     1: 用户模式;
      bit 3 == 1: 探测到使用了保留位;
      bit 4 == 1: 是一个取指错误;
   
  • 越界访问:
      何时会发生缺页中断:
      1.相应的页面目录项或页面表项为空,即该线性地址与物理地址的映射关系尚未建立,或已撤销。
      2.相应的物理页面不再内存。
      3.指令中规定的访问方式和页面权限不符。
      页面异常服务程序的主体入口处为 do_page_fault(),其处理流程为:
      1.当i386 CPU产生缺页中断异常时,CPU将导致映射失败的线性地址放到控制寄存器CR2中。
      2.内核的中断/异常响应机制传来两个参数。(pt_regs *regs, u32 error_code)regs指向例外
        发生前夕CPU中各寄存器内容的一份副本。这是由内核的中断响应机制保存下来的"现场"。
        error_code则进一步指明映射失败的具体原因。
      3.获取当前进程的task_struct数据结构 task_struct *tsk = current
      4.检测两个特殊情况。
         a. in_interrupt()返回非0,说明映射失败发生在某个中断服务程序中,
            所以与当前进程毫无关系.
         b. current->mm == NULL, 说明该进程的映射还未建立,所以也与当前进程无关。说明这次异
            常发生在某个 中断/异常 服务程序中。此时,用goto跳转到 no_cotext()
      5.判断发生异常的线性地址是否落在某个已经建立起映射的区间。或者指出在那个区间:find_vma()
         a. 如果地址在3G以上,则也是越界,即访问了系统空间,此时控制流转向 bad_area.
         b. 如果找到了,说明给定的地址恰好在落在这个区间,控制流转向 good_area.
         c. 剩下一种情况是,所给的线性地址正好落在两个区间之间的地址空洞里。也就是说该地址所在
            页面的映射尚未建立或已经撤销。在用户虚存空间中,可能有2中不同的空洞。
              1. 只能有一个,即堆栈区以下的那个大空洞。它代表着供动态分配的剩余空间。当
                 VM_GROWSDOWN为 1 时,说明空洞上方的区间是堆栈区间。
              2. 虚存空间之间的空洞。可以有多个。如果该线性地址的vm_flags中的标VM_GROWSDOWN
                 为0,则说明空洞上方的区间并非堆栈区说明,则这个空洞是因为一个映射区间被撤销留
                 下的。或者在建立映射时跳过了一块地址。
      6.当控制流到达bad_area后,已经不再需要互斥了(因为不再对mm_struct结构进行操作),所以通
        过up()退出临界区。接着就要进一步考察error_code,看看失败的具体原因:
          error_code:
            bit2 == 1,表面失败是当CPU处于用户模式时发生的。说明所给的线性地址为虚存空间的空洞
                      则对当前进程task_struct结构内的一些成分进行一些设置,就向该进程发送一
                      个强制信号: SIGSEGV。 至此,本次例外服务结束。
  • 用户堆栈的扩展  
      越界访问有时候是正常的。这只发生在一种情况下:当用户堆栈过小,但是因越界访问而"因祸得福"得
      以延展的情况。扩展流程如下:
      1.判断空洞上方的区间是否是堆栈区间。当VM_GROWSDOWN == 1 时,为堆栈区间。
      2.当映射失败发生在用户空间时(即 bit2 == 1),则因堆栈操作引起的越界是作为特殊情况对待的
      3.检查发生异常时的地址是否紧挨着堆栈指针所指的地方。X86中考虑到(pusha指令的存在,一次将
        32个字节压栈),所以 越界地址要 > %esp - 32。若小于,则转向 bad_area.
      4.至此,则该地址为正常的堆栈扩展要求,所以从空洞的顶部开始分配若干页面建立映射,并将之并
        入堆栈区间,使其得以扩展。上述操作通过调用expand_stack()即可(include/linux/mm.h).
  • expand_stack(struct vm_area_struct *vma, unsigned long address)
      vm_area_struct *vma 代表一个区间,在这里代表着用户空间堆栈所在的区间。
      unsigned long address 代表越界地址。
     
      1. 首先,将地址按页面边界对齐,并计算需要增长几个页面才能把给定地址包括进来(通常1个)
      2. 每个进程的task_struct结构中有个rlim结构数组,规定了对每种资源分配使用的限制,而
         RLIMIT_STACK就是对用户空间堆栈大小的限制。所以,先要进行检查。
      3. 如果分配失败,则返回-ENOMEM,在do_page_fault()也会转向bad_area。如果分配成功
         则返回0,一般都会成功的。
      4. expend_stack()只是改变了堆栈区的vm_area_struct结构,而并未建立起新扩展的页面对
         物理内存的映射。这个任务由接下来的good_area完成。
  • 缺页中断 ? no 缺页异常:
中断或者陷阱(trap指令)发生时: cpu都会将下一条指令地址压栈作为中断服务的返回地址。
异常发生时: cpu将因无法完成而夭折的指令本身的地址压入栈中,这样,就可以在异常处理
           返回时完成未完成的工作。此特性由CPU内部电路实现。


物理页面的使用和周转

阅读(1137) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~