Linux | 堆内存管理

news/2025/3/15 15:24:43/文章来源:https://www.cnblogs.com/cilinmengye/p/18773723

image

from pixiv

进程的地址空间

  • jyy 进程的地址空间
  • Linux 堆内存管理深入分析

如何查看Linux进程的地址空间?

答:

  • pmap
  • /proc/$PID/maps

/proc文件系统
动态内核信息: /proc 是一个虚拟文件系统,主要提供内核和正在运行的进程的信息。它不是存储在磁盘上的真实文件,而是在运行时动态生成的。

  • 进程信息: 每个正在运行的进程在 /proc 中都有一个以其 PID 命名的目录,里面包含了该进程的状态、内存、文件描述符等详细信息。
  • 系统参数: /proc 中包含许多系统运行时参数,例如 /proc/cpuinfo(CPU 信息)、/proc/meminfo(内存信息)、/proc/uptime(系统运行时间)等。

pmap 是一个用于显示进程内存映射情况的工具,pmap 的实现主要依赖于 Linux 内核提供的/proc 文件系统包括读取读取/proc/<pid>/maps读取 /proc/<pid>/smaps(包含更详细的内存统计,该文件提供了每个内存区域的内存使用情况,包括私有内存、共享内存、脏页、换页信息等。)

pmap $PID

输出案例

33498:   ./test
000059cef6580000      4K r---- test
000059cef6581000      4K r-x-- test
000059cef6582000      4K r---- test
000059cef6583000      4K r---- test
000059cef6584000      4K rw--- test
000059cf32a73000    132K rw---   [ anon ]
0000734aa0e00000 102404K rw---   [ anon ]
0000734aa7400000    160K r---- libc.so.6
0000734aa7428000   1620K r-x-- libc.so.6
0000734aa75bd000    352K r---- libc.so.6
0000734aa7615000      4K ----- libc.so.6
0000734aa7616000     16K r---- libc.so.6
0000734aa761a000      8K rw--- libc.so.6
0000734aa761c000     52K rw---   [ anon ]
0000734aa7652000     12K rw---   [ anon ]
0000734aa7669000      8K rw---   [ anon ]
0000734aa766b000      8K r---- ld-linux-x86-64.so.2
0000734aa766d000    168K r-x-- ld-linux-x86-64.so.2
0000734aa7697000     44K r---- ld-linux-x86-64.so.2
0000734aa76a3000      8K r---- ld-linux-x86-64.so.2
0000734aa76a5000      8K rw--- ld-linux-x86-64.so.2
00007ffdb8f1e000    136K rw---   [ stack ]
00007ffdb8ffa000     16K r----   [ anon ]
00007ffdb8ffe000      8K r-x--   [ anon ]
ffffffffff600000      4K --x--   [ anon ]total           105188K

当然可以直接cat /proc/$PID/maps,会看到一些更原始的输出:

59cef6580000-59cef6581000 r--p 00000000 08:03 1835085                    /home/cilinmengye/tmp/test
59cef6581000-59cef6582000 r-xp 00001000 08:03 1835085                    /home/cilinmengye/tmp/test
59cef6582000-59cef6583000 r--p 00002000 08:03 1835085                    /home/cilinmengye/tmp/test
59cef6583000-59cef6584000 r--p 00002000 08:03 1835085                    /home/cilinmengye/tmp/test
59cef6584000-59cef6585000 rw-p 00003000 08:03 1835085                    /home/cilinmengye/tmp/test
59cf32a73000-59cf32a94000 rw-p 00000000 00:00 0                          [heap]
734aa0e00000-734aa7201000 rw-p 00000000 00:00 0 
734aa7400000-734aa7428000 r--p 00000000 08:03 395403                     /usr/lib/x86_64-linux-gnu/libc.so.6
734aa7428000-734aa75bd000 r-xp 00028000 08:03 395403                     /usr/lib/x86_64-linux-gnu/libc.so.6
734aa75bd000-734aa7615000 r--p 001bd000 08:03 395403                     /usr/lib/x86_64-linux-gnu/libc.so.6
734aa7615000-734aa7616000 ---p 00215000 08:03 395403                     /usr/lib/x86_64-linux-gnu/libc.so.6
734aa7616000-734aa761a000 r--p 00215000 08:03 395403                     /usr/lib/x86_64-linux-gnu/libc.so.6
734aa761a000-734aa761c000 rw-p 00219000 08:03 395403                     /usr/lib/x86_64-linux-gnu/libc.so.6
734aa761c000-734aa7629000 rw-p 00000000 00:00 0 
734aa7652000-734aa7655000 rw-p 00000000 00:00 0 
734aa7669000-734aa766b000 rw-p 00000000 00:00 0 
734aa766b000-734aa766d000 r--p 00000000 08:03 395381                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
734aa766d000-734aa7697000 r-xp 00002000 08:03 395381                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
734aa7697000-734aa76a2000 r--p 0002c000 08:03 395381                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
734aa76a3000-734aa76a5000 r--p 00037000 08:03 395381                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
734aa76a5000-734aa76a7000 rw-p 00039000 08:03 395381                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ffdb8f1e000-7ffdb8f40000 rw-p 00000000 00:00 0                          [stack]
7ffdb8ffa000-7ffdb8ffe000 r--p 00000000 00:00 0                          [vvar]
7ffdb8ffe000-7ffdb9000000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]

每列从左到右的含义为:

  • 地址范围
  • 权限标识:权限部分有四个字符,典型的形式如 r--p、r-xp、rw-p:
    • r:可读(readable)。
    • w:可写(writable)。
    • x:可执行(executable)。
    • -:相应的权限缺失。
    • p 表示私有(private,即写时复制),s 表示共享。
  • 偏移量:紧接在权限后面的字段(例如 00000000、00001000)表示该段内存在对应映射文件中的偏移量(以 16 进制计)。
    比如59cef6580000-59cef6581000这段虚拟内存空间的内容来源于(或者说对应于映射文件)``/home/cilinmengye/tmp/test+偏移量00000000`开始
  • 设备号:类似 08:03 的字段表示设备的主设备号和次设备号。
  • inode 号:例如 1835094 表示文件系统中该映射文件的 inode 编号。inode 为 0 表示这段映射不是由文件提供的(如匿名映射、堆、栈)。
  • 映射文件或标签:
    • 如果是某个文件(如 /home/cilinmengye/tmp/test2、/usr/lib/x86_64-linux-gnu/libc.so.6 等),说明该内存区域映射自该文件。
    • [heap]、[stack]、[vdso]、[vvar]、[vsyscall] 等标识显示特殊用途的内存区域。
    • [ anon ] 表示匿名映射,即不是直接从磁盘文件映射而来的内存区域。

如何管理Linux进制的地址空间

malloc()/free()库函数, 它们的作用是在用户程序的堆区中申请/释放一块内存区域. 堆区的使用情况是由libc来进行管理的, 但堆区的大小却需要通过系统调用向操作系统提出更改.

调整堆区大小是通过sbrk()库函数来实现的, 它的原型是:

void* sbrk(intptr_t increment);

用于将用户程序的program break增长increment字节, 其中increment可为负数.

所谓program break, 就是用户程序的数据段(data segment)结束的位置. 我们知道可执行文件里面有代码段和数据段, 链接的时候ld会默认添加一个名为_end的符号, 来指示程序的数据段结束的位置. 用户程序开始运行的时候, program break会位于_end所指示的位置, 意味着此时堆区的大小为0.

malloc()被第一次调用的时候, 会通过sbrk(0)来查询用户程序当前program break的位置, 之后就可以通过后续的sbrk()调用来动态调整用户程序program break的位置了.

当前program break和和其初始值之间的区间就可以作为用户程序的堆区, 由malloc()/free()进行管理. 注意用户程序不应该直接使用sbrk(), 否则将会扰乱malloc()/free()对堆区的管理记录.

mmap

现代 libc(例如 glibc)的 malloc 在分配大块内存时,会调用mmap,而不是依赖于 sbrk 来扩展堆。此时分配到的内存区域会显示为匿名映射([ anon ]),而不会标记为 “[ heap ]”, 此时分配的内存空间在Memory Map Segment

image

在状态机(进程)状态上增加/删除/修改一段可访问的内存:

image

实验分析

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main() {printf("Press Enter to allocate memory...\n");getchar();void *heap_mem = malloc(100 * 1024 * 1024); // 分配 100MB 内存if (!heap_mem) {perror("malloc failed");return 1;}printf("Allocated 100MB on heap. Press Enter to exit...\n");getchar();free(heap_mem);return 0;
}

运行上述代码,使用strace和pmap分析在malloc之前和之后进程的地址空间变化:

运行malloc之前:

4414:   ./test
0000564aa69b2000      4K r---- test
0000564aa69b3000      4K r-x-- test
0000564aa69b4000      4K r---- test
0000564aa69b5000      4K r---- test
0000564aa69b6000      4K rw--- test
0000564aaa120000    132K rw---   [ anon ]
000076a64e200000    160K r---- libc.so.6
000076a64e228000   1620K r-x-- libc.so.6
000076a64e3bd000    352K r---- libc.so.6
000076a64e415000      4K ----- libc.so.6
000076a64e416000     16K r---- libc.so.6
000076a64e41a000      8K rw--- libc.so.6
000076a64e41c000     52K rw---   [ anon ]
000076a64e4dd000     12K rw---   [ anon ]
000076a64e4f4000      8K rw---   [ anon ]
000076a64e4f6000      8K r---- ld-linux-x86-64.so.2
000076a64e4f8000    168K r-x-- ld-linux-x86-64.so.2
000076a64e522000     44K r---- ld-linux-x86-64.so.2
000076a64e52e000      8K r---- ld-linux-x86-64.so.2
000076a64e530000      8K rw--- ld-linux-x86-64.so.2
00007ffe93cac000    136K rw---   [ stack ]
00007ffe93dc1000     16K r----   [ anon ]
00007ffe93dc5000      8K r-x--   [ anon ]
ffffffffff600000      4K --x--   [ anon ]total             2784K

可以看到初始给我们分配了132K的堆,000076a64e200000~000076a64e530000为Memory Map Segment

堆和.bss之间是有空隙的, 可见他们的地址分别为0000564aaa120000, 0000564aa69b6000


运行malloc之后:

# strace 输出
mmap(NULL, 104861696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7c0f54e00000
...
munmap(0x7c0f54e00000, 104861696)       = 0
exit_group(0)                           = ?# pmap输出
0000564aa69b2000      4K r---- test
0000564aa69b3000      4K r-x-- test
0000564aa69b4000      4K r---- test
0000564aa69b5000      4K r---- test
0000564aa69b6000      4K rw--- test
0000564aaa120000    132K rw---   [ anon ]
000076a647c00000 102404K rw---   [ anon ]
000076a64e200000    160K r---- libc.so.6
000076a64e228000   1620K r-x-- libc.so.6
000076a64e3bd000    352K r---- libc.so.6
000076a64e415000      4K ----- libc.so.6
000076a64e416000     16K r---- libc.so.6
000076a64e41a000      8K rw--- libc.so.6
000076a64e41c000     52K rw---   [ anon ]
000076a64e4dd000     12K rw---   [ anon ]
000076a64e4f4000      8K rw---   [ anon ]
000076a64e4f6000      8K r---- ld-linux-x86-64.so.2
000076a64e4f8000    168K r-x-- ld-linux-x86-64.so.2
000076a64e522000     44K r---- ld-linux-x86-64.so.2
000076a64e52e000      8K r---- ld-linux-x86-64.so.2
000076a64e530000      8K rw--- ld-linux-x86-64.so.2
00007ffe93cac000    136K rw---   [ stack ]
00007ffe93dc1000     16K r----   [ anon ]
00007ffe93dc5000      8K r-x--   [ anon ]
ffffffffff600000      4K --x--   [ anon ]total           105188K

注意:malloc 在分配大块内存时,会调用mmap,且分配的内存空间在Memory Map Segment。正如上述000076a647c00000 102404K rw--- [ anon ]

子进程的地址空间在哪?

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>void* threadFunc(void *arg)
{printf("Before malloc in thread 1\n");getchar();char* addr = (char*)malloc(1000);printf("After malloc and before free in thread 1\n");getchar();free(addr);printf("After free in thread 1\n");getchar();
}int main()
{pthread_t t1;void *s;int ret;char* addr;printf("Welcome to per thread arena example: %d\n", getpid());printf("Before malloc in main thread\n");getchar();addr = (char*) malloc(1000);printf("After malloc and before free in main thread\n");getchar();free(addr);printf("After free in main thread\n");getchar();ret = pthread_create(&t1, NULL, threadFunc, NULL);if (ret){printf("Thread creation error\n");return -1;}ret = pthread_join(t1, &s);if (ret){printf("THread join error\n");return -1;}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/899221.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用Wled控制RGB LED

要装饰您的照明设置,WS2812B RGB led是最好的选择之一。为了控制这些可寻址的led,我们需要一个像ESP32这样的微控制器,还必须上传代码。编写代码来控制可寻址led并不难,但如果你只是想在客厅或办公室添加一些环境照明,并通过智能手机来管理它呢?目前最好的选择,毫无疑问…

将树莓派Pico变成带有触摸屏界面的USB-HID设备

将您的树莓派Pico变成带有触摸屏界面的USB-HID设备!在这个项目中,我们将在Arduino IDE中设置编程环境,为3.5”Pico Touch LCD屏蔽配置库,测试基本功能,创建简单的按钮(用于复制和粘贴),显示自定义图标,甚至使用Windows环境变量来启动程序。设置编程环境 安装Arduino IDE …

20250315

1. 20号胶 3-5的细分

语音交友app源码,优化白屏降低用户负面情绪

语音交友app源码,优化白屏降低用户负面情绪目标: 缩小打包体积,优化白屏时间路由懒加载const Home = () => import(/* webpackChunkName: "home" */"@/views/home/index.vue"); const MetricGroup = () => import(/* webpackChunkName: "met…

Visio绘制时间轴安排图的方法

本文介绍基于Visio软件绘制时间轴、日程安排图、时间进度图等的方法~本文介绍基于Visio软件绘制时间轴、日程安排图、时间进度图等的方法。在很多学习、工作场合中,我们往往需要绘制如下所示的一些带有具体时间进度的日程安排、工作流程、项目进展等可视化图表。而基于Visio软…

华为---MUX VLAN简介及示例配置

https://blog.csdn.net/lehe99/article/details/1426209886.4 配置及解析 system-view [Huawei]sysname R [R]interface GigabitEthernet 0/0/0 [R-GigabitEthernet0/0/0]ip address 192.168.11.254 24 [R]interface LoopBack 11 [R-LoopBack11]ip address 11.11.11.11 32 syst…

SQL Server 列存储HTAP介绍

本篇文章是我线下活动分享的一个活动PPT,现在分享在我的个人博客中

基于QWidget打造的进度条控件(等待控件、加载控件)

效果图功能支持设置加载进度条颜色 支持设置中间显示文字、文字颜色、文字大小 基于QWidget开发, 开箱即用。全部代码 CircleWaitingWidget.h #pragma once #include <QWidget>/// /// 环状等待控件。 /// class CircleWaitingWidget : public QWidget {Q_OBJECTpubli…

完全开源的SQL2API低代码工具SQLREST

在现代 Web 应用中,API(应用程序编程接口)是连接前端与后端业务逻辑的重要桥梁。传统的 API 开发方式通常需要手动编写大量代码,尤其是当需要与数据库交互时,开发效率往往较低。 一、什么是 SQLREST SQLREST是一款完全开源的SQL2API低代码工具,帮助用户同步编写SQL或简单…

【晶体】范得华力,氢键

晶体的分类: 1、 金属晶体,比如铜,2、离子晶体,比如NaCl晶体,盐,3、分子晶体,比如:干冰 4、共价晶体,比如说SiO2 分子晶体,是由分子间范得华力吸引形成的。 什么是 范得华力? 分子的部分区域是正电,部分区域是负电。分子与分子之间,正负吸引的力,就是范得华…

JDK8-日历类--java进阶day07

JDK7和JDK8之间的时间API比较1.日历类1.LocalDateTime LocalDateTime最为齐全,只要掌握这个类,另外两个都是一样的now方法获取到此刻时间,of方法设置想要的时间如下图2.LocalDateTime与LocalDate和LocalTime之间的转换.3.get方法获取时间使用get,后面加上自己想要获取的时间…

ASE50N25-ASEMI光伏逆变器专用ASE50N25

ASE50N25-ASEMI光伏逆变器专用ASE50N25编辑:LL ASE50N25-ASEMI光伏逆变器专用ASE50N25 型号:ASE50N25 品牌:ASEMI 封装:TO-220F 最大漏源电流:50A 漏源击穿电压:250V 批号:最新 RDS(ON)Max:45mΩ 引脚数量:3 沟道类型:N沟道MOS管 封装尺寸:如图 特性:MOS管、N沟…