linux内存学习(四)——概念分析

 

内容来源:网络

l         Linux最常见的可执行文件格式为elf(Executable and Linkable Format)。在elf格式的可执行代码中,ld总是从0x8000000开始安排程序的“代码段”,对每个程序都是这样。至于程序执行时在物理内存中的实际地址,则由内核为其建立内存映射时临时分配,具体地址取决于当时所分配的物理内存页面。

用户可以将编译将到的可执行文件,用”objdump –d  可执行文件” 对其进行反汇编。查看代码对应的虚拟地址。

l         虚拟内存、内核空间和用户空间

Linux虚拟内存的大小为2^32(在32位的x86机器上),内核将这4G字节的空间分为两部分。最高的1G字节(从虚地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间”。而较低的3G字节(从虚地址0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间”。因为每个进程可以通过系统调用进入内核,因此,Linux内核空间由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟地址空间(也叫虚拟内存)。

每个进程有各自的私有用户空间(0~3G),这个空间对系统中的其他进程是不可见的。最高的1GB内核空间则为所有进程以及内核所共享。另外,进程的“用户空间”也叫“地址空间”,在后面的叙述中,我们对这两个术语不再区分。

用户空间不是进程共享的,而是进程隔离的。每个进程最大都可以有3GB的用户空间。一个进程对其中一个地址的访问,与其它进程对于同一地址的访问绝不冲突。比如,一个进程从其用户空间的地址0x1234ABCD处可以读出整数8,而另外一个进程从其用户空间的地址0x1234ABCD处可以读出整数20,这取决于进程自身的逻辑。

任意一个时刻,在一个CPU上只有一个进程在运行。所以对于此CPU来讲,在这一时刻,整个系统只存在一个4GB的虚拟地址空间,这个虚拟地址空间是面向此进程的。当进程发生切换的时候,虚拟地址空间也随着切换。由此可以看出,每个进程都有自己的虚拟地址空间,只有此进程运行的时候,其虚拟地址空间才被运行它的CPU所知。在其它时刻,其虚拟地址空间对于CPU来说,是不可知的。所以尽管每个进程都可以有4 GB的虚拟地址空间,但在CPU眼中,只有一个虚拟地址空间存在。虚拟地址空间的变化,随着进程切换而变化。

从上面我们知道,一个程序编译连接后形成的地址空间是一个虚拟地址空间,但是程序最终还是要运行在物理内存中。因此,应用程序所给出的任何虚地址最终必须被转化为物理地址,所以,虚拟地址空间必须被映射到物理内存空间中,这个映射关系需要通过硬件体系结构所规定的数据结构来建立。这就是我们所说的段描述符表和页表,Linux主要通过页表来进行映射。

于是,我们得出一个结论,如果给出的页表不同,那么CPU将某一虚拟地址空间中的地址转化成的物理地址就会不同。所以我们为每一个进程都建立其页表,将每个进程的虚拟地址空间根据自己的需要映射到物理地址空间上。既然某一时刻在某一CPU上只能有一个进程在运行,那么当进程发生切换的时候,将页表也更换为相应进程的页表,这就可以实现每个进程都有自己的虚拟地址空间而互不影响。所以,在任意时刻,对于一个CPU来说,只需要有当前进程的页表,就可以实现其虚拟地址到物理地址的转化。

l         内存管理

首先介绍几个概念:

phys,在linux内核中,一般用这个字符代表实际的物理地址。

pfn, 在linux内核中,一般用这个字符代表实际的物理地址的页桢号。它是物理地址除以PAGE_SIZE后得到的。

page,描述pfn的状态信息结构,内核通过 page管理物理内存。一个pfn对应一个page

       mem_map[]:存储page数组,它描述了系统支持的所有物理内存。如果page的大小为4K,那么对于32位的系统来说,该数组的大小为2^20。mem_map[pfn]对应的就是pfn相应的page。

       virt,代表内核的虚拟地址。

       vma,描述一块虚拟内存区的,其中包括了与物理地址的映射关系,虚拟地址的管理方法,权限等。

       mm,结构体为mm_strcut, 用来描述一个进程的虚拟地址空间。被用来管理进程地址空间。即用户空间。它是vma的一个更高层次的结构。它包括了对vma的管理,进程程序段的划分等。进程就是通过mm_struct结构体来访问pgd的。

       pgd,pud,pmd,pte,这是linux的四级页表结构。其中,pgd为顶级结构。这四个结构是用来完成虚拟地址管理的(大小分级)。一般在32位的CPU中,pgd=pud=pmd=2^10,即有1K个pte。每个pte又有2^10个表项,每个表项大小为PAGE_SIZE,一般为2^12,即4K大小。总结一下,每个pte有1K*4K=4M,pgd=1K*4M=4G。

 

 在内核空间中,kmalloc分配的空间往往被称为内核逻辑地址(必须与物理地址的映射是线性的和一一对应的),vmalloc分配的空间被称为内核虚拟地址(不必和物理地址线性对应)。

  所有的内核逻辑地址都是内核虚拟地址,但是许多内核虚拟地址不是逻辑地址。

  内核逻辑地址通常保存在unsigned long或者void*类型的变量中;内核虚拟地址通常保存在指针变量中。

  物理内存中,低端内存存在于内核逻辑地址上,且与内核逻辑地址线性对应;而高端内存是不存在内核逻辑地址上的,只处于内核虚拟地址之上的。所有的物理内存都是通过page结构来描述的。

  page结构中有个void *virtual域,表示如果页面被映射,则指向页的内核虚拟地址,如果未被映射则为NULL。低端内存页面总是被映射,而高端内存通常不被映射。

  page结构和内核地址的转换

  struct page *virt_to_page(void *kaddr); /* 内核逻辑地址->page结构指针 */

  struct page *pfn_to_page(int pfn); /* 页帧号->page结构指针,应在传递前用pfn_valid检查页帧号的合法性 */

  void *page_address(struct page *page); /* page结构指针-> 内核虚拟地址,大多数情况用kmap替代 */

  #include <linux/highmem.h>

  void *kmap(struct page *page);  /* page结构指针-> 内核虚拟地址 ,不同于page_address若是高端内存,则会创建映射,不要持有映射过长时间。会休眠*/

 

  void kunmap(struct page *page); /* 释放kmap创建的映射 */

  #include <linux/highmem.h>

  #include <asm/kmap_types.h>

  void *kmap_atomic(struct page *page, enum km_type type); /* kmap的原子版本,type指定使用的槽 */

  void kunmap_atomic(void *addr, enum km_type type);

  The only slots that make sense for drivers are KM_USER0 and KM_USER1 (for code running directly from a call from user space), and KM_IRQ0 and KM_IRQ1 (for interrupt handlers).

  前面提到了每个进程都各自维护一个页表,来实现内存映射关系。2.6内核中删除了对页表直接操作的需求。

  虚拟内存区(VMA)用于管理进程地址空间中不同区域的内核数据结构。查看/proc/<pid>/maps文件就能了解进程的内存区域。 /proc/self是一个特殊的文件,它始终指向当前进程。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值