64位Linux一般使用48位来表示虚拟地址空间,45位表示物理地址。通过命令: cat /proc/cpuinfo。查看Linux内核位数和proc文件系统输出系统软硬件信息如下:
x86架构体系内核分布情况:
一、物理内存相关
MemTotal
系统物理内存总量(单位:KB),包含已使用和未使用的所有内存。MemFree
未被使用的物理内存,即当前完全空闲的内存空间。MemAvailable
系统可实际使用的内存,不仅包含MemFree
,还计算了可回收的缓存(如Cached
、SReclaimable
等),更贴近程序实际可用内存的真实情况。Buffers
块设备(如硬盘、SSD)的缓冲区内存,用于临时存储磁盘写入数据。Cached
文件缓存内存,用于缓存从磁盘读取的文件数据,加速后续读取操作。二、交换空间(Swap)相关
SwapTotal
交换空间(虚拟内存)的总大小。SwapFree
剩余可用的交换空间。SwapCached
被交换空间缓存的内存页,即曾换出到 Swap 又被重新访问、暂时缓存的数据。三、内存活动状态分类
Active
活跃内存页,近期被频繁访问的内存(如正在运行程序的内存)。Inactive
非活跃内存页,近期未被频繁访问的内存,可能被系统回收。Active(anon) / Inactive(anon)
“匿名内存”(如进程堆、栈)的活跃 / 非活跃状态。Active(file) / Inactive(file)
与文件相关的内存(如文件缓存)的活跃 / 非活跃状态。四、其他内存统计
Unevictable
不可回收的内存页,如被锁定的内存。Mlocked
通过mlock()
系统调用锁定、禁止换出到 Swap 的内存。Zswap
压缩交换空间使用的内存(若启用 Zswap 压缩技术)。Dirty
等待写入磁盘的脏数据占用的内存。Writeback
正在写入磁盘的回写数据占用的内存。AnonPages
匿名内存页(如进程堆、栈、未映射文件的内存)。Mapped
映射文件(如通过mmap()
映射的文件)占用的内存。Shmem
共享内存(如tmpfs
挂载的临时文件系统、进程间共享内存)。Slab
内核缓存对象(如目录项、索引节点)占用的内存,包含可回收和不可回收部分。SReclaimable
Slab 中可回收的内存(如缓存的目录项、索引节点)。SUnreclaim
Slab 中不可回收的内存(如内核固定使用的结构)。
Linux内核动态内存分配通过系统接口实现:
alloc_pages/__get_free_page:以页为单位分配 vmalloc:以字节为单位分配虚拟地址连续的内存块 kmalloc:以字节为单位分配物理地址连续的内存块,它是 以 slab 为中心。
我们可以通过vmalloc分配的内存将它统计输出,具体如下:
1. 地址范围
每行开头的
0xffff...
是虚拟地址区间,格式为 起始地址 - 结束地址,表示vmalloc
分配的虚拟内存范围。例如0xffffface34000000-0xffffface34001000
,代表一段连续的虚拟地址空间。2. 数字标识(如 8192、20480 等)
这些数字是 页数量,Linux 中每页默认大小为 4KB。通过页数量可计算分配的内存大小。例如:
8192 pages
:对应8192 × 4KB = 32MB
;20480 pages
:对应20480 × 4KB = 80MB
。3. 分配用途描述
如
bpf_prog_alloc_no_stats
、gen_pool_add
、dup_task_struct
等,标识该内存分配的内核组件或功能场景。例如:
bpf_prog_alloc_no_stats
:与 eBPF(扩展 Berkeley 包过滤)程序分配相关;dup_task_struct
:与进程任务结构体复制相关。4.
pages=
明确标注分配的页数量,与前面的数字含义一致,用于直观展示内存分配的页单位数量。
5.
vmalloc NO=
分配序号,用于唯一标识每一次
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:vmalooc 函数使用的虚拟地址空间
.text:代码段
.init:模块初始化数据
.data:数据段
.bss:静态内存分配段
fixed:固定映射区域
PCI I/O:PCI 设备的 I/O 地址空间
vmemmap:内存的物理地址如果不连续的话,就会存在内 存空洞(稀疏内存)
vmemmap 就用来存放稀疏内存的 page 结构体的数据的虚拟地址空间
memory:线性映射区域
堆是进程中主要用于动态分配变量和数据的内存区域,堆的管理对应程序员不是直接可见的。malloc 和内核之间的经典接口是 brk 系统调用,负责扩展 / 收缩堆。
堆是一个连续的内存区域,在扩展时自下至上增长。其中 mm_struct 结构,包含堆在虚拟地址空间中的起始和当前结束地址 (start_brk 和 brk)。
Linux中有两个方法可以创建堆:
#include
#include
#include
int main() {
// 获取当前 brk 地址
void* original_brk = sbrk(0);
if (original_brk == (void*)-1) {
perror("sbrk");
return 1;
}
// 扩展堆,分配 1024 字节
void* alloc_addr = sbrk(1024);
if (alloc_addr == (void*)-1) {
perror("brk");
return 1;
}
printf("Allocated with brk: %p\n", alloc_addr);
// (演示收缩,实际场景中较少手动收缩堆)
if (brk(original_brk) == -1) {
perror("brk shrink");
return 1;
}
return 0;
}
#include
#include
#include
int main() {
// 使用 mmap 分配 1024 字节匿名空间
void* mmap_addr = mmap(
NULL, // 让内核决定映射地址
1024, // 映射大小(字节)
PROT_READ | PROT_WRITE, // 读写权限
MAP_PRIVATE | MAP_ANONYMOUS, // 匿名、私有映射
-1, // 不关联文件描述符
0 // 偏移量
);
if (mmap_addr == MAP_FAILED) {
perror("mmap");
return 1;
}
printf("Allocated with mmap: %p\n", mmap_addr);
// 使用完后释放内存
if (munmap(mmap_addr, 1024) == -1) {
perror("munmap");
return 1;
}
return 0;
}
per-CPU计数器,引入它来加速SMP系统上的计数器操作,Linux内核源码如下:
1. per - CPU 计数器作用
在对称多处理(SMP)系统中,多个 CPU 核心可同时执行任务,若多个 CPU 核心同时对同一计数器进行操作(如递增、递减等),会因竞争同一资源引发锁争用问题,导致性能下降。per - CPU 计数器为每个 CPU 核心分配独立计数器实例,各 CPU 核心操作自己的计数器实例,减少锁争用。比如统计系统中网络接收数据包数量,每个 CPU 核心可记录自己处理的数据包数,最后汇总得到总数,加快计数操作速度,提升系统性能。
2. 使用方法
- 定义与初始化:先定义
percpu_counter
结构体变量。通常在内核模块初始化代码或相关子系统初始化时,调用percpu_counter_init
函数初始化计数器,指定初始计数值等参数 。示例代码:#include
struct percpu_counter my_counter; void my_module_init(void) { percpu_counter_init(&my_counter, 0); // 初始值设为0 }
- 计数操作:在需要计数的地方,如某个事件发生需递增计数时,使用
percpu_counter_inc
等函数。每个 CPU 核心操作自己的本地计数器副本。示例:void some_event_occurred(void) { percpu_counter_inc(&my_counter); }
- 获取计数值:要获取计数器总值时,调用
percpu_counter_read
函数,它会汇总所有 CPU 核心的本地计数器值。示例:s64 total_count = percpu_counter_read(&my_counter);
- 销毁计数器:在内核模块退出或相关子系统清理时,调用
percpu_counter_destroy
函数释放计数器占用资源。示例:void my_module_exit(void) { percpu_counter_destroy(&my_counter); }
https://github.com/0voice