Linux 上 C 程序的内存布局
在仔细研究这个问题之前,我认为 C 程序在内存中只有代码段,堆和栈三部分构成。前几天面试被问到了这个问题,才发现自己的印象是不完全的。
在本文中通过解析析一个 C 程序中变量和函数的地址来分析 C 程序在内存中的布局。
首先简单介绍一下Linux上C程序的内存分布。
一般情况下从低地址到高地址分布着:
- 程序代码段及只读数据段
- 程序代码,以及字符串常量等都存储在这里
- 可读可写数据段
- 全局变量,静态变量存储在这里
- 数据堆
- 程序中动态分配的内存在这一块
- 共享库
- 程序加载的共享库加载在这个地方
- 数据栈
- 局部变量,函数参数,函数返回地址等存储在这里
- 内核地址
- 操作系统内核内存空间
C语言代码如下(var_memory.c)
#include <stdio.h>
#include <stdlib.h> #include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <argp.h> int global = ;
static int static_global = ; int main()
{
char *string_literal = "hello";
static int static_local = ;
char local_array[] = {'a', 'b', 'c'};
int *malloced = (int *)malloc(*sizeof(int)); pid_t pid = getpid();
printf("pid: %ld\n", (long)pid); // string_literaling literal and function in this module are loaded
// into readonly and excutable segment
printf("%.8p: addr of main\n", &main);
printf("%.8p: addr of local string_literal literal\n", string_literal);
puts(""); // static and global variables are allocated in data segment
printf("%.8p: addr of global int\n", &global);
printf("%.8p: addr of global static int\n", &static_global);
printf("%.8p: addr of local static int\n", &static_local);
puts(""); // allocated in heap
printf("%.8p: addr of local malloc\n", malloced);
puts(""); // errno is from a library
printf("%.8p: addr of errno\n", &errno);
puts(""); // printf is from a library
printf("%.8p: addr of printf\n", &printf);
puts(""); printf("%.8p: addr of argp_program_verstatic_localon_hook\n", & argp_program_version_hook);
puts(""); // local variable in stack
printf("%.8p: addr of local pid\n", &pid);
printf("%.8p: addr of local array\n", local_array);
puts(""); scanf("%s", local_array);
return ;
}
在linux命令行
make var_memory.c
生成var_memory可执行文件,运行
./var_memory
输出如下:
pid:
0x004b26dd: addr of main
0x004b29d0: addr of local string_literal literal 0x004b4008: addr of global int
0x004b400c: addr of global static int
0x004b4010: addr of local static int 0x00d30160: addr of local malloc 0xb7db86b4: addr of errno 0xb7e0b1c0: addr of printf 0xb7f8f798: addr of argp_program_verstatic_localon_hook 0xbf85518c: addr of local pid
0xbf855198: addr of local array
打开另外一个终端窗口,根据上面输出的pid,运行命令
pmap 4760 # 进程id以上面输出为准
输出如下
: ./var_memory
004b2000 4K r-x-- var_memory
004b3000 4K r---- var_memory
004b4000 4K rw--- var_memory
00d30000 136K rw--- [ anon ]
b7db8000 8K rw--- [ anon ]
b7dba000 1852K r-x-- libc-2.26.so
b7f89000 4K ----- libc-2.26.so
b7f8a000 8K r---- libc-2.26.so
b7f8c000 4K rw--- libc-2.26.so
b7f8d000 12K rw--- [ anon ]
b7fbb000 8K rw--- [ anon ]
b7fbd000 12K r---- [ anon ]
b7fc0000 8K r-x-- [ anon ]
b7fc2000 148K r-x-- ld-2.26.so
b7fe7000 4K r---- ld-2.26.so
b7fe8000 4K rw--- ld-2.26.so
bf836000 132K rw--- [ stack ]
total 2352K
从运行C语言程序与pmap的输出可以看出。
- main函数(0x004b26dd)和字符串常量(0x004b29d0)被分配在了内存区域
- 004b2000 4K r-x-- var_memory,其中rx代表只读和可执行
- 全局变量global(0x004b4008),全局静态变量 static_global(0x004b400c),局部静态变量(0x004b4010)被分配在了数据段
- 004b4000 4K rw--- var_memory,其中rw代表可以读和写
- 使用malloc分配的空间(0x00d30160)处在堆(heap)里
- 00d30000 136K rw--- [ anon ]
- 共享库提供的全局变量errno(0xb7db86b4),被分配在共享库数据段里
- b7db8000 8K rw--- [ anon ]
- 共享库提供的函数printf(0xb7e0b1c0),被分配在共享库代码段里
- b7dba000 1852K r-x-- libc-2.26.so
- 局部变量(0xbf85518c,0xbf855198)被分配在了栈上
- bf836000 132K rw--- [ stack ]
希望这篇文章,对同样困惑于C程序在内存中布局的你有所帮助。
Linux 上 C 程序的内存布局的更多相关文章
- Linux下C程序的内存布局
参考下列书籍中的对应章节: <Linux高级程序设计(第3版)>第3章Linux进程存储管理.相关视频:一.二. <C专家编程>第6章 运动的诗章:运行时数据结构. <U ...
- Anatomy of a Program in Memory.剖析程序的内存布局
原文标题:Anatomy of a Program in Memory 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精彩文章翻译 ...
- linux上应用程序的执行机制
linux上应用程序的执行机制 执行文件是如何在shell中被"执行"的.本文中尽可能少用一些源码,免得太过于无 聊,主要讲清这个过程,感兴趣的同学可以去查看相应的源码了解更多的信 ...
- C语言程序的内存布局
C语言程序的内存布局 一:C语言程序的存储区域 C语言编写的程序经过编绎-链接后,将形成一个统一的文件,它由几个部分组成,在程序运行时又会产生几个其他部分,各个部分代表了不同的存储区域: 1.代码段( ...
- Linux下C程序的内存映像
2.Linux下C程序的内存映像 2.1. 代码段.只读数据段(1)对应着程序中的代码(函数),代码段在Linux中又叫文本段(.text)(2)只读数据段就是在程序运行期间只能读不能写的数据,con ...
- Linux上java程序的jar包启动通用脚本(稳定用过)
Linux上java程序的jar包启动通用脚本如下: #! /bin/sh export LANG="zh_CN.GBK" SERVICE_NAME=` .sh` SCRIPT_N ...
- ESP32应用程序的内存布局
应用程序内存布局 ESP32芯片具有灵活的内存映射功能.本节介绍ESP-IDF在默认情况下如何使用这些功能. ESP-IDF中的应用程序代码可以放置在以下内存区域之一中. IRAM(指令RAM) ES ...
- Linux 上如何清除 RAM 内存高速缓存,缓存和交换空间
像任何其他的操作系统一样,GNU / Linux已经有效地实施了内存管理甚至更多.但是,如果有任何进程正在蚕食你的内存,你要清除它,Linux提供了一个方法来刷新或清除RAM缓存. 在Linux中如何 ...
- 分析Linux上的程序依赖
ldd [path_to_exe] ldd通过调用动态链接器来获取可执行程序的依赖库,但是并不推荐在未知的可执行程序上执行业ldd来获取其依赖库,因为部分版本的ldd会直接通过调用该可执行程序来获取其 ...
随机推荐
- [SinGuLaRiTy] 复习模板-高精度模板
[SinGuLaRiTy-1042] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 结构体封装 //高精度运算 注意%I64d与%lld # ...
- 商品鼠标移过去hover效果---图片放大1.1倍
.home-standard-layout .middle-goods-list ul li:hover{ box-shadow: 0 0 10px gray;} .home-standard-lay ...
- 2017-06-30(ps pstree top kill w killall pkill)
ps(查看系统下所有进程) -a 显示一个终端的所有进程,除了会话引线 -u 显示进程的归属用户以及内存的使用情况 -x 显示没有控制终端的进程 -l 长格式显示,更加详细的信息 -e 显示所有的进程 ...
- android Fragment的数据传递
Bundle传递参数 Fragment1 fragment1 = new Fragment1();Bundle bundle = new Bundle();bundle.putString(" ...
- 完美解决打开github速度慢的问题
摘抄自知乎. 修改hosts(HOSTS文件路径:C:\Windows\System32\drivers\etc\hosts) 1.打开Dns检测|Dns查询 - 站长工具 2.在检测输入栏中输入ht ...
- javascript 中的console.log有什么作用啊?
相比alert他的优点是: 他能看到结构话的东西,如果是alert,淡出一个对象就是[object object],但是console能看到对象的内容. console不会打断你页面的操作,如果用al ...
- nodejs爬虫笔记(二)---代理设置
node爬虫代理设置 最近想爬取YouTube上面的视频信息,利用nodejs爬虫笔记(一)的方法,代码和错误如下 var request = require('request'); var chee ...
- MySQL数据库学习笔记----MySQL多表查询之外键、表连接、子查询、索引
本章主要内容: 一.外键 二.表连接 三.子查询 四.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复 ...
- MySQL--如何快速对比数据
在MySQL运维中,研发同事想对比下两个不同实例上的数据并找出差异,除主键外还需要对比每一个字段,如何做呢? 第一种方案,写程序将两个实例上的每一行数据取出来进行对比,理论可行,但是对比时间较长. 第 ...
- 一个最简单的cell按钮点击回调
在cell.h定义 @property(nonatomic,strong)void(^pushType)(NSInteger); 在cell.m按钮点击时 _pushType(1):(举例) ...