zoukankan      html  css  js  c++  java
  • Linux0.11内核--加载可执行二进制文件之2.change_ldt

    前面分析完了copy_strings函数,这里来分析另一个注意的函数change_ldt。

    先来看调用处:

    // 根据a_text 修改局部表中描述符基址和段限长,并将参数和环境空间页面放置在数据段末端。
    // 执行下面语句之后,p 此时是以数据段起始处为原点的偏移值,仍指向参数和环境空间数据开始处,
    // 也即转换成为堆栈的指针。
      p += change_ldt (ex.a_text, page) - MAX_ARG_PAGES * PAGE_SIZE;
    

    解释的很清楚,也就是说p指向的是相当于在图9-23的左方添加了64M-MAX_ARG_PAGES * PAGE_SIZE的大小容量。总容量为64M。

    struct exec
    {
      unsigned long a_magic;	/* 执行文件魔数。使用N_MAGIC 等宏访问。 */
      unsigned a_text;		/* 代码长度,字节数。 */
      unsigned a_data;		/* 数据长度,字节数。 */
      unsigned a_bss;		/* 文件中的未初始化数据区长度,字节数。 */
      unsigned a_syms;		/* 文件中的符号表长度,字节数。 */
      unsigned a_entry;		/* 执行开始地址。 */
      unsigned a_trsize;		/* 代码重定位信息长度,字节数。 */
      unsigned a_drsize;		/* 数据重定位信息长度,字节数。 */
    };
    
    // 下面对执行文件的头结构数据进行处理,首先让ex 指向执行头部分的数据结构。
      ex = *((struct exec *) bh->b_data);	/* read exec-header *//* 读取执行头部分 */
    

    这里的ex为读取的可执行二进制文件头部分。下面进入change_ldt函数:

    //// 修改局部描述符表中的描述符基址和段限长,并将参数和环境空间页面放置在数据段末端。
    // 参数:text_size - 执行文件头部中a_text 字段给出的代码段长度值;
    // page - 参数和环境空间页面指针数组。
    // 返回:数据段限长值(64MB)。
    static unsigned long
    change_ldt (unsigned long text_size, unsigned long *page)
    {
      unsigned long code_limit, data_limit, code_base, data_base;
      int i;
    
    // 根据执行文件头部a_text 值,计算以页面长度为边界的代码段限长。并设置数据段长度为64MB。
      code_limit = text_size + PAGE_SIZE - 1;
      code_limit &= 0xFFFFF000;
      data_limit = 0x4000000;
    // 取当前进程中局部描述符表代码段描述符中代码段基址,代码段基址与数据段基址相同。
      code_base = get_base (current->ldt[1]);
      data_base = code_base;
    // 重新设置局部表中代码段和数据段描述符的基址和段限长。
      set_base (current->ldt[1], code_base);
      set_limit (current->ldt[1], code_limit);
      set_base (current->ldt[2], data_base);
      set_limit (current->ldt[2], data_limit);
    /* make sure fs points to the NEW data segment */
    /* 要确信fs 段寄存器已指向新的数据段 */
    // fs 段寄存器中放入局部表数据段描述符的选择符(0x17)。
      __asm__ ("pushl $0x17
    	pop %%fs"::);
    // 将参数和环境空间已存放数据的页面(共可有MAX_ARG_PAGES 页,128kB)放到数据段线性地址的
    // 末端。是调用函数put_page()进行操作的(mm/memory.c, 197)。
      data_base += data_limit;
      for (i = MAX_ARG_PAGES - 1; i >= 0; i--)
        {
          data_base -= PAGE_SIZE;
          if (page[i])		// 如果该页面存在,
    	put_page (page[i], data_base);	// 就放置该页面。
        }
      return data_limit;		// 最后返回数据段限长(64MB)。
    }
    

    第一二行的意思是code_limit最少也要有一内存页的长度。

    data_limit赋值为64M。

    紧接着设置当前进程的LDT的代码段和数据段的基址和段限长。

    注意最后一段比较关键,从数据段末尾data_base开始放置参数和环境空间已存放数据的页面。page是参数环境空间所有页面地址的数组。如果page[i]该页面存在,就调用put_page函数:

    /*
    * 下面函数将一内存页面放置在指定地址处。它返回页面的物理地址,如果
    * 内存不够(在访问页表或页面时),则返回0。
    */
    //// 把一物理内存页面映射到指定的线性地址处。
    // 主要工作是在页目录和页表中设置指定页面的信息。若成功则返回页面地址。
    // 在缺页异常的C 函数do_no_page()中会调用此函数。对于缺页引起的异常,由于任何缺页缘故而
    // 对页表作修改时,并不需要刷新CPU 的页变换缓冲(或称Translation Lookaside Buffer,TLB),
    // 即使页表项中标志P 被从0 修改成1。因为无效页项不会被缓冲,因此当修改了一个无效的页表项
    // 时不需要刷新。在此就表现为不用调用Invalidate()函数。
    unsigned long
    put_page (unsigned long page, unsigned long address)
    {
      unsigned long tmp, *page_table;
    
    /* NOTE !!! This uses the fact that _pg_dir=0 */
    /* 注意!!!这里使用了页目录基址_pg_dir=0 的条件 */
    
    // 如果申请的页面位置低于LOW_MEM(1Mb)或超出系统实际含有内存高端HIGH_MEMORY,则发出警告。
      if (page < LOW_MEM || page >= HIGH_MEMORY)
        printk ("Trying to put page %p at %p
    ", page, address);
    // 如果申请的页面在内存页面映射字节图中没有置位,则显示警告信息。
      if (mem_map[(page - LOW_MEM) >> 12] != 1)
        printk ("mem_map disagrees with %p at %p
    ", page, address);
    // 计算指定地址在页目录表中对应的目录项指针。
      page_table = (unsigned long *) ((address >> 20) & 0xffc);
    // 如果该目录项有效(P=1)(也即指定的页表在内存中),则从中取得指定页表的地址??page_table。
      if ((*page_table) & 1)
        page_table = (unsigned long *) (0xfffff000 & *page_table);
      else
        {
    // 否则,申请空闲页面给页表使用,并在对应目录项中置相应标志7(User, U/S, R/W)。然后将
    // 该页表的地址??page_table。
          if (!(tmp = get_free_page ()))
    	return 0;
          *page_table = tmp | 7;
          page_table = (unsigned long *) tmp;
        }
    // 在页表中设置指定地址的物理内存页面的页表项内容。每个页表共可有1024 项(0x3ff)。
      page_table[(address >> 12) & 0x3ff] = page | 7;
    /* no need for invalidate */
    /* 不需要刷新页变换高速缓冲 */
      return page;			// 返回页面地址。
    }
    

    首先通过data_base获取到相对应的页目录项指针,如果有效则获取页表地址。比较关键的是最后一句,address>>12表示data_base/4k,再与0x3ff(1024个项),因为这里不是字节而是索引,所以不是0xffc。然后用这个索引和page(参数和环境空间已存放数据的页面地址)绑定。

    最后返回64M。

  • 相关阅读:
    Count and Say
    Roman to Integer LeetCode Java
    白菜刷LeetCode记-121. Best Time to Buy and Sell Stock
    白菜刷LeetCode记-103. Binary Tree Zigzag Level Order Traversal
    白菜刷LeetCode记-102. Binary Tree Level Order Traversal
    白菜刷LeetCode记-350. Intersection of Two Arrays II
    白菜刷LeetCode记-268. Missing Number
    白菜刷LeetCode记-378. Kth Smallest Element in a Sorted Matrix
    白菜刷LeetCode记-328. Odd Even Linked List
    白菜刷LeetCode记-230. Kth Smallest Element in a BST
  • 原文地址:https://www.cnblogs.com/joey-hua/p/5638481.html
Copyright © 2011-2022 走看看