0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

程序从编译到被执行的流程介绍

sakobpqhz6 来源:平凡人笔记 2023-03-15 09:11 次阅读

当掌握越来越多的基础知识之后,你所看到的代码视角和你之前看代码的视角会发生一个翻天覆地的变化,就像你写代码看到的是一行一行代码的逻辑,而高级程序员看到的是一行一行指令或者你写函数调用是一个正常的函数调用,其他人看到的是调用链背后被调用的情况,所以学东西尽量学习一些基础,这样能够带给我们很不一样的编程体验,也能够让你了解整个程序的本质。当遇到瓶颈之后,更应该多学一些基础知识来丰富自己的眼界。

首先看下编译的过程,

b6346b74-c2ab-11ed-bfe3-dac502259ad0.png

源代码会经过编译器,首先编译成汇编文件,汇编文件经过汇编器变成目标文件。在目标文件当中,函数调用地址是没有被真正的链接起来的,链接的过程是需要经过链接器,把目标文件当中相关的地址信息给链接起来,最后形成可执行的文件。

c编译举例

b6457fcc-c2ab-11ed-bfe3-dac502259ad0.png

这是一个简单的add函数,在main方法里面调用这个add函数,然后进行打印。

生成目标文件

gcc -c main.c

b6cfea5e-c2ab-11ed-bfe3-dac502259ad0.png

用gcc -c的命令可以生成一个目标文件,

看下生成的目标文件里的地址信息

objdump -d main.o

objdump反编译看下目标文件存了哪些信息,

b7180f6e-c2ab-11ed-bfe3-dac502259ad0.png

b74e6424-c2ab-11ed-bfe3-dac502259ad0.png

这是一个.test段,程序最终在内存上面或磁盘上面存储的时候,它不是无规律的存储,最后被翻译成机器码之后,也是一段一段存储的,每一段所存的内容是不一样的,像.test段存储的就是正常的代码段也是函数段,而声明的全局变量会存在.data段或.bss段。

这里只需要理解,我们写的代码被翻译成机器码大概的分段逻辑就行了。

左边是这条指令的地址0 4 5 8 .... ,就是我们写的程序加载到内存当中的时候是被加载成一条一条指令,然后每一个指令都会对应一个特定的地址,cpu在取的时候,就会取这个地址上面的信息,就可以知道这条指令地址所对应指令的具体内容。目标文件的这个地址是相对地址,相对于当前段的地址,当前段是.test段,所以是从0开始 按顺序排下来。

callq在汇编里面是调用函数的指令,这里写的是33 ,但其实在真正目标文件被链接成可执行文件之后,33会变成add函数的绝对地址。

被链接成可执行文件之后,看下整个代码地址的变化,用gcc命令编译了一个可执行文件,反汇编看下,将.test段的地址列出来了,它已经不是相对于.test段的相对地址了,而是一个绝对地址。

b7deaf70-c2ab-11ed-bfe3-dac502259ad0.pngb879aebc-c2ab-11ed-bfe3-dac502259ad0.png

然后看下调用callq add函数的时候 ,1149所对应的首地址是add函数的第一行。<>括号在真正的机器代码中是不存在的,反汇编为了增加可读性才显示的。

在看了程序是怎么被编译成可执行文件之后,我们又知道了可执行文件里面,每一条指令所对应的地址代表什么意思之后,来看下是如何被加载?

这里要明白一点,程序是在内存里面被执行的,被加载到内存之后,cpu才能从内存里面读取并执行,所以有一个从磁盘加载到内存的过程,这个过程由加载器去完成的。

提到内存的话,就要提到cpu的实模式和保护模式。

在很早之前,cpu在实模式时期,我们的程序所使用的地址都是物理地址,就是真正的在内存芯片上所能看到的物理地址,使用物理地址之后,就会导致我们写的程序被编译成可执行文件之后,可执行文件是由链接器编译成链接脚本生成的,然后在链接脚本里面可以指定程序的首地址,如果要指定首地址(有一个默认的首地址),在实模式下,指定了当前编译程序的首地址之后,那它被加载到物理地址之后,这个首地址就只能是真正的被加载到物理地址的那个地方,如果它的首地址比如是0x10,那它被加载到的物理地址的首地址如果不是0x10 就会导致后面那些指令的顺序出现问题,因为指令是顺序排布的,就会导致后面的那些指令地址和可执行文件里面描述的这些指令地址是不吻合的。

这样会导致callq函数会调用到错误的地址,所以在cpu的实模式下,调用程序,程序在执行的时候,它的首地址要固定住,这样就会导致一个问题就是得考虑调得那个地址是不是可用的,调用期间内存是不是可用的,所以会演变成后面的cpu保护模式。

cpu保护模式能够让程序使用的是一个虚拟地址,现在的64位系统都是使用的页式管理,基于这个分析一下。要明白虚拟地址,首先要明白地址空间的概念,地址空间可以理解为进程能用的一个地址范围,比如进程能用的内存是512G,然后由于程序经过编译之后是分段的,就认为这512G里面,0-10G是属于.test段,10-20G是属于.data段,20-200G属于堆空间,其他范围分:栈空间是哪个范围,内核空间又是哪个范围,只是将这段区间划分为了具体的内容所在的这段范围,但是不会实际的在内存上去分配这些内存,只是将范围划分出来,而实际保存的也是这些范围,当需要用到这些范围地址的时候,cpu才会去通过MMU列表里面去寻找这个虚拟地址所对应的物理地址,如果没有这个映射关系,才会去真正的分配物理内存创建映射关系,如果可执行文件一开始没有加载到内存,那么后续地址缺失是如何找到磁盘上面的文件位置的?所以需要看下可执行文件里面到底有哪些信息?

b8b27fa8-c2ab-11ed-bfe3-dac502259ad0.png

这里列出了可执行文件里面段的头部信息,在段的头部信息里面包含了虚拟地址、文件的偏移量,文件的偏移量可以理解为磁盘信息,可以通过偏移量去定位到在磁盘上的哪个位置,所以操作系统是可以这样做的:在可执行文件里面能够读到段地址还有文件偏移地址,所以在进程被加载执行的时候,刚开始被加载的时候,是可以为这个进程创建页表项,页表项是能够覆盖每个段的地址还有文件偏移的地址,但是这个时候,只是标记这个页表项所映射的这个映射关系,只是标记,并没有真正的分配实际的物理内存,这样等到页缺失的时候 ,够找到这个页表项并并且能够从这个页表项的标记去发现没有分配物理内存,这个时候再从磁盘上去读,再建立映射关系,这样就能够达到在真正使用的时候再去分配物理内存的目的了。





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10444

    浏览量

    206566
  • GCC
    GCC
    +关注

    关注

    0

    文章

    104

    浏览量

    24716
  • 编译器
    +关注

    关注

    1

    文章

    1577

    浏览量

    48618
  • 汇编器
    +关注

    关注

    0

    文章

    31

    浏览量

    11193

原文标题:程序从编译到被执行的流程

文章出处:【微信号:IC学习,微信公众号:IC学习】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    cc2640 multirole工程代码没有被执行

    cc2640 multirole工程代码没有被执行?在使用cc2640 2.1版本的multirole工程时出现问题,当工程作为主机连接机时会出现一个问题,当机因为距离较远等原因与工程主机断开
    发表于 03-14 09:58

    C/C++程序编译流程

    指令,并生成可重定位目标程序的.o文件,该文件为二进制文件,字节编码是机器指令。汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。所以汇编器的汇编过程相对于编译器来讲
    发表于 04-17 16:24

    异常处理程序没有被执行

    Hy-ALL,我的问题是我的异常处理程序没有被执行,而是Stand异常处理程序IS.我定义了我的异常处理程序:代替它,MPLAB提供的一般异常处理
    发表于 09-17 16:50

    定时器中断实验里的中断服务是怎么被执行的?

    我试时,一直搞不明白,里面的中断服务是怎样被执行的呢?当TIM3产生中断后,Keil 怎么会自动执行" void TIM3_IRQHandler(void)"这个服务程序的呢?是不是函数名相同就会
    发表于 09-18 23:21

    求芯片内部的程序执行流程

    想了解仿真连接仿真后,芯片内部的程序执行流程,请问有相关介绍的资料吗?
    发表于 05-25 12:08

    源代码CPU执行过程

    1.源代码CPU执行过程.c等高级语言经过编译编译后转换为.s汇编源代码经过汇编器转化为elf格式二进制可
    发表于 12-20 07:55

    C++:全局对象、局部对象、静态对象

    1.对于 全局对象 ,程序一开始,其构造函数就先被执行(比程序进入点更早);程序即将结束前其析构函数将被执行。 2.对于 局部对象 ,当对象
    发表于 11-29 19:12 2958次阅读

    mfc程序执行流程小结,MFC程序执行顺序

     摘要:本文章主要以MFC程序执行流程执行顺序等执行过程的剖析做出的结论,下面一起来看看原文的具体
    发表于 12-08 15:48 9221次阅读
    mfc<b class='flag-5'>程序</b><b class='flag-5'>执行</b><b class='flag-5'>流程</b>小结,MFC<b class='flag-5'>程序</b>的<b class='flag-5'>执行</b>顺序

    一文看懂python程序执行过程

    本文主要介绍的是python程序执行过程,首先介绍的是编译过程,其次介绍的是过程图解及
    发表于 04-26 18:18 1.7w次阅读
    一文看懂python<b class='flag-5'>程序</b>的<b class='flag-5'>执行</b>过程

    C语言程序设计实用教程之如何进行顺序程序资料和程序概述

    C程序流程一般分为顺序结构、选择结构和循环结构。 顺序结构是程序设计语言中最基本的结构,顺序结构程序由简单语句组成,语句按书写顺序执行
    发表于 10-31 18:04 1次下载
    C语言<b class='flag-5'>程序</b>设计实用教程之如何进行顺序<b class='flag-5'>程序</b>资料和<b class='flag-5'>程序</b>概述

    一条SQL语句是怎么被执行

    一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了。 本文将从MySQL总体架构---》查询执行流程---》语句
    的头像 发表于 09-12 09:44 1314次阅读
    一条SQL语句是怎么<b class='flag-5'>被执行</b>的

    罗永浩回应被执行信息清零

    近日,罗永浩对被执行信息清零做出了相关回应,称被执行信息清零不等于债务已经全部偿还完毕,自己还款远超预期,目前已经在按照已达成的执行和解协议继续履行,称罗老师仍在努力工作。
    的头像 发表于 01-06 11:14 1437次阅读

    中断服务子程序是如何被执行的 ?

    笔者在 《程序是如何在 CPU 中运行的(二)》中从 PC 指针寄存器的角度分析了一级函数调用和二级函数调用执行的过程,那么中断服务子程序...
    发表于 02-07 11:02 2次下载
    中断服务子<b class='flag-5'>程序</b>是如何<b class='flag-5'>被执行</b>的 ?

    折叠屏手机鼻祖柔宇科技拖欠百万合同款成失信被执行人,公司累计被执行超 1 亿

    曾与和力广告公司达成和解协议,约定向后者分两期支付合同欠款 110 余万元,如未履行任何一期的付款义务,后者有权立即向法院申请强制执行。案件流程显示,去年 9 月,柔宇科技公司首次被执行执行
    的头像 发表于 03-01 22:18 340次阅读

    程序编译被执行流程

    当掌握越来越多的基础知识之后,你所看到的代码视角和你之前看代码的视角会发生一个翻天覆地的变化,就像你写代码看到的是一行一行代码的逻辑,而高级程序员看到的是一行一行指令或者你写函数调用是一个正常的函数
    的头像 发表于 03-15 09:11 372次阅读