程序的运行(6/13)

news2025/2/13 5:04:32

程序的运行分为两种:一种基于操作系统环境,另一种是在无操作系统的环境下执行裸机程序。在 Linux 环境下,可执行文件是 ELF 格式(除了基本的代码段、数据段、还有文件头、符号表等用来辅助程序运行的信息),在裸机环境下执行的程序一般是 BIN/HEX 格式,它们是纯指令文件。

虽然两种程序运行环境不同,文件格式也有所差异,但原理是相通的:都要将指令加载到内存中的指定位置,而这个指定位置和执行文件链接时的链接地址有关。

操作系统环境下的程序运行

一个装有操作系统的计算机系统,当执行一个应用程序时,首先会运行一个叫做加载器的程序,加载器会根据软件的安装路径信息,将可执行文件从 ROM 中加载到内存,然后进行一些与初始化、动态库重定位相关的操作,最后才跳转到程序的入口运行。在 Linux 命令行模式下运行一个应用程序,类似 sh、bash 这样的 Shell 终端程序充当加载器的角色:把程序加载到内存,封装成进程,参与操作系统的调度和运行。

一个可执行文件可由不同的 section 组成,分为代码段、数据段、BSS 段等。加载器在加载程序运行时,会将这些代码段、数据段分别加载到内存的不同位置。可执行文件的文件头提供了文件类型、运行平台、程序的入口地址等基本信息,加载器在加载程序之前会首先根据文件头的信息做一些判断,如果发现程序的运行平台和当前的环境不符,则会报错。

除此之外,可执行文件中还有一个叫做段头表的段,段头表中记录的是如何将可执行文件加载到内存的相关信息,包括可执行文件中要加载到内存中的段、入口地址等信息。在一个可执行文件中,加载器要加载程序到内存,要依赖段头表提供的信息,因此段头表是必需的。

jiaming@jiaming-pc:~/Documents/CSDN_Project$ readelf -l a.out 

Elf file type is EXEC (Executable file)
Entry point 0x1030c
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x000718 0x00010718 0x00010718 0x00008 0x00008 R   0x4
  PHDR           0x000034 0x00010034 0x00010034 0x00120 0x00120 R   0x4
  INTERP         0x000154 0x00010154 0x00010154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.3]
  LOAD           0x000000 0x00010000 0x00010000 0x00724 0x00724 R E 0x10000
  LOAD           0x000f10 0x00020f10 0x00020f10 0x00118 0x0011c RW  0x10000
  DYNAMIC        0x000f18 0x00020f18 0x00020f18 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x00010168 0x00010168 0x00044 0x00044 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x000f10 0x00020f10 0x00020f10 0x000f0 0x000f0 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     .ARM.exidx 
   01     
   02     .interp 
   03     .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .ARM.exidx .eh_frame 
   04     .init_array .fini_array .dynamic .got .data .bss 
   05     .dynamic 
   06     .note.gnu.build-id .note.ABI-tag 
   07     
   08     .init_array .fini_array .dynamic 

在 Linux 环境下运行的程序一般都会封装成进程,参与操作系统的统一调度和运行。在 Shell 环境下运行一个程序,Shell 终端程序一般会先 fork 一个子进程,创建一个独立的虚拟进程地址空间,接着调用 execve 函数将要运行的程序加载到进程空间:通过可执行文件的文件头,找到程序的入口地址,建立进程虚拟地址空间与可执行文件的映射关系,将 PC 指针设置为可执行文件的入口地址,即可启动运行。一段 C 程序、编译生成的可执行文件、可执行文件运行时的进程之间的对应关系如图:

在这里插入图片描述
不同的编译器有不同的链接起始地址。在 Linux 环境下,GCC 链接时一般以 0x08040000 为起始地址开始存放代码段,而 ARM GCC 交叉编译器一般以 0x10000 为链接起始地址。紧挨着代码段,从一个 4KB 边界对齐的地址处开始存放数据段。紧挨着数据段,就是 BSS 段。BSS 段后面的第一个 4KB 地址对齐处,就是我们在程序中使用 malloc() / free() 申请的堆空间。

对于每一个运行的进程,Linux 内核都会使用一个 task_struct 结构体来表示,多个结构体通过指针构成链表。操作系统基于该链表就可以对这些进程进行管理、调度和运行。不同进程的代码段和数据段分别存储在物理内存不同的物理页上,进程间彼此独立,通过上下文切换,轮流占用 CPU 去执行自己的指令。当 Linux 环境下有多个进程并发运行时,C 源程序、可执行文件、进程和物理内存之间的对应关系如图:

在这里插入图片描述

裸机环境下的程序运行

在一个裸机平台下,系统上电后,没有程序运行的环境,需要借助第三方工具将程序加载到内存,然后才能正常运行。

很多集成开发环境如 ADS1.2、Keil、RVDS 等 IDE,不仅提供了程序编辑、编译的功能,同时支持程序运行、调试、烧写。以 ADS1.2 集成开发环境为例,可以通过 JTAG 接口和开发板通信,将在 PC 上编译好的 BIN/HEX 格式的 ARM 可执行文件下载到开发板的内存中运行。可以根据开发板的实际 RAM 物理地址,在编译程序时通过 ADS1.2 集成开发环境提供的 Debug Setting 设置选项来设置。

在这里插入图片描述

在一个嵌入式 Linux 系统中,Linux 内核镜像的运行其实就是裸机环境下的程序运行。Linux 内核镜像一般会借助 U-boot 这个加载工具将其从 Flash 存储分区加载到内存中运行,u-boot 在 Linux 启动过程中扮演了加载器的角色。

程序入口 main() 函数分析

加载器将指令加载到内存后,接着就开始运行程序了,main() 函数是通常理解意义上所有程序的入口函数,但默认的程序入口是 _start 符号,而不是 main,后者只是一个约定。

在 main 函数运行之前,已经做了许多初始化操作:它们主要完成运行 main 函数之前的一些初始化工作,如初始化堆栈指针、初始化 data 段内容,初始化的全局变量中,int 类型的全部初始化为 0,布尔型的变量初始化为 False,指针类型初始化为 NULL。完成初始化环境之后,这部分代码还会将用户传入的参数传递给 main,最后才跳入 main 函数运行。

这部分初始化代码是在程序编译阶段,由编译器自动添加到可执行文件中的。这部分代码属于 C 运行库(C Running Time,CRT)中的代码,编译器厂商在开发编译器时,除了实现 C 语言标准中规定的 printf、fopen、fread 等标准函数,还会实现这部分初始化代码,完成进入 main 函数之前的一系列初始化操作。

  • C 语言运行的基本堆栈环境、进程环境。
  • 动态库的加载、释放、初始化、清理等工作。
  • 向 main 函数传参 argc、argv,调用 main 函数执行。
  • 在 main 函数退出后,调用 exit 函数,结束进程的运行。

在 ARM 交叉编译器安装路径下的 lib 目录下,会看到 crt1.o 目标文件,这个文件由汇编初始化代码编译生成,是 CRT 的一部分。在链接过程中,链接器会将 crt1.o 这个目标文件和项目中的目标组装在一起,生成最终的可执行文件。

BSS 段

对于未初始化额全局变量和静态局部变量,编译器将其放置在 BSS 段中。BSS 段是不占用可执行文件存储空间,设置 BSS 段的目的是减少可执行文件的体积,节省磁盘空间。

虽然 BSS 段在可执行文件中不占用存储空间,但是当程序加载到内存运行时,加载器会在内存中给 BSS 段开辟一段存储空间。在段表中会记录 BSS 段的大小,在符号表中会记录每个变量的地址和大小。加载器会根据这些信息,在数据段的后面分配指定大小的内存空间并清零,根据符号表中各个变量的地址在这片内存中给各个未初始化的全局变量、静态变量分配存储空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/864028.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Arcgis中影像图切片有白斑或者白点

效果 步骤 1、3dmax渲染或者其它原片 2、Arcgis中加载图片 原数据效果 3、定义投影和转换坐标系等等 我这边测试数据是EPSG:4326的坐标系 4、导出jp2(JPG2000)格式 转换后效果 5、发布服务 6、效果对比

机器学习---梯度下降代码

1. 归一化 # Read data from csv pga pd.read_csv("pga.csv") print(type(pga))print(pga.head())# Normalize the data 归一化值 (x - mean) / (std) pga.distance (pga.distance - pga.distance.mean()) / pga.distance.std() pga.accuracy (pga.accuracy - pg…

centos7部署openldap开启memberof并接入jumpserver

文章目录 前言1.yum安装openldap2.配置密码3.导入配置4.定义域5.配置memberof6.配置base dn7.安装phpldapadmin管理8.调整httpd的配置9.调整php的配置10.登陆php管理页面11.同步旧ldapsever用户数据(可省略)12.客户端配置13.对接jumpserver 前言 介绍如何在centos7上部署openl…

SpringBoot复习:(29)静态资源的配置路径

WebMvcAutoConfiguration 首页处理:

Linux用户管理命令

一、系统存储用户信息的文件 (1)/etc/passwd 存储用户基本信息: 通过vi /etc/passwd查看用户基本信息: (2)/etc/group 存储用户组的信息: 通过vi /etc/group查看用户组信息: &…

SAP MM学习笔记16-在库品目评价

在库品目评价是指评估物料。具体比如物料价格,数量,保管场所等发生变化的时候,判断是否发生了变化,要不要生成 FI票,用哪个FI科目来进行管理等内容就叫在库品目评价。 在库品目评价有很多层级,这里先讲3兄弟…

【C++手撕系列】——设计日期类实现日期计算器

【C手撕系列】——设计日期类实现日期计算器😎 前言🙌C嘎嘎类中六大护法实现代码:获取每一个月天数的函数源码分享构造函数源码分享拷贝构造函数源码分享析构函数源码分享赋值运算符重载函数源码分享取地址和const取地址运算符重载函数源码分…

如何写一篇吸引人的新闻稿?揭秘新闻稿写作的技巧!

一篇高质量的新闻稿不仅能够吸引读者的眼球,还能提高文章的曝光量。下面,伯乐网络传媒将给大家揭秘新闻稿写作的十大技巧,帮助大家写出更有吸引力的新闻稿。 1. 选择热门而有吸引力的话题或爆点 要想写出一篇吸引人的新闻稿,首先…

【前端】CSS水平居中的6种方法

左右两边间隔相等的居中 文章目录 flex绝对定位margin:auto绝对定位margin:负值定位transformtext-align: center;margin: 0 auto;思维导图 flex display: flex;justify-content: center; <div classparent><div class"son"></div> </div>…

【VUE】7、VUE项目中集成watermark实现页面添加水印

在网站浏览中&#xff0c;常常需要网页水印&#xff0c;以便防止用户截图或录屏暴露敏感信息后&#xff0c;方便追踪用户来源。 1、安装 watermark 在 package.json 文件 dependencies 节点增加 watermark-dom 依赖 "watermark-dom": "2.3.0"然后执行命…

fastadmin 自定义搜索分类和时间范围

1.分类搜索&#xff0c;分类信息获取----php 2.对应html页面&#xff0c;页面底部加搜索提交代码&#xff08;这里需要注意&#xff1a;红框内容&#xff09; 图上代码----方便直接复制使用 <script id"countrySearch" type"text/html"><!--form…

初识鸿蒙跨平台开发框架ArkUI-X

HarmonyOS是一款面向万物互联时代的、全新的分布式操作系统。在传统的单设备系统能力基础上&#xff0c;HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念&#xff0c;能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备&#xff0c;提供全场景&#…

vue项目里面有多个模块的服务,前端处理url转发

先看下vue的代理配置里面&#xff1a; 现在是在 /pca 基础上增加了 2个模块的服务&#xff1a; /dca、 /api 现在服务器的nginx 没有在/pca 服务里面做转发接受 /dca、 /api的服务&#xff0c;所以需要前端自己去配置每个服务模块对应的 URL 先拿登录的api 做示例吧: 先定义…

多用户微商城多端智慧生态电商系统搭建

多用户微商城多端智慧生态电商系统的搭建步骤如下&#xff1a; 系统规划&#xff1a;在搭建多用户微商城多端智慧生态电商系统之前&#xff0c;需要进行系统规划。包括确定系统的目标、功能、架构、技术选型、开发流程等方面。市场调研&#xff1a;进行市场调研&#xff0c;了…

解决createRoot is not a function

报错&#xff1a; 出现的原因&#xff1a;在于把react18使用的vite构建&#xff0c;在开发中因react版本太高与其他库不兼容&#xff0c;而在降级的时候&#xff0c;出现以上dom渲染出现报错。 解决&#xff1a;将 src/index.j文件改成如下 import React from react; import…

玩转C链表

链表是C语言编程中常用的数据结构&#xff0c;比如我们要建一个整数链表&#xff0c;一般可能这么定义&#xff1a; struct int_node {int val;struct int_node *next;}; 为了实现链表的插入、删除、遍历等功能&#xff0c;另外要再实现一系列函数&#xff0c;比如&#xff1a…

微星游戏本旗舰性价比爆款:泰坦GP68 HX新品开启预售

微星游戏本金字塔尖的旗舰泰坦系列&#xff0c;讲究的就是一个“极致的性能释放”&#xff1a;除了机皇泰坦GT&#xff0c;旗舰性能的泰坦GE以外&#xff0c;泰坦GP则成为了泰坦系列中、甚至所有旗舰游戏本产品中的“旗舰游戏本的守门员”&#xff0c;让更多性能控玩家&#xf…

linux pwn 基础知识

环境搭建 虚拟机安装 镜像下载网站为了避免环境问题建议 22.04 &#xff0c;20.04&#xff0c;18.04&#xff0c;16.04 等常见版本 ubuntu 虚拟机环境各准备一份。注意定期更新快照以防意外。虚拟机建议硬盘 256 G 以上&#xff0c;内存也尽量大一些。硬盘大小只是上界&#…

Mongodb:业务应用(1)

环境搭建参考&#xff1a;mongodb&#xff1a;环境搭建_Success___的博客-CSDN博客 需求&#xff1a; 在文章搜索服务中实现保存搜索记录到mongdb 并在搜索时查询出mongdb保存的数据 1、安装mongodb依赖 <dependency><groupId>org.springframework.data</groupI…

rknn3588如何查看npu使用情况

cat /sys/kernel/debug/rknpu/load在Linux中&#xff0c;你可以使用一些工具和命令来绘制某一进程的实时内存折线图。一个常用的工具是gnuplot&#xff0c;它可以用来绘制各种图表&#xff0c;包括折线图。 首先&#xff0c;你需要收集进程的实时内存数据。你可以使用pidstat命…