C语言中内存分布及程序运行加载过程

一个程序内存分配:

下图是APUE中的一个典型C内存空间分布图(虚拟内存)

例如:

#include
int g1=0, g2=0, g3=0;
int max(int i)
{
    int m1=0,m2,m3=0,*p_max;
    static n1_max=0,n2_max,n3_max=0;
     p_max = (int*)malloc(10);
    printf("打印max程序地址\n");
    printf("in max: 0xx\n\n",max);
    printf("打印max传入参数地址\n");
    printf("in max: 0xx\n\n",&i);
    printf("打印max函数中静态变量地址\n");
    printf("0xx\n",&n1_max); //打印各本地变量的内存地址
    printf("0xx\n",&n2_max);
    printf("0xx\n\n",&n3_max);
    printf("打印max函数中局部变量地址\n");
    printf("0xx\n",&m1); //打印各本地变量的内存地址
    printf("0xx\n",&m2);
    printf("0xx\n\n",&m3);
    printf("打印max函数中malloc分配地址\n");
    printf("0xx\n\n",p_max); //打印各本地变量的内存地址
    if(i) return 1;
    else return 0;
}
int main(int argc, char **argv)
{
    static int s1=0, s2, s3=0;
    int v1=0, v2, v3=0;
    int *p;   
    p = (int*)malloc(10);
    printf("打印各全局变量(已初始化)的内存地址\n");
    printf("0xx\n",&g1); //打印各全局变量的内存地址
    printf("0xx\n",&g2);
    printf("0xx\n\n",&g3);
    printf("======================\n");
    printf("打印程序初始程序main地址\n");
    printf("main: 0xx\n\n", main);
    printf("打印主参地址\n");
    printf("argv: 0xx\n\n",argv);
    printf("打印各静态变量的内存地址\n");
    printf("0xx\n",&s1); //打印各静态变量的内存地址
    printf("0xx\n",&s2);
    printf("0xx\n\n",&s3);
    printf("打印各局部变量的内存地址\n");
    printf("0xx\n",&v1); //打印各本地变量的内存地址
    printf("0xx\n",&v2);
    printf("0xx\n\n",&v3);
    printf("打印malloc分配的堆地址\n");
    printf("malloc: 0xx\n\n",p);
    printf("======================\n");
    max(v1);
    printf("======================\n");
    printf("打印子函数起始地址\n");
    printf("max: 0xx\n\n",max);
    return 0;
}

打印结果:

可以大致查看整个程序在内存中的分配情况:

可以看出,传入的参数,局部变量,都是在栈顶分布,

随着子函数的增多而向下增长. 函数的调用地址(函数运行代码)(高地址)

而malloc分配的堆则存在于这些内存之上,并向上生长

全局变量,静态变量都是在分配内存的低部存在(低地址)

程序如何装载的

1 编译:

2 编译结果:

file a.out 查看文件类型 a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0xd66ac36636c4fcfcbe395efb6bbd38c053e1c6c7, not stripped

ELF目标文件格式的最前端是ELF文件头(ELF Header)

包含了描述整个文件的基本属性,如ELF版本、目标机器型号、程序入口地址

3 加载:

图1做了简单的说明(Linux系统下的)

左边的是UNIX/LINUX系统的执行文件,右边是对应进程逻辑地址空间的划分情况。

我理解就是类似mmap函数 直接内存映射

1 ELF文件头 指定加载入口地址

2 加载 代码段 数据段 其他部分

参考

1 Linux内核如何装载和启动一个可执行程序

http://www.cnblogs.com/bushifudongjing/p/5361805.html

2 <程序员的自我修养—链接、装载与库.pdf>

原文发布于微信公众号 - 架构说(JiaGouS)

原文发表时间:2016-04-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java一日一条

Java编程常见问题汇总2

这里有一个前提,就是文件大小不能讲JVM的heap撑爆。否则就等着OOM吧,尤其是在高并发的服务器端代码。最好的做法是采用Stream的方式边读取边存储(本地文...

9810
来自专栏云霄雨霁

Java--线程同步&线程通信

30370
来自专栏Python爬虫实战

Python指南:文件处理

哪种文件格式最适合用于存储整个数据集——二进制、文本还是XML?这严重依赖于具体的上下文。

15710
来自专栏Python

django:DateTimeField如何自动设置为当前时间并且能被修改 ——django日期时间字段的使用

创建django的model时,有DateTimeField、DateField和TimeField三种类型可以用来创建日期字段,其值分别对应着datetime...

62680
来自专栏小樱的经验随笔

【Java学习笔记之三十四】超详解Java多线程基础

前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧。 正文 线...

38250
来自专栏大闲人柴毛毛

Java并发编程的艺术(五)——中断

什么是中断? 在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的机制——中断。 中断...

36770
来自专栏Python自动化测试

Jmeter4.0接口测试之断言实战(六)

在接口测试用例中得有断言,没有断言的接口用例是无效的,一个接口的断言有三个层面,一个是HTTP状态码的断言,另外一个是业务状态码的断言,最后是某一接口请求后服...

94440
来自专栏云计算教程系列

如何使用Grep

Grep是一个命令行实用程序,可以使用常见的正则表达式语法搜索和过滤文本。它无处不在,动词“to grep”已经成为“搜索”的同义词。它grep是一个有用的工具...

14730
来自专栏北京马哥教育

20 分钟 Shell 入门深度教程

作者:aron1992 来源:https://my.oschina.net/FEEDFACF/blog/1789695 0. 背景 之前写了系列的shell实...

65360
来自专栏coder修行路

SocketServer源码学习补充

16230

扫码关注云+社区

领取腾讯云代金券