内存与调试-内存管理(3)用户空间和内核空间

X86地址空间分布

linux物理地址空间布局:

物理地址空间的顶部以下一段空间,被PCI设备的I/O内存映射占据,它们的大小和布局由PCI规范所决定。640K~1M这段地址空间被BIOS和VGA适配器所占据。

  Linux系统在初始化时,会根据实际的物理内存的大小,为每个物理页面创建一个page对象,所有的page对象构成一个mem_map数组。

进一步,针对不同的用途,Linux内核将所有的物理页面划分到3类内存管理区中,如图,分别为ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。

  ZONE_DMA的范围是0~16M,该区域的物理页面专门供I/O设备的DMA使用。之所以需要单独管理DMA的物理页面,是因为DMA使用物理地址访问内存,不经过MMU,并且需要连续的缓冲区,所以为了能够提供物理上连续的缓冲区,必须从物理地址空间专门划分一段区域用于DMA。

  ZONE_NORMAL的范围是16M~896M,该区域的物理页面是内核能够直接使用的。

  ZONE_HIGHMEM的范围是896M~结束,该区域即为高端内存,内核不能直接使用。

linux虚拟地址

内核空间分布

在kernel image下面有16M的内核空间用于DMA操作。位于内核空间高端的128M地址主要由3部分组成,分别为vmalloc area,持久化内核映射区,临时内核映射区。

由于ZONE_NORMAL和内核线性空间存在直接映射关系,所以内核会将频繁使用的数据如kernel代码、GDT、IDT、PGD、mem_map数组等放在ZONE_NORMAL里。而将用户数据、页表(PT)等不常用数据放在ZONE_ HIGHMEM里,只在要访问这些数据时才建立映射关系(kmap())。比如,当内核要访问I/O设备存储空间时,就使用ioremap()将位于物理地址高端的mmio区内存映射到内核空间的vmalloc area中,在使用完之后便断开映射关系。 上面描述默认都是32位的机器,对于64位的机器,PAGE_OFFSET为0x0xffff880000000000,用户地址空间范围:0x0000000000000000 – 0x00007fffffffffff,内核代码地址空间:0xffffffff80000000 – 0xffffffffa0000000。

大白话:内核的线性地址 – PAGE_OFFSET = 内核的物理地址

用户空间分布

用户进程的代码区一般从虚拟地址空间的0x08048000开始,这是为了便于检查空指针。代码区之上便是数据区,未初始化数据区,堆区,栈区,以及参数、全局环境变量。

 linux虚拟地址与物理地址映射的关系

Linux将4G的线性地址空间分为2部分,0~3G为user space,3G~4G为kernel space。

  由于开启了分页机制,内核想要访问物理地址空间的话,必须先建立映射关系,然后通过虚拟地址来访问。为了能够访问所有的物理地址空间,就要将全部物理地址空间映射到1G的内核线性空间中,这显然不可能。于是,内核将0~896M的物理地址空间一对一映射到自己的线性地址空间中,这样它便可以随时访问ZONE_DMA和ZONE_NORMAL里的物理页面;此时内核剩下的128M线性地址空间不足以完全映射所有的ZONE_HIGHMEM,Linux采取了动态映射的方法,即按需的将ZONE_HIGHMEM里的物理页面映射到kernel space的最后128M线性地址空间里,使用完之后释放映射关系,以供其它物理页面映射。虽然这样存在效率的问题,但是内核毕竟可以正常的访问所有的物理地址空间了。

linux中可执行程序与虚拟地址空间的映射关系

虚拟内存区域(VMA,Virtual Memory Area)是Linux中进程虚拟地址空间中的一个段,在Windows里面叫虚拟段。当操作系统创建线程后,会在进程相应的数据结构中设置一个.text段的VMA,它在虚拟空间中的地址为0x08048000~0x08049000,它对应ELF文件中的偏移为0的.text。可以查看操作系统为运行的进程维护的信息

从上面的图可以看出,虚拟空间地址为0x08048000~0x08049000的VMA映射为elf文件中的一个段(segment),并且是按整页进行映射的。

  由于linux下的ELF可执行文件会有很多个段(section),所以如果把每个section都映射为一个VMA,那么没有一个页大小的段(section)也会被映射为一个页的VMA,这样就浪费了物理空间,由于不足会用0补充。故elf有一个装载的段(segment),与前面的段(section)不同,前面的段(section)主要用于链接,而段(segment)主要用于装载进内存。

可以看出段(segment)02包含了很多的段(section),那链接器怎样将段(section)合并到一个段(segment)中的呢?可以通过段(section)的权限来合并,如以代码段为代表的权限为可读可执行权限;以数据段和BSS段为代表的权限为可读可写的段;以只读数据为代表的权限为只读权限。

  ELF与Linux进程虚拟空间映射关系如下图所示:

  即使把多个段(section)合并到几个段(segment),每个段(segment)还是又很能产生较大的页内碎片,怎样解决这个问题呢?Unix巧妙的通过各个段(segment)接壤部分共享一个物理页来解决这个问题。

high memory 和 low memory

高内存区域和低内存区域,是针对物理地址空间来说的。

在32位系统中,运行 命令可以看到

high  total 即 high memory   ; low total 即 low memory

cat /proc/meminfo

total: used: free: shared: buffers: cached: 
Mem: 1049051136 765775872 283275264 0 158441472 376979456 
Swap: 1036115968 0 1036115968 
MemTotal: 1024464 kB 
MemFree: 276636 kB 
MemShared: 0 kB 
Buffers: 154728 kB 
Cached: 368144 kB 
... 
HighTotal: 131072 kB 
HighFree: 3064 kB 
LowTotal: 893392 kB 
LowFree: 273572 kB

在32位的linux系统上,CPU 最大能寻址是4GB内存。
内存被进一步划分成被内核直接映射的 Low memory (or Normal memory),和内核间接映射的 High memory

– The kernel itself (including its active modules, e.g. Check Point kernel modules) can only make use of LOW memory.
– User processes on the system (anything that is not kernel itself) can potentially make use of LOW and HIGH memory.
在64位系统中,因为有足够的虚拟地址空间,Low memory equals  Total memory. When running  cat /proc/meminfo command on the module, HighTotal and HighFree will always be zero:
# cat /proc/meminfo
MemTotal:      3591768 kB
MemFree:        135968 kB
Buffers:        378312 kB
Cached:        1754432 kB
SwapCached:         68 kB
Active:         965532 kB
Inactive:      2080476 kB
HighTotal:           0 kB
HighFree:            0 kB

实际上,我在现在的电脑上测试,发现没有 HighTotal 和HighFree 显示项了。

512M的内存空间划分

来自:https://stackoverflow.com/questions/4528568/how-does-the-linux-kernel-manage-less-than-1gb-physical-memory

Not all virtual (linear) addresses must be mapped to anything. If the code accesses unmapped page, the page fault is risen.

The physical page can be mapped to several virtual addresses simultaneously.

In the 4 GB virtual memory there are 2 sections: 0x0… 0xbfffffff – is process virtual memory and 0xc0000000 .. 0xffffffff is a kernel virtual memory.

  • How can the kernel map 896 MB from only 512 MB ?

It maps up to 896 MB. So, if you have only 512, there will be only 512 MB mapped.

If your physical memory is in 0x00000000 to 0x20000000, it will be mapped for direct kernel access to virtual addresses 0xC0000000 to 0xE0000000 (linear mapping).

  • What about user mode processes in this situation?

Phys memory for user processes will be mapped (not sequentially but rather random page-to-page mapping) to virtual addresses 0x0 …. 0xc0000000. This mapping will be the second mapping for pages from 0..896MB. The pages will be taken from free page lists.

  • Where are user mode processes in phys RAM?

Anywhere.

  • Every article explains only the situation, when you’ve installed 4 GB of memory and the

No. Every article explains how 4 Gb of virtual address space is mapped. The size of virtual memory is always 4 GB (for 32-bit machine without memory extensions like PAE/PSE/etc for x86)

As stated in 8.1.3. Memory Zones of the book Linux Kernel Development by Robert Love (I use third edition), there are several zones of physical memory:

  • ZONE_DMA – Contains page frames of memory below 16 MB
  • ZONE_NORMAL – Contains page frames of memory at and above 16 MB and below 896 MB
  • ZONE_HIGHMEM – Contains page frames of memory at and above 896 MB

So, if you have 512 MB, your ZONE_HIGHMEM will be empty, and ZONE_NORMAL will have 496 MB of physical memory mapped.

Also, take a look to 2.5.5.2. Final kernel Page Table when RAM size is less than 896 MBsection of the book. It is about case, when you have less memory than 896 MB.

Also, for ARM there is some description of virtual memory layout: http://www.mjmwired.net/kernel/Documentation/arm/memory.txt

The line 63 PAGE_OFFSET high_memory-1 is the direct mapped part of memory

X64地址空间分布

The 64-bit x86 virtual memory map splits the address space into two: the lower section (with the top bit set to 0) is user-space, the upper section (with the top bit set to 1) is kernel-space. (Note that x86-64 defines “canonical” “lower half” and “higher half” addresses, with a number of bits effectively limited to 48 or 56; see Wikipedia for details.)

The complete map is documented in detail in the kernel; currently it looks like

===========================================================================================
    Start addr    |   Offset   |     End addr     |  Size   | VM area description
===========================================================================================
                  |            |                  |         |
 0000000000000000 |    0       | 00007fffffffffff |  128 TB | user-space virtual memory
__________________|____________|__________________|_________|______________________________
                  |            |                  |         |
 0000800000000000 | +128    TB | ffff7fffffffffff | ~16M TB | non-canonical
__________________|____________|__________________|_________|______________________________
                  |            |                  |         |
 ffff800000000000 | -128    TB | ffffffffffffffff |  128 TB | kernel-space virtual memory
__________________|____________|__________________|_________|______________________________

实际上,

  • the kernel’s memory are those high addresses where the 17 high-order bits are 1
  • user space memory are low memory, where the high-order bits are 0

 

参考:
https://www.cnblogs.com/chengxuyuancc/archive/2013/04/17/3026920.html
https://unix.stackexchange.com/questions/509607/how-a-64-bit-process-virtual-address-space-is-divided-in-linux

https://stackoverflow.com/questions/4528568/how-does-the-linux-kernel-manage-less-than-1gb-physical-memory

https://support.symantec.com/us/en/article.tech244351.html

http://www.cnblogs.com/zszmhd/archive/2012/08/29/2661461.html、深入理解linux内核、Linux Core Kernel Commentrary、程序员的自我修养。

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments