Linux内存管理-函数详解

来源:互联网 发布:lol物品数据库哪里有 编辑:程序博客网 时间:2024/05/29 18:23

 0.11核提供的内存管理函数大部分在memery.c中,我将其中的函数从功能上分为三类:内存的分配与释放、页异常处理和内存初始化。第一类主要包括单个物理页的操作和页表的操作,单个物理页的操作就是一个物理页的申请、映射和释放;页表操作主要是多个页表的释放、复制。第二类主要是处理缺页异常和写保护异常的中断处理函数。第三类就是一个函数负责内存的初始化工作。

一、内存的分配与释放

1、get_free_page():在主内存区中申请一空闲物理页。

   首先扫描mem_map[],从末端向前寻找值为0的元素,找到后将它的值置成1,并根据这个元素的在mem_map[]中的索引值得到物理页的地址,再将物理页中的4K字节清零,最后返回物理地址。

2、free_page():释放指定物理地址处的物理页。

   在验证了这个地址的有效性之后,根据它换算出对应的页地址:(addr-1M)/4K,再利用此地址值作为索引在mem_map[]中找到对应项,将其清零。

3、free_page_tables():释放从指定线性地址开始的若干个页表以及它对应的物理页。

  先判断传入的线性地址是否有效,即是否在4MB边界上,然后就算出它所占的页目录项的个数和第一个目录项的地址,接着从第一个目录项开始,对于可用的目录项(也就是p=1:存在对应的页表)就根据目录项得到页表的地址,从而释放页表中的所有记录着的物理页(free_page)并将此页表项清零,之后释放掉这个页表本身所占的物理页并将它对应的页目录项清零。直到处理完最后一个页目录项。最后刷新TLB。

4、copy_page_tables():复制指定线性地址开始的若干个页表到指定的目标线性地址处。

  这个函数是fork一个进程时候用到的,它让子进程共享父进程的物理内存。在验证了地址的边界性之后,换算出页目录项表中的源目录项起始地址和目标目录项的起始地址以及要复制的页目录项的个数。然后开始复制工作:首先申请一页内存存放新页表,然后设置对应的页目录项,再循环复制源页表中各页表项到新页表中(对于内核进程只复制前160项就行,因为内核模块不超过640K),并将各个页表项设为只读,最后根据各个页的物理地址在mem_map[]中将此页对应的元素的值加1(表示引用次数加1)。然后循环这样的复制工作直到复制完所有的页表。

5、put_page():将一物理页映射到指定的线性地址空间处。

  也就是根据线性地址找到对应的页表项,将这个页表项中的值设为这个物理页的物理地址。这个函数一般在get_free_page()后使用。在验证了地址有效性之后,先根据线性地址的高10位换算出页目录项的地址,在从页目录项中得到页表地址,再根据线性地址的中间10位得到页表项地址。最后将在这个页表项中设置它所映射的物理页地址和一些页信息。

6、get_empty_page():get_free_page()和put_page()的结合。

二、页异常处理相关函数

1、up_wp_page():取消指定页表项对应的物理页的写保护。

  这个函数是处理页写保护的情况。它首先检查这个页是否只被一个进程使用(mem_map[]中对应的元素是1),如果是直接将它改为可写,如果它被多个进程使用,则申请一个页面,然后改写页表项:更新新物理页的地址、设为可读写,接着将原物理页的引用次数减1,最后复制原物理页的内容到新物理页中后刷新TLB。

2、do_wp_page():中断处理函数:处理写保护异常

   根据线性地址得到页表项地址,并把它作为参数调用up_wp_page()。

3、write_verify():检查页面是否可写,如果不可写复制页面。

   根据给定的线性地址得到页表项,根据页表项判断对应的物理页是否可写,如果不可写,则调用un_wp_page(),完成新页面的复制。

4、try_to_share():让当前进程的指定逻辑地址共享另一个进程这个逻辑地址对应的物理页。

  当一个进程与另一个进程对应同一个可执行程序的时候,可以让一个进程共享另一个进程的物理内存。首先根据逻辑地址分别得到当前进程和目标进程的页目录项,然后根据目标进程的页目录项中得到页表从而得到页表项,然后判断这个页表项对应的物理页是否被修改过,如果没被修改过,则表示可以被共享。接着得到当前进程的要操作的页表项,并将此页表的内容设为刚才得到的目的进程页表项的内容,最后将这两个页表项都设为只读。

5、share_page():共享页面

   找到与当前进程对应同一个可执行程序的进程,然后调用try_to_share()进行页面的共享。寻找这个进程的方法是先得到当前进程的对应的可执行程序的i节点,然后扫描整个task数组,找到与当前进程i节点相同的进程。

6、do_no_page():中断处理函数:缺页处理

  缺页有两种情况,一种是动态申请内存导致的缺页,一种访问进程代码或者数据时候缺页。先根据线性地址和当前进程的起始地址得到逻辑地址,判断这个逻辑地址是否在代码段和数据段范围之外,如果在这个范围之外说明是在堆段中,则表示这是动态申请内存引起的缺页,这个时候,用get_empty_page()就可以解决问题了。如果在这个范围之内,说明是反问程序的代码或者数据引起的缺页,整个时候,需要从磁盘的可执行程序中复制相应页面到内存中:先申请一块物理内存页,接着根据逻辑地址得到可执行文件中对应的逻辑块(每块大小1KB)号,然后将连续的四个逻辑块复制到新申请的物理页中,在处理完一些无用信息(有可能读入的4个逻辑块中实际的有用信息不足4KB)之后,完成物理页到线性地址的映射。

三、内存的初始化:mem_init()

   这个函数在main初始化的时候被调用的,前面已经说过Linux对内存的管理主要是通过mem_map[]完成的,内存的初时话,主要就是对mem_map[]进行初始化:就是将1M以上的内存空间中的主内存部分在mem_map[]对应的元素置为0,其它部分置位占用状态。

    附:0.11核内存管理中还有一个文件,page.s,这个文件主要是页异常引起中断的中断处理函数,具体的就是根据异常类型调用不同的处理函数:do_no_page()、do_wp_page()。

    至此,Linux0.11核的内存管理的学习,我打算告一段落,下面我计划学习Linxu的设配管理,主要是设配驱动。

原创粉丝点击