一、Linux内核内存布局
-
64位Linux一般使用48位来表示虚拟地址空间,45位表示物理地址。通过命令:
cat/proc/cpuinfo
。查看Linux内核位数和proc
文件系统输出系统软硬件信息如下:lh@LH_LINUX:~$ cat /proc/cpuinfo vendor_id : GenuineIntel // CPU制造商 cpu family : 6 // CPU产品代号 model : 165 // CPU属于其系列当中的哪一个代号 model name : Intel(R) Core(TM) i7-10875H CPU @ 2.30GHz // CPU属于的名称、编号、主频 stepping : 2 // CPU属于制作更新版本 cpu MHz : 2303.999 //CPU实际使用主频 cache size : 16384 KB //CPU二级缓存 physical id : 0 //单个CPU的标号 siblings : 2 //单个CPU逻辑物理核数 core id : 0 //当前物理核在其所处的CPU中编号 cpu cores : 2 //逻辑核所在CPU的物理核数 apicid : 0 //用来区分不同逻辑核的编号 bogomips : 4607.99 //系统内核启动时测算的CPU速度 clflush size : 64 //每次刷新缓存的大小单位 cache_alignment : 64 //缓存地址空间对齐单位 address sizes : 45 bits physical. 48 bits virtual // 可以访问的地址空间位数
-
通过
cat/proc/meminfo
输出系统架构内存分布情况,具体如下lh@LH_LINUX:~$ cat /proc/meminfo MemTotal: 12165668 kB //所有可用内存空间的大小 MemFree: 10151616 kB //系统还没有使用内存 MemAvailable: 10570548 kB //真正系统可用内存 Buffers: 52596 kB //专用用来给块设备做缓存的内存 Cached: 575008 kB //分配给文件缓冲区的内存 SwapCached: 0 kB //被高速缓冲缓存使用的交换空间大小 Active: 1158808 kB //使用高速缓冲存储器页面文件大小 Inactive: 345196 kB //没有经常使用的高速缓存存储器大小 Active(anon): 870720 kB //活跃的匿名内存 Inactive(anon): 12504 kB //不活跃的匿名内存 Active(file): 288088 kB //活跃的文件使用内存 Inactive(file): 332692 kB //不活跃的文件使用内存 Unevictable: 16kB //不能被释放的内存页 Mlocked: 16 kB //系统调用mlock允许程序在物理内存上锁住部分或全部地址空间 SwapTotal: 2097148 kB //交换空间总内存大小 SwapFree: 2097148 kB //交换空间空闲的内存大小 Dirty: 24 kB //等待被写回到磁盘 Writeback: 0 kB //正在被写加的大小 AnonPages: 876420 kB //未映射页的内存/映射到用户空间的非文件页表大小 Kipped: 255420 kB //映射文件内存 Shmem: 13896 kB //已经被分配的共享内存 KReclaimabie: 70884 kB //可回收的slab内存 Slab: 151976 kB //内存数据结构缓存大小 CommitLimit: 8179980 kB //系统实际可以分配内存 Committed_AS: 4649264 kB //系统当前已经分配的内存 VmallocTotal: 34359738367 kB//预留虚拟内存的总量 VmallocUsed: 27836 kB //已经被使用的虚拟内存 VmallocChunk: 0 kB //可分配的最大逻辑地址连续的虚拟内存
-
Linux内核动态内存分配通过系统接口实现
alloc_pages
/_get_free_page
:以页为单位分配vmalloc
:以字节为单位分配虚拟地址连续的内存块kmalloc
:以字节为单位分配物理地址连续的内存块,它是以slab
为中心
-
我们也可以通过
vmalloc
分配的内存将它统计输出,具体如下:
-
ARM64
架构采用48
位物理寻址方式,最大可寻找256TB
的物理地址空间。对于目前应用完全足够,不需要扩展到64
位。虚拟地址也同样最大支持48
位寻址。Linux
内核在大多数体系结构上将地址空间划为:用户空间和内核空间。- 用户空间(User space) :
0x0000_0000_0000_0000
至0x0000_FFFF_FFFF_FFFF
- 内核空间(Kernel space) :
0xFFFF_0000_0000_0000
至0xFFFF_FFFF_FFFF_FFFF
KASAN
(影子区):它是一个动态检测内存错误的工具,原理利用额外的内存标记可用内存的状态(将1/8
内存作为影子区);modules
:内核模块使用的虚拟地址空间;vmalloc
:vmalloc
函数使用的虚拟地址空间.text
:代码段.init
:模块初始化数据.data
:数据段bss
:静态内存分配段fixed
:固定映射区域PCI I/O
:PCI
设备的I/O
地址空间vmemmap
:内存的物理地址如果不连续的话,就会存在内存空洞(稀疏内存)vmemmap
就用来存放稀疏内存的page
结构体的数据的虚拟地址空间memory
:线性映射区域
- 我们可以打印出ARM64体系结构的Linux内存布局(
Linux
内核初始化完成后,整体布局稳定。通过Vexpress
平台输出即可,因为我们暂时没有环境,所以先通过源码分析打印流程。
start_kernel()
==>mm_init()
==>mem_init()
==>printk(...)
- 用户空间(User space) :
二、堆管理
-
堆是进程中主要用于动态分配变量和数据的内存区域,堆的管理对应程序员不是直接可见的。
malloc
和内核之间的经典接口是brk
系统调用,负责拓展/收缩堆。堆是一个连续的内存区域,在拓展时自下至上增长。其中mm_struct
结构,包含堆在虚拟地址空间中的起始和当前结束地址(start_brk
和brk
)。struct mm_struct{ ... unsigned long start_brk,brk,start_stack; ... }
-
brk
系统调用指定堆在虚拟地址空间中新的结束地址(如果堆将要收缩,当然可以小于当前值)。brk
系统调用动态分配,具体Linux
内核源码分析如下SYSCALL_DEFINE1(brk, unsigned long, brk){ ... }
-
Linux系统当中有两个方法可以创建堆:
brk()
是系统调用,实际是设置进程数据段的结束地址,将数据段的结束地址向高地址移动。mmap()
向操作系统申请一段虚拟地址空间(使用映射到某个文件)。当不用此空间来映射到某个文件时,这块空间称为匿名空间可以用来作为堆空间。
-
per-cpu
计数器,引入它用来加速SMP
系统上计数器工作,Linux具体内核源码如下:#ifdef CONFIG_SMP struct percpu_counter { spinlock_t lock; s64 count; #ifdef CONFIG_HOTPLUG_CPU struct list_head list; /* All percpu_counters are on a list */ #endif s32 *counters; };
- 采用
per-cpu
变量有下列好处:所需数据很可能存在于处理器的缓存中,因此可以更快速地访问。如果在多处理器系统中使用可能被所有CPU
同时访问的变量,可能会引发一些通信方面的问题,采用上述概念刚好绕过了这些问题。
- 采用