华人澳洲中文论坛

热图推荐

    顺序从编译到被履行的流程

    [复制链接]

    2022-11-8 22:01:02 27 0

    当掌握愈来愈多的根底常识之后,你所看到的代码视角和你以前看代码的视角会产生一个天翻地覆的变动,就像你写代码看到的是一行一行代码的逻辑,而初级顺序员看到的是一行一行指令或者你写函数调用是一个正常的函数调用,其余人看到的是调用链面前被调用的状况,所以学货色尽可能学习一些根底,这样可以带给咱们很纷歧样的编程体验,也可以让你理解全部顺序的实质。当遇到瓶颈之后,更应该多学一些根底常识来丰硕本人的眼界。
    首先看下编译的进程,


    源代码会通过编译器,首先编译成汇编文件,汇编文件通过汇编器变为指标文件。在指标文件傍边,函数调用地址是没有被真实的链接起来的,链接的进程是需求通过链接器,把指标文件傍边相干的地址信息给链接起来,最初造成可履行的文件。
    c编译举例


    这是一个简略的add函数,在main办法外面调用这个add函数,而后进行打印。
    生成指标文件
    gcc -c main.c


    用gcc -c的命令能够生成一个指标文件,
    看下生成的指标文件里的地址信息
    objdump -d main.o
    objdump反编译看下指标文件存了哪些信息,




    这是一个.test段,顺序终究在内存下面或磁盘下面存储的时分,它不是无法则的存储,最初被翻译成机器码之后,也是一段一段存储的,每一个段所存的内容是纷歧样的,像.test段存储的就是正常的代码段也是函数段,而声明的全局变量会存在.data段或.bss段。
    这里只需求了解,咱们写的代码被翻译成机器码大略的分段逻辑就行了。
    左侧是这条指令的地址0 4 5 8 .... ,就是咱们写的顺序加载到内存傍边的时分是被加载成一条一条指令,而后每一个个指令都会对应一个特定的地址,cpu在取的时分,就会取这个地址下面的信息,就能知道这条指令地址所对应指令的详细内容。指标文件的这个地址是相对于地址,相对于于以后段的地址,以后段是.test段,所以是从0开始 按程序排上去。
    callq在汇编外面是调用函数的指令,这里写的是33 ,但其真实真正指标文件被链接成可履行文件之后,33会变为add函数的绝对地址。
    被链接成可履行文件之后,看下全部代码地址的变动,用gcc命令编译了一个可履行文件,反汇编看下,将.test段的地址列出来了,它曾经不是相对于于.test段的相对于地址了,而是一个绝对地址。




    而后看下调用callq add函数的时分 ,十一49所对应的首地址是add函数的第一行。括号在真实的机器代码中是不存在的,反汇编为了减少可读性才显示的。
    在看了顺序是怎么被编译成可履行文件之后,咱们又知道了可履行文件外面,每一个条指令所对应的地址代表甚么意思之后,来看下是如何被加载?
    这里要明确一点,顺序是在内存外面被履行的,被加载到内存之后,cpu能力从内存外面读取并履行,所以有一个从磁盘加载到内存的进程,这个进程由加载器去实现的。
    提到内存的话,就要提到cpu的实模式和维护模式。
    在很早以前,cpu在实模式时代,咱们的顺序所使用的地址都是物理地址,就是真实的在内存芯片上所能看到的物理地址,使用物理地址之后,就会致使咱们写的顺序被编译成可履行文件之后,可履行文件是由链接器编译成链接脚本生成的,而后在链接脚本外面能够指定顺序的首地址,假如要指定首地址(有一个默许的首地址),在实模式下,指定了以后编译顺序的首地址之后,那它被加载到物理地址之后,这个首地址就只能是真实的被加载到物理地址的阿谁中央,假如它的首地址好比是0x10,那它被加载到的物理地址的首地址假如不是0x10 就会致使前面那些指令的程序泛起问题,由于指令是程序排布的,就会致使前面的那些指令地址和可履行文件外面形容的这些指令地址是不吻合的。
    这样会致使callq函数会调用到过错的地址,所以在cpu的实模式下,调用顺序,顺序在履行的时分,它的首地址要固定住,这样就会致使一个问题就是得斟酌调得阿谁地址是否可用的,调用期间内存是否可用的,所以调演变为前面的cpu维护模式。
    cpu维护模式可以让顺序使用的是一个虚构地址,当初的64位零碎都是使用的页式办理,基于这个剖析一下。要明确虚构地址,首先要明确地址空间的概念,地址空间能够了解为过程能用的一个地址规模,好比过程能用的内存是5十二G,而后因为顺序通过编译之后是分段的,就以为这5十二G外面,0-10G是属于.test段,10-20G是属于.data段,20-200G属于堆空间,其余规模分:栈空间是哪一个规模,内核空间又是哪一个规模,只是将这段区间划分为了详细的内容所在的这段规模,然而不会实际的在内存下来调配这些内存,只是将规模划分出来,而实际保留的也是这些规模,当需求用到这些规模地址的时分,cpu才会去经过MMU列内外面去寻觅这个虚构地址所对应的物理地址,假如没有这个映照瓜葛,才会去真实的调配物理内存创立映照瓜葛,假如可履行文件一开始没有加载到内存,那末后续地址缺失是如何找到磁盘下面的文件地位的?所以需求看下可履行文件外面究竟有哪些信息?


    这里列出了可履行文件外面段的头部信息,在段的头部信息外面包孕了虚构地址、文件的偏移量,文件的偏移量能够了解为磁盘信息,能够经过偏移量去定位到在磁盘上的哪一个地位,所以操作零碎是能够这样做的:在可履行文件外面可以读到段地址还有文件偏移地址,所以在过程被加载履行的时分,刚开始被加载的时分,是能够为这个过程创立页表项,页表项是可以掩盖每个段的地址还有文件偏移的地址,然而这个时分,只是标志这个页表项所映照的这个映照瓜葛,只是标志,并无真实的调配实际的物理内存,这样比及页缺失的时分 ,够找到这个页表项并而且可以从这个页表项的标志去发现没有调配物理内存,这个时分再从磁盘下来读,再建设映照瓜葛,这样就可以够达到在真正使用的时分再去调配物理内存的目的了。

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    返回列表 本版积分规则

    :
    中级会员
    :
    论坛短信
    :
    未填写
    :
    未填写
    :
    未填写

    主题32

    帖子52

    积分228

    图文推荐