linux中申请内存的情况分析

一 前言内存对于系统资源来说,非常重要,内存问题可以导致系统延迟增大,系统内存泄漏,进程被kill等多种严重问题,所以分析进程的内存占用很有必要 。本文重点分析了程序中动态申请内存的情况 。
注意所有测试是 5.13.0-52内核条件下测试的,不同的内核测试环境,内存分类会有很大的不同 。
二 程序内存结构在linux 32位系统中默认虚拟的内存布局如下:

linux中申请内存的情况分析

文章插图
 
说明:
在linux中每个进程都有各自的虚拟内存空间,空间的大小和cpu的位数决定了虚拟空间的上限,比如在32位系统下,硬件可以访问的内存空间上限是4GB,这4GB的空间也不是完全可以给应用程序使用 。
整个内存空间分为两个部分,操作系统占用一部分,从地址0xC0000000到0xFFFFFFFF这1GB的空间 。剩下的从0x00000000到0xBFFFFFFF共3GB空间共3GB空间;
ELF可执行文件将整个虚拟内存空间分为多个segment;
操作系统是通过VMA 对进程的虚拟内存空间进行管理的 。
VMA 是virtual Memory Area的简称,一个简单的程序VMA展示如下:
root@ubuntu-lab:/sys/kernel/debug/tracing# cat /proc/9776/maps5655a000-5655b000 r--p 00000000 fd:00 1193979/home/miao/c-test/mm-test/a.out5655b000-5655c000 r-xp 00001000 fd:00 1193979/home/miao/c-test/mm-test/a.out5655c000-5655d000 r--p 00002000 fd:00 1193979/home/miao/c-test/mm-test/a.out5655d000-5655e000 r--p 00002000 fd:00 1193979/home/miao/c-test/mm-test/a.out5655e000-5655f000 rw-p 00003000 fd:00 1193979/home/miao/c-test/mm-test/a.out5746c000-5748e000 rw-p 00000000 00:00 0[heap]f7d83000-f7da3000 r--p 00000000 fd:00 546008/usr/lib32/libc.so.6f7da3000-f7f1f000 r-xp 00020000 fd:00 546008/usr/lib32/libc.so.6f7f1f000-f7fa4000 r--p 0019c000 fd:00 546008/usr/lib32/libc.so.6f7fa4000-f7fa5000 ---p 00221000 fd:00 546008/usr/lib32/libc.so.6f7fa5000-f7fa7000 r--p 00221000 fd:00 546008/usr/lib32/libc.so.6f7fa7000-f7fa8000 rw-p 00223000 fd:00 546008/usr/lib32/libc.so.6f7fa8000-f7fb2000 rw-p 00000000 00:00 0 f7fbc000-f7fbe000 rw-p 00000000 00:00 0 f7fbe000-f7fc2000 r--p 00000000 00:00 0[vvar]f7fc2000-f7fc4000 r-xp 00000000 00:00 0[vdso]f7fc4000-f7fc5000 r--p 00000000 fd:00 546004/usr/lib32/ld-linux.so.2f7fc5000-f7fe8000 r-xp 00001000 fd:00 546004/usr/lib32/ld-linux.so.2f7fe8000-f7ff5000 r--p 00024000 fd:00 546004/usr/lib32/ld-linux.so.2f7ff6000-f7ff8000 r--p 00031000 fd:00 546004/usr/lib32/ld-linux.so.2f7ff8000-f7ff9000 rw-p 00033000 fd:00 546004/usr/lib32/ld-linux.so.2ffe18000-ffe39000 rw-p 00000000 00:00 0[stack]说明:
  1. 第一列VMA的地址范围;(虚拟地址)
  2. 第二列VMA的权限,r标识可读,w标识可写,x标识可执行,p 标识私有,s标识共享;
  3. 第三列偏移,表示VMA对应的Segment在映像文件中的便宜;
  4. 第四列一般表示映像文件所在设备的主设备号:次设备号,这里面主设备号大多显示为fd,难道是一个原因?非文件映射的内存,比如堆和栈,则这两位显示为00:00
  5. 第五列 标识映射文件的节点号;
  6. 第六列标识映射的具体文件,可以看到除了程序文件外,还有使用的库的文件信息 。vdso 为特殊的VMA,用于和内核进行交互 。
采用的代码如下:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>int g_int = 123;static g_static_int2 = 456;static g_static_int_not_init;int main(void){int l_int = 3;int l_int2 = 4;static l_static_int =6;static l_static_int2;int * pint = (int*)malloc(sizeof(int));*pint = 12;printf("g_int:%d,tg_static_int2:%d tg_static_int_not_init:%d n",g_int,g_static_int2,g_static_int_not_init);printf("g_int:%p,tg_static_int2:%p tg_static_int_not_init:%p n",&g_int,&g_static_int2,&g_static_int_not_init);printf("l_int:%d tl_int2:%d tl_static_int:%d,tl_static_int2:%d,tpint:%dn",l_int,l_int2,l_static_int,l_static_int2,*pint);printf("l_int:%p tl_int2:%p tl_static_int:%p,tl_static_int2:%p,tpint:%pn",&l_int,&l_int2,&l_static_int,&l_static_int2,pint);while(1) {sleep(3);printf("PID:%dn",getpid());}free(pint);return 0;}多次运行可以发现我们变量的地址在其应该对应的空间内,同时发现每次堆和stack的地址是每次不同的,这也是为了安全期间,设置的随机偏移 。
顺便说下stack为主线程的栈,最大的大小默认是ulimit -s,一般为8MB,pthread_create 创建的栈大小一般为2MB,不同架构不同而且和ulimit 设置的大小有关,也可以自行更改 。
三 计算程序内存大小至于程序内存使用大小,比较简单的方法是top -p pid 直接看到如下:


推荐阅读