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会直接通过调用该可执行程序来获取其 ...
随机推荐
- 使用Flink的SavePoint功能
Flink通过SavePoint功能可以做到程序升级后,继续从升级前的那个点开始执行计算,保证数据不中断. Flink中CheckPoint用于保存状态,是自动执行的,SavePoint是指向Chec ...
- 【开发技术】JAutodoc使用指南
JAutodoc使用指南 下载地址:http://sourceforge.net/projects/jautodoc/?source=directory 使用方法:http://wenku.baidu ...
- Java Draw
简单绘画 直线 矩形 圆 根据矩阵画图 package com.zhoudm; import java.awt.*; import javax.swing.*; public class Draw e ...
- Spring学习之路二——概念上理解Spring
一.概念. Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Develop ...
- Python+Selenium安装及环境配置
一.Python安装 Window系统下,python的安装很简单.访问python.org/download,下载最新版本,安装过程与其他windows软件类似.记得下载后设置path环境变量,然后 ...
- Linux - ubuntu 16 打开SSH服务
ubuntu 16 打开SSH服务 1.查看是否启动进程 roott@jiqing-virtual-machine:~# ps -ef | grep sshd root 3477 1 0 18:36 ...
- SpringMvc4.x--@ControllerAdvice注解
通过@ControllerAdvice.我们可以将对于控制器的全局配置放置在同一个位置,注解了@ControllerAdvice的类的方法可以使用@ExceptionHandler,@InitBind ...
- Windows核心编程&内核对象
1. 一个进程在初始化时,系统将会他分配一个空的句柄表,这个句柄表仅供内核对象使用,不供用户对象和GDI对象使用.进程在首次 初始化时,该句柄表为空.句柄表是一个由数据结构组成的数组,包含一个内核对象 ...
- npm install安装时忘记--save解决方法
title: npm install安装时忘记--save解决方法 date: 2017-05-07 20:17:54 tags: npm categories: --- 网上还有一个解决方案就是: ...
- easyUI中点击datagrid列标题排序
easyUI中点击datagrid的排序有两种,一种是本地的,一种是服务器的.本地的只能排序当前页,而服务器的可以对全部页进行排序.这里主要是分享下服务器排序. 1.为datagrid添加属性remo ...