前面分析完了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\n\tpop %%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\n", page, address);
// 如果申请的页面在内存页面映射字节图中没有置位,则显示警告信息。
if (mem_map[(page - LOW_MEM) >> 12] != 1)
printk ("mem_map disagrees with %p at %p\n", 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。

Linux0.11内核--加载可执行二进制文件之2.change_ldt的更多相关文章

  1. Linux0.11内核--加载可执行二进制文件之3.exec

    最后剩下最核心的函数do_execve了,由于这里为了简单起见我不分析shell命令的情况, /* * 'do_execve()'函数执行一个新程序. */ //// execve()系统中断调用函数 ...

  2. Linux0.11内核--加载可执行二进制文件之1.copy_strings

    从现在开始就是分析最后的核心模块exec.c了,分析完这个文件后,就会和之前的所有分析形成一个环路,从创建进程.加载进程程序到进程调度.内存管理. exec.c的核心do_execve函数很长,而且用 ...

  3. ASM:《X86汇编语言-从实模式到保护模式》第13章:保护模式下内核的加载,程序的动态加载和执行

    ★PART1:32位保护模式下内核简易模型 1. 内核的结构,功能和加载 每个内核的主引导程序都会有所不同,因为内核都会有不同的结构.有时候主引导程序的一些段和内核段是可以共用的(事实上加载完内核以后 ...

  4. JavaScript 的性能优化:加载和执行

    随着 Web2.0 技术的不断推广,越来越多的应用使用 javascript 技术在客户端进行处理,从而使 JavaScript 在浏览器中的性能成为开发者所面临的最重要的可用性问题.而这个问题又因 ...

  5. [转]JavaScript 的性能优化:加载和执行

    原文链接:http://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/index.html?ca=drs- JavaScript 的性能优化: ...

  6. bootm命令中地址参数,内核加载地址以及内核入口地址

    bootm命令只能用来引导经过mkimage构建了镜像头的内核镜像文件以及根文件镜像,对于没有用mkimage对内核进行处理的话,那直接把内核下载到连接脚本中指定的加载地址0x30008000再运行就 ...

  7. Linux-0.11内核源代码分析系列:内存管理get_free_page()函数分析

    Linux-0.11内存管理模块是源码中比較难以理解的部分,如今把笔者个人的理解发表 先发Linux-0.11内核内存管理get_free_page()函数分析 有时间再写其它函数或者文件的:) /* ...

  8. 内核加载与linux的grub

    计算机系统的启动是一个复杂的过程,启动过程大致可以分为以下几个阶段: +------计算机系统启动流程----------------------------- ------------------- ...

  9. JavaScript的性能优化:加载和执行

    随着 Web2.0 技术的不断推广,越来越多的应用使用 javascript 技术在客户端进行处理,从而使 JavaScript 在浏览器中的性能成为开发者所面临的最重要的可用性问题.而这个问题又因 ...

随机推荐

  1. ASP.NET MVC之如何看待内置配置来提高性能优化(四)

    前言 前几篇我们比较基础的讲了下MVC中的知识,这一节我们穿插点知识,讲讲MVC中我们可以提高性能的办法. Razor视图引擎优化(优化一) 我们知道默认情况下配置MVC去解析一个视图会首先约定通过查 ...

  2. Objective-C精选字符串处理方法

    无论是什么编程语言对字符串的操作是少不了的,对复杂的字符串的分析和操作我们可以用正则表达式来达到我们的目的.简单的字符串处理我们可以借助OC中NSString封装好的字符串处理方法,不过前提是你得了解 ...

  3. 【实时】DevExpress内存监视

    前言 在做项目的时候,我们有时候需要检测项目的内存占用情况,有时候是检测内存泄露~,有时候是查看某段代码执行前后的内存对比,以方便找出问题并以解决. 内存泄漏也称作“存储渗漏”,用动态存储分配函数动态 ...

  4. MVC, MVP, MVVM比较以及区别(下)

    上一篇得到大家的关注,非常感谢.一些朋友评论中,希望快点出下一篇.由于自己对于这些模式的理解也是有限,所以这一篇来得迟了一些.对于这些模式的比较,是结合自己的理解,一些地方不一定准确,但是只有亮出自己 ...

  5. geotrellis使用(二十一)自动导入数据

    目录 前言 整体介绍 前台界面 后台控制 总结 一.前言        之前Geotrellis数据导入集群采用的是命令行的方式,即通过命令行提交spark任务来ingest数据,待数据导入完毕再启动 ...

  6. java.util.concurrent包详细分析--转

    原文地址:http://blog.csdn.net/windsunmoon/article/details/36903901 概述 Java.util.concurrent 包含许多线程安全.测试良好 ...

  7. MySQL学习(一)MySQLWorkbench(MySQL可视化工具)下载,安装,测试连接,以及注意事项

    PS:MySQLWorkbench是MYSQL自带的可视化工具,无论使用哪个可视化工具,其实大同小异,如果想以后走的更远的话,可以考虑使用命令行操作数据库MYSQL.可视化工具让我们初学者更能理解数据 ...

  8. js构建ui的统一异常处理方案(一)

    从早期从事基于java的服务器端开发,再到之后从事基于web和js的ui开发,总体感觉基于web页面的ui开发远不如服务器端健壮.主要是早期ie浏览器功能太弱小,很多业务被迫放到服务器端去实现,浏览器 ...

  9. JS正则表达式总结

    关于JS的正则用法,已经有很多文章了,大同小异 正则表达式30分钟入门教程 MDN正则表达式 玩转JavaScript正则表达式 ES6正则的扩展

  10. .Net(c#)模拟Http请求之HttpWebRequest封装

    一.需求: 向某个服务发起请求获取数据,如:爬虫,采集. 二.步骤(HttpWebRequest): 无非在客户端Client(即程序)设置请求报文(如:Method,Content-Type,Age ...