Linux下可执行程序的分段

简介: Linux下可执行程序的分段

Linux下可执行程序的分段


说明:

Linux系统下编译产生的二进制程序是以ELF格式存储的,ELF格式是分段的;Linux系统采用段式内存管理架构,二进制程序加载进内存后内存分布也是分段的。

windows系统也是类似的。

个人理解:

分段是由编译器和操作系统实现,编译时编译器将不同类型的元素存储到相应的段,以区分处理,利于管理和加快操作效率。

分段行为不是固定的,不同编译器,不同平台可能有细微差别,虚拟内存和硬盘都是连续的。

程序由数据和处理两部分组成,处理部分(代码)编译后为二进制指令(代码段)比较固定,而数据部分(变量)有多种形式,例如:全局变量,局部变量,static变量,const变量等等,为了实现这些需求和加快操作效率,因此数据段有多种分段类型。

一、代码:

段名:代码段(text段)

编译后生成给CPU执行的机器指令,一个程序只有一个代码段,只读,防止程序由于意外事故而修改自身指令。

该段也有可能包含一些只读的常量,例如字符串常量,const修饰的变量(各种单片机的编译器会这样存储),由编译器决定。

变量:

全局变量(生命周期等于整个程序执行期)

全局变量在编码时已经固定了,占用内存大小和值已确定,因为这些特性,全局变量可以在编译时就准备好,不需要等到运行时再去逐个分配内存设置值。

这里说的全局变量包括 全局变量,static的全局变量,static的局部变量。

全局变量由于初始值的不同,有以下两种情况。

二、已赋不为0的初始值:

段名:已初始化数据段(data段)。

初值不为0,编译器简单处理认为数据都不同,需要逐个变量保存到二进制程序文件中,程序运行时直接将整段数据拷贝进内存。

个人理解:编译器可以进一步优化,相同的值可以只存一份。

该段保存了程序中所有赋了初值并且初值不为0的全局变量,包括全局变量,static的全局变量,static的局部变量。

如果该段数据较多,会导致程序二进制文件非常大,如下:

int ar[300000] = {1};        //将全部存储在data段,虽然值都是一样的
static int a = 10;           //保存在data段
void test(){
    static int b = 10; 
}

三、未赋初值或者初值为0:

   1、段名:未初始化数据段(bss段)。

   2、由于可以将这些变量的初值处理成一样的,都设置为0,二进制程序文件就没必要存储这些值,只需要记录该段的首地址和段长度,程序运行加载进内存时再按这些参数申请和格式化内存就好。

   3、该段保存程序中没有进行初始化或者初始化为0的全局变量;例如:

int a; 
int b = 0;
static int a;
static int a = 0;
void test(){
    static int b;
    static int b = 0;
}

四、局部变量(生命周期由系统控制)

   1、段名:栈。

   2、由于局部变量不是固定的,无法在编译时进行处理。

   3、 增长方向:自顶向下增长;普通的局部变量以及每次函数调用时所需要保存的信息(返回地址;环境信息)。

   4、栈变量最大的特征是:栈变量都应该是临时变量,出栈后内存就非常可能被覆盖。

五、动态变量(生命周期由程序员控制)

  1、段名:堆。

  2、该段的大小并不固定,可动态扩张或缩减。

  3、因为动态变量并不是固定的,无法在编译时进行处理。

  4、动态存储区,是向高地址扩展的数据类型,是自下向上的扩展方式,动态内存分配的内存区域。

  5、堆变量最大的特征是:大内存变量或者永久变量,临时小内存变量不应该放在堆内存中,应该放在栈内存中。

六、只读变量

 1、段名:只读变量区域(rodata段)

 2、只读的内存区域,const修饰的常量,以及常量字符串保存在该区域。例如:

const int a = 100; 
char *b = "hello world"。 //hello world保存在该段,但是变量b不是。

存储时形态:

   二进制程序中不包含堆,栈需要动态管理的段,程序加载进内存才会产生。

运行时形态:

  1、每个进程都独自拥有4GB的虚拟内存空间,使用时再由系统转换成物理内存地址,该转换对用户是透明不可见的,因此时常讲的内存空间都是虚拟内存空间。

  2、程序的代码段和一些全局变量是固定的,不会改变的,编译时会将代码段和这些变量的虚拟内存地址确定,因此每次运行都是固定的。

  3、Linux下C程序的内存分布图如下,C程序将内存分为以下5部分,地址从低到高排列:

相关文章
|
5月前
|
安全 Linux Shell
Linux上执行内存中的脚本和程序
【9月更文挑战第3天】在 Linux 系统中,可以通过多种方式执行内存中的脚本和程序:一是使用 `eval` 命令直接执行内存中的脚本内容;二是利用管道将脚本内容传递给 `bash` 解释器执行;三是将编译好的程序复制到 `/dev/shm` 并执行。这些方法虽便捷,但也需谨慎操作以避免安全风险。
267 6
|
6月前
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
795 2
|
6月前
|
Linux Python
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
113 2
|
4月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
158 1
|
5月前
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
65 5
|
6月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
257 3
|
6月前
|
网络协议 Linux Shell
在Linux中,如何通过一个端口找到程序?
在Linux中,如何通过一个端口找到程序?
|
6月前
|
Linux API
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
|
6月前
|
Linux Windows Python
最新 Windows\Linux 后台运行程序注解
本文介绍了在Windows和Linux系统后台运行程序的方法,包括Linux系统中使用nohup命令和ps命令查看进程,以及Windows系统中通过编写bat文件和使用PowerShell启动隐藏窗口的程序,确保即使退出命令行界面程序也继续在后台运行。
|
7月前
|
Java Linux Shell
Linux后台运行jar程序
【7月更文挑战第23天】
136 1