OnTheSSH使用技巧(四)直接观看进程内存

发布时间 2023-10-14 20:57:50作者: dyf029

堆(heap)和栈(stack)是进程中的两片内存区域,这是学习编程过程中,特别是C语言这种直接操作内存的程序员必须要掌握的知识。如果能直观的看到进程运行时堆内存和栈内存的变化,相信对内存知识的掌握和程序的调试都能带来帮助。
OnTheSSH是一款SSH工具,提供了图形化的进程内存的监控功能,今天我们就使用它来分析栈和堆内存。
1、先分析栈内存
在linux中,栈内存大小一般限制在8M,可以用 ulimit -a 命令查看系统对栈内存的限制:

 编写一段C程序来测试栈内存的申请:

// test1.c
#include <stdio.h>

main()
{
  // 4M栈内存
  char cache[1024 * 1024 * 4];

  printf("%X", &cache);

  //这里scanf()用来阻止程序退出
  int a = 0;
  scanf("%d", &a);
}

cache变量是char类型数组,长度定义为4M(1024 * 1024 * 4),然后编译、运行:

 

程序打印出cache变量的地址: FFA15EA0,先记录在这里,一会儿和进程内存栈地址进行比较。

打开 OnTheSSH 软件的进程监控功能,在进程列表中找到a.out进程,然后点击顶部的“进程内存”按钮:

 在进程内存窗口中(下图),显示了a.out进程(进程id 18622)的内存实时状态,上半部分是内存页角度的状态,下半部分是进程在内存中的布局和映射。

 内存映射图中显示,a.out进程的栈内存段大小是4M,和cache变量大小相符,地址范围7fffffa15000-7fffffe17000,程序打印的cache地址是FFA15EA0,去除地址前缀,cache地址正好落在栈地址范围内:

 

2、接下来分析堆内存
C语言中堆内存要用malloc来申请,在64位系统中几乎可以申请到你想得到的任意内存大小(上限极大),比如下面的代码:

//test2.c
#include <stdio.h>
#include <malloc.h>
#include <string.h>

main()
{
  int len = 1024 * 1024 * 1024;
  //申请1G堆内存
  int *p = (int*)malloc(len);

  printf("%X", p);

  int a = 0;
  scanf("%d", &a);
}

通过malloc申请了1G内存,然后编译、运行:

再次查看OnTheSSH的内存映射:

 比对内存地址,malloc申请到的内存地址正落在这段范围:

 内存映射也“真实”反应申请到了1G内存,但情况确实如此吗,不尽然!我们用 OnTheSSH 的系统监控功能查看一下内存在系统层面的使用情况:

 

 

可以看到系统层面只使用了0.51G内存,肯定没有计算a.out进程的内存啊!怎么回事?
其实这里就碰到了Linux中的copy on write机制:进程中申请的内存在没有写入时,它只是声明存在,系统并不分配物理内存,直到这片内存被写入时才真正分配。
继续测试,修改代码,对malloc申请的堆内存进行写入操作:

//test3.c
#include <stdio.h>
#include <malloc.h>
#include <string.h>

main()
{
  int len = 1024 * 1024 * 1024;
  //申请1G堆内存
  int *p = (int*)malloc(len);
  //写内存
  memset(p, 0, len);

  int a = 0;
  scanf("%d", &a);
}

编译运行,再看系统内存监控,可以看到这时内存才真的被使用: