C语言程序在内存中是怎样布局的

这篇具有很好参考价值的文章主要介绍了C语言程序在内存中是怎样布局的。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

理论

我们假设在32位Linux下进行编程,首先要明确,我们的虚拟地址空间有4G,4G的地址空间的寻址范围就是 0x0000_0000 ~ 0xFFFF_FFFF,在Linux下,高地址的1G内存是给操作系统使用的,也就是 0xC000_0000 ~ 0xFFFF_FFFF,这一段地址我们无法使用,可以使用的只有 0x0000_0000~0xBFFF_FFFF这一段地址。

其次要知道程序会分为那些段

  • .text 代码段:可执行程序代码(只读)
  • .rodata 只读数据段: 程序中的常量(只读)
  • .data 初始化数据段: 程序中已经初始化的全局变量。(可读可写)
  • .bss 未初始数据段: 程序中未初始的全局变量。(可读可写)
  • heap 堆区:程序中动态分配的内存。堆的内存向上增长。(可读可写)
  • 共享数据区:
  • stack 栈区:程序中的函数、函数的形参、函数的局部变量 。(可读可写)

我们的程序,编译为汇编文件的过程中,会有很多的节(section),链接器将这些目标文件中属性相同的节(section)合并成段(segment),因此一个段是由多个节组成的,我们平时所说的C程序内存空间中的数据段、代码段就是指合并后的segment。

为什么要将section合并成segment?这么做的原因也很简单,一是为了保护模式下的安全检查,二是为了操作系统在加载程序时省事。在保护模式下对内存的访问必须要经过段描述符,段描述符用来描述一段内存区域的访问属性,其中的S位和TYPE位可组合成多种权限属性,处理器用这些属性来限制程序对内存的使用,如果程序对某片内存的访问方式不符合该内存所对应的段描述符(由访问内存时所使用的选择子决定)中设置的权限,比如对代码这种具备只读属性的内存区域执行了写操作,处理器会检查到这种情况并抛出GP异常。

扯远了,那么我们的内存布局就可以看下面这个图:

C语言程序在内存中是怎样布局的

其中多了一个区域 dynamic library 动态装载区,这个区域用于映射装载的动态链接库。在Linux下,如果可执行文件依赖其他共享库,那么系统就会为它在从0x40000000开始的地址分配相应的空间,并将共享库载入到该空间。

其中我们发现,蓝色的是只读的,橘色的是可读可写的数据,红色的是堆栈。其实其存放是按照权限来的,这也符合我们上面将的和操作系统对权限的检查相吻合。

实例

只讲理论是很难理清的,我们举个例子

我们写个c的程序

#include <stdlib.h>

int main(){
	int i = 0;
	while (1) {
		sleep(1000);
	}
	return 0;
}

编译一下

gcc temp.c -o temp.out -fno-pic -m32

其中 -no-pie 是不将程序地址随机化,-m32是将程序编译为32位程序,因为现在使用的都是64位系统,我们这里只讲32位,实际上本质是一样的。

执行这个程序

./temp.out

找到这个程序的pid

ps -C temp.out

得到程序的PID

➜  ~ ps -C temp.out
    PID TTY          TIME CMD
  33862 pts/2    00:00:00 temp.out

根据PID获取程序的内存映射

pmap -d 33862

得到内存映射关系

➜  ~ pmap -d 34204                      
34204:   ./temp.out
Address           Kbytes Mode  Offset           Device    Mapping
0000000008048000       4 r---- 0000000000000000 008:00005 temp.out
0000000008049000       4 r-x-- 0000000000001000 008:00005 temp.out
000000000804a000       4 r---- 0000000000002000 008:00005 temp.out
000000000804b000       4 r---- 0000000000002000 008:00005 temp.out
000000000804c000       4 rw--- 0000000000003000 008:00005 temp.out
00000000f7d25000     100 r---- 0000000000000000 008:00005 libc-2.31.so
00000000f7d3e000    1388 r-x-- 0000000000019000 008:00005 libc-2.31.so
00000000f7e99000     464 r---- 0000000000174000 008:00005 libc-2.31.so
00000000f7f0d000       4 ----- 00000000001e8000 008:00005 libc-2.31.so
00000000f7f0e000       8 r---- 00000000001e8000 008:00005 libc-2.31.so
00000000f7f10000       4 rw--- 00000000001ea000 008:00005 libc-2.31.so
00000000f7f11000      12 rw--- 0000000000000000 000:00000   [ anon ]
00000000f7f28000       8 rw--- 0000000000000000 000:00000   [ anon ]
00000000f7f2a000      16 r---- 0000000000000000 000:00000   [ anon ]
00000000f7f2e000       8 r-x-- 0000000000000000 000:00000   [ anon ]
00000000f7f30000       4 r---- 0000000000000000 008:00005 ld-2.31.so
00000000f7f31000     120 r-x-- 0000000000001000 008:00005 ld-2.31.so
00000000f7f4f000      44 r---- 000000000001f000 008:00005 ld-2.31.so
00000000f7f5b000       4 r---- 000000000002a000 008:00005 ld-2.31.so
00000000f7f5c000       4 rw--- 000000000002b000 008:00005 ld-2.31.so
00000000fff9c000     132 rw--- 0000000000000000 000:00000   [ stack ]
mapped: 2340K    writeable/private: 164K    shared: 0K

当然我们也可以换一个命令

➜  ~ cat /proc/33862/maps           
08048000-08049000 r--p 00000000 08:05 658197                             /home/lovetzp/Desktop/temp.out
08049000-0804a000 r-xp 00001000 08:05 658197                             /home/lovetzp/Desktop/temp.out
0804a000-0804b000 r--p 00002000 08:05 658197                             /home/lovetzp/Desktop/temp.out
0804b000-0804c000 r--p 00002000 08:05 658197                             /home/lovetzp/Desktop/temp.out
0804c000-0804d000 rw-p 00003000 08:05 658197                             /home/lovetzp/Desktop/temp.out
f7d25000-f7d3e000 r--p 00000000 08:05 569025                             /usr/lib/i386-linux-gnu/libc-2.31.so
f7d3e000-f7e99000 r-xp 00019000 08:05 569025                             /usr/lib/i386-linux-gnu/libc-2.31.so
f7e99000-f7f0d000 r--p 00174000 08:05 569025                             /usr/lib/i386-linux-gnu/libc-2.31.so
f7f0d000-f7f0e000 ---p 001e8000 08:05 569025                             /usr/lib/i386-linux-gnu/libc-2.31.so
f7f0e000-f7f10000 r--p 001e8000 08:05 569025                             /usr/lib/i386-linux-gnu/libc-2.31.so
f7f10000-f7f11000 rw-p 001ea000 08:05 569025                             /usr/lib/i386-linux-gnu/libc-2.31.so
f7f11000-f7f14000 rw-p 00000000 00:00 0 
f7f28000-f7f2a000 rw-p 00000000 00:00 0 
f7f2a000-f7f2e000 r--p 00000000 00:00 0                                  [vvar]
f7f2e000-f7f30000 r-xp 00000000 00:00 0                                  [vdso]
f7f30000-f7f31000 r--p 00000000 08:05 569021                             /usr/lib/i386-linux-gnu/ld-2.31.so
f7f31000-f7f4f000 r-xp 00001000 08:05 569021                             /usr/lib/i386-linux-gnu/ld-2.31.so
f7f4f000-f7f5a000 r--p 0001f000 08:05 569021                             /usr/lib/i386-linux-gnu/ld-2.31.so
f7f5b000-f7f5c000 r--p 0002a000 08:05 569021                             /usr/lib/i386-linux-gnu/ld-2.31.so
f7f5c000-f7f5d000 rw-p 0002b000 08:05 569021                             /usr/lib/i386-linux-gnu/ld-2.31.so
fff9c000-fffbd000 rw-p 00000000 00:00 0                                  [stack]

也可以得到内存映射关系。

从内存的分布中我们可以找到栈区,以及动态链接的一些库,以及头部的五个来自temp.out的区,这个内存映射分布与我们图并不是那么一致,是因为操作系统在处理segment段的时候,有自己的一些理解在里面,具体的就得看操作系统的实现了。

而且我们没有发现有堆区,这个我们可以在程序中加一个malloc,再看一下内存布局就有了。
库,以及头部的五个来自temp.out的区,这个内存映射分布与我们图并不是那么一致,是因为操作系统在处理segment段的时候,有自己的一些理解在里面,具体的就得看操作系统的实现了。

而且我们没有发现有堆区,这个我们可以在程序中加一个malloc,再看一下内存布局就有了。文章来源地址https://www.toymoban.com/news/detail-507667.html

到了这里,关于C语言程序在内存中是怎样布局的的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【软件设计师07】程序设计语言与语言处理程序基础

    编译与解释、文法、正规式、有限自动机、表达式、传值与传址、多中程序语言特点 (逐渐降低考察比例,很少考察) 概念 文法类型 语法推导树 有限自动机 与正规式(重点) 常见问题:给一个串比如01、10、001等,看图中起点到终点能否连起来得到这样的串 正规式 是有限自

    2023年04月08日
    浏览(45)
  • 【C语言学习2——第一个C语言程序】

    程序员之间有一个约定俗成的习惯,我们在学习任何编程语言时,所写的第一个程序,就是在显示屏上 打印一行字符“Hello World”。 而为什么会有这个习惯呢?这个习惯又是从什么时候开始的呢? 其实,先让我们回顾一下C语言的历史,就可以了解到这个习惯的出处。 1972年

    2023年04月13日
    浏览(67)
  • 【C语言】详解计算机二级c语言程序题

    最近计算机二级的c语言快要考试了,我从网上摘抄了一些c语言二级的原题,并利用gpt做了一些解析来理解这些问题,以便大家能够更好的学习。 同时,我将问题答案设置为白色(只需要选中就可以查看),大家可以在理解问题的同时进行练习。 Dotcpp——里面有C语言原题以

    2024年02月21日
    浏览(29)
  • C语言程序生命周期

    可能大家都知道一个C语言程序需要经过编译生成可执行文件就可以运行起来,但是这并非是一个完整的C语言程序流程,下面我们就详细了解一下C语言程序的整个生命周期。 一个完整C语言的生命周期分为以下五个部分: 编写代码 编译 链接 装载 执行 编写代码是大家最熟悉不过

    2024年02月11日
    浏览(39)
  • C语言程序10题

    第60题 (20.0分)            难度:中        第1章 /*------------------------------------------------------- 【程序设计】 --------------------------------------------------------- 题目:请编写一个函数fun,它的功能是:计算并输出给定整数n的所有因子       (不包括1与自身)之和。规定n的值

    2024年04月15日
    浏览(34)
  • 浅谈Python中的内存管理 & 程序的内存布局

    Python 的内存管理是通过私有堆空间来实现的。这个私有堆内存中存储了所有 Python 对象和数据结构。Python 的解释器自身则拥有对堆空间的访问权,程序员不能直接访问这个私有堆,但可以通过解释器的 API 来进行某些操作。 以下是 Python 内存管理的关键特点和机制: 私有堆

    2024年02月14日
    浏览(27)
  • C语言程序实例100个

    C语言程序实例100个(一) 【程序2】 题目:企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可可提成7.5%;20万到40万之间时,高于20万元的部分,可提成5%;40万到60万之

    2024年02月05日
    浏览(41)
  • C语言程序运行需要的两大环境《C语言进阶》

    目录  程序的翻译环境和执行环境 翻译环境分为两部分,编译+链接 第一步:预编译(预处理) 第二步,编译 第三步:汇编 关于运行环境分为四点: 关于链接库 在 ANSI C(标准C) 的任何一种实现中,存在两个不同的环境。 *第1种是翻译环境。 在这个环境中源代码被转换为可

    2024年02月16日
    浏览(24)
  • 【C语言】程序阅读题

    输出以下程序的结果 A C E G 从A开始到H结束,k从0开始自加,k为偶数时输出字符 输出以下程序结果 k=8 输出程序结果 4 25 27 16 输出程序结果 1 0 2 2 5 7 13 20 从第三个开始,每个数加上前两个数的和 注意之前的求和会对后续的求和有影响 输入数据为2,4,输出程序的结果 SUM=2468

    2024年01月17日
    浏览(23)
  • 程序环境和预处理(含C语言程序的编译+链接)--2

    文章前言: 上章我们把      程序的翻译环境     程序的执行环境   C语言程序的编译+链接     预定义符号介绍    预处理指令   #define    宏和函数的对比     预处理操作符    #和##的介绍   的相关知识进行了梳理讲解,接下来被把剩余知识    命令定义     预处

    2024年02月14日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包