现在的位置: 首页 > 综合 > 正文

Hello World 3

2013年08月31日 ⁄ 综合 ⁄ 共 2973字 ⁄ 字号 评论关闭

本文装载自:http://www.cnblogs.com/xuqiang/archive/2010/03/29/1953689.html

微笑

这里开始分析hello中的一个寻址过程的实现。当然现在的情景是:(当然可能只是一小部分加载到了内存中,大部分的需要使用缺页异常处理来实现内存分配)。

在sys_exece()函数中,在内存ram中保存了命令行参数,环境参数,但是代码段,数据段,bss段,可执行文件的其他段提供”映射“(映射的具体含义参见"深入理解Hello World 3"),此时文件到虚存的映射仅仅是建立了一种映射关系,也就是说,虚存页面到物理页面之间的映射还没有建立。

在说明内存寻址 之前,先来看看进程是如何管理虚拟地址,然后开始说明在没有缺页的情况下,保护模式下的寻址,最后开始hello的情景分析,当hello程序开始寻址内存的某个位置时,到底发生了什么(还是从hello刚被加载到内存中)?

1.进程的地址空间及管理

该部分参见的是《深入理解linux内核。

内核中的函数通过相当直接了当的方式来得到动态内存。内核对于内存的分配时采用不同的方式,对于内核而言,总是立即分配内存,但是对于用户进程而言,内核总是在“不能再推迟的情况下”才分配内存。linux下的进程总是维护自己的虚拟地址空间(linux为每个用户进程 分配4DB虚拟地址空间),而虚拟内存到物理内存的实际转换的话,是采用的"页表“的机制来实现的。下面说明相关的数据结构:

在内核源码include/linux/sched.h中定义了stask_struct结构,用来标记每个进程:

struct task_struct {

...

struct mm_struct *mm, *active_mm;

...

}

进程的task_struct中的mm指向mm_struct用来维护进程管理的虚拟内存,哪些虚拟内存地址使用,而那些虚拟内存地址还没有使用,mm_struct定义如下:

structmm_struct
{

unsignedlong

start_code
,
end_code
,
end_data
;

...

struct rb_toor mm_root;

structvm_area_struct *

mmap
;

structvm_area_struct *

mmap_avl
;

..

}

所有的mm_struct都是存放在一个链表中,链表的第一个元素是init_mm元素,init_mm是初始化进程0使用的内存描述符。定义:

#define INIT_MM { \

0, \

                 0, 0, 0, \
                 0, 0, 0, 0, \
                 0, 0, 0, 0, \
                 0, \
 
 */      0, 0, 0, 0, \
                 0, \
 
 */      0, 0, 0, 0, \
                 &init_mmap, &init_mmap
}
vm_area_struct定义如下:
/*
 * This struct defines a memory VMM memory area. There is one of these
 * per VM-area/task.  A VM area is any part of the process virtual memory
 * space that has a special rule for the page-fault handlers (ie a shared
 * library, the executable area etc).
 */
struct vm_area_struct {
        struct mm_struct * vm_mm;       /* VM area parameters */
        unsigned long vm_start;
        unsigned long vm_end;

        /* linked list of VM areas per task, sorted by address */
        pgprot_t vm_page_prot;
        unsigned short vm_flags;

        /* AVL tree of VM areas per task, sorted by address */
	...

        /* For areas with inode, the list inode->i_mmap{,_shared}, for shm areas,
         * the list of attaches, otherwise unused.
         */
	...
        struct vm_operations_struct * vm_ops;
        unsigned long vm_offset;
        struct file * vm_file;
        unsigned long vm_pte;                   /* shared mem */
};

上面的vm_area_struct 中需要特别强调的是vm_file,vm_ops,vm_mm,vm_mm指向进程的mm字段, vm_file执行虚拟地址映射的文件的

file对象,vm_ops字段指向vm_operations_struct结构,该结构中 存放的是作用域线性区的方法,常见的有:open,close,nopage,

populate。

总结上面可得:

process --> task_struct --> mm(mm_struct) --> vm_area_struct list来管理进程虚拟地址空间

了解了上面的数据结构之后,开始看进程的虚拟地址空间是如何创建的?如何删除的?在这里hello的情景中,进程是由sys_execve()系统调用来

创建的,在sys_execve函数中,首先是分配一个页框(物理地址)填充进程的命令行参数,环境变量,将这个页框分配各这个进程(设置页表),

然后开始映射可执行文件的.text段,这里”映射text段“所做的工作只是分配虚拟地址,将可执行文件的一部分和它对应上,在实际的物理内存中是

不存在该“段”的内容,如果需要访问的话 ,产生"缺页异常",调用异常处理函数将可执行文件对应的部分读入到内存中,并设置相关的页表(这一步

其实才建立了虚拟地址和物理地址的映射,也就是说映射其实分为两个部分,虚拟地址 <--> 文件 虚拟地址 <-->物理地址,具体的映射部分参见

第四部分)

删除进程的地址空间使用的是exit_mm。具体的参见“深入理解Hello World程序卸载”部分。

2.保护模式下寻址


计算机中引入了”虚拟内存“的东西,好处是很大,但是还是给程序的理解带还很多的麻烦啊。我学习时主要疑问是:

1.哪些是虚拟地址?哪些是实际的物理地址?

如果开启了分页机制的话,在应用程序中使用的所有地址都是虚拟地址,将虚拟地址转换成物理地址的工作是有硬件来完成,同时使用软件来初始化

一些table,显然必须存在一些”东西“来记录实际的物理地址,这个工作是寄存器中写入物理地址的值。

2.linux为每个活动的进程分配i、一个页目录项。

至于具体的转换过程,在很多操作系统课本中讲的有。

3.hello情景分析

依旧使用上面的假设:hello程序刚刚被加载到内存中,下面需要寻址一个text段内容,由于在sys_execve中做的仅是映射,那么虚拟地址在转换

成物理地址时,所访问的页表项是空,产生缺页异常,调用缺页异常处理函数,读入该块美容,然后读取页表,cpu的地指线上就是该物理地址的值。然后得到该物理地址中的值,最终开始执行这条指令

下一步开始解决的是“映射”的实际含义?这其中设计到了文件系统的相关东西,好像雪球越来越大了,继续。。。

哎,顺便说上一句,内核庞大的代码,查找一个函数或者是数据结构的定义是在是很难,推荐使用的是google code search来完成相关搜索。

抱歉!评论已关闭.