zoukankan      html  css  js  c++  java
  • 从一段代码的汇编看计算机的工作原理

    朱宇轲 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

    /*-------------------------------------以下内容是课堂笔记,咿呀咿呀呦!------------------------------------------*/

    本课主要对计算机的运行原理和汇编语言进行了简单的介绍。

    冯若依曼体系结构即存储程序计算机,也就是将程序写在内存中,由CPU通过总线从内存中读取一条条程序,根据程序的内容执行具体的步骤。

    如图所示

    CPU在读取指令时,通过寄存器IP来指向下一条指令(如果是32位系统,则为EIP)

    CPU的寄存器分为通用寄存器、段寄存器、状态寄存器

    四种寻址方式:

    movl %eax,%edx     edx=eax                          寄存器寻址

    movl $0x123,%edx  edx=0x123                       立即寻址

    movl 0x123,%edx    edx=*(int32_t*)0x123        直接寻址

    movl (%ebx),%edx   edx=*(int32_t*)ebx           间接寻址

    movl 4(%ebx),%edx  edx=*(int32_t*)(ebx+4)    变址寻址

    了解pushl、popl、call 0x12345、ret命令

    注意:IP寄存器一般不能随便修改,只能通过call、ret等命令更改!

    函数的返回值默认使用EAX寄存器存储返回给上一级函数

        

    /*-------------------------以下内容是实验分析,咿呀咿呀呦!------------------------------------------*/

         首先写下这么一段C程序:

     1 //linux.c
     2 int g(x)
     3 {
     4     return x+3;
     5 }
     6 int f(x)
     7 {
     8     return g(x);
     9 }
    10 int main()
    11 {
    12     return f(10)+1;
    13 }

      在Linux的环境中输入如下指令:

    gcc –S –o linux.s linux.c -m32

      然后打开linux.s,就可以看到我们汇编后的代码(直接上截图了)

     

       将里面以“.”开头的行去掉(这是为链接用的),得到汇编后的代码:

     1 g:
     2     pushl   %ebp
     3     movl    %esp, %ebp
     4     movl    8(%ebp), %eax
     5     addl    $3, %eax
     6     popl    %ebp
     7     ret
     8 f:
     9     pushl   %ebp
    10     movl    %esp, %ebp
    11     subl    $4, %esp
    12     movl    8(%ebp), %eax
    13     movl    %eax, (%esp)
    14     call    g
    15     leave
    16     ret
    17 main:
    18     pushl   %ebp
    19     movl    %esp, %ebp
    20     subl    $4, %esp
    21     movl    $10, (%esp)
    22     call    f
    23     addl    $1, %eax
    24     leave
    25     ret

       接下来我们来分析一下改程序具体的流程。

      程序一开始,CPU的IP寄存器指向汇编代码的第18行,假设堆栈在内存中的地址分别为0,1,2,3……堆栈基指针寄存器(EBP)和堆栈顶指针寄存器(ESP)均指向堆栈段0处。

      第18~21行首先为main函数开辟新的内存区域,之后将传的参数10入栈,此时堆栈段如下所示:

      

      然后程序调用call 函数,将IP入栈,IP指向代码第9行f处。

      在f函数的代码处,首先为f函数开辟新的内存区域,接着将传入的参数10赋值给EAX,并将EAX入栈,此时堆栈段内存如下图:

      程序在此调用call进入g函数。在g函数中,同样先是开辟内存空间,然后将参数传给EAX,并将EAX的值加上3。

      之后将EBP出栈,并调用ret命令。此时IP重新指向f函数call之后的命令,堆栈内存的情况如下:

      

      之后就是不断的调用leave与ret命令,跳出当前的内存区域,回到上一级函数的内存区域中,并将EAX的值加3,直到跳出main函数,至此程序结束。

      从上面的分析中,我觉得可以归纳出以下几点:

      1.计算机的运行流程确是遵循冯诺依曼框架,CPU将内存中的代码和数据读取到自己的寄存器中,再根据一条条命令调用寄存器进行进一步的操作。

      2.在进入每一个程序之前,CPU都会将上一级的EIP和EBP压栈,相当于为新的函数重新开辟了一段新的内存空间,直到退出函数的时候才将它们出栈。与此同时,将函数的返回值保存在EAX中。

      3.CPU的各个寄存器都有不同的分工,如EIP指向要执行的代码,EAX存储返回值等。它们贯穿于整个程序执行流程,自己写程序时一般不要轻易改动。

      

  • 相关阅读:
    pipelinewise 学习二 创建一个简单的pipeline
    pipelinewise 学习一 docker方式安装
    Supercharging your ETL with Airflow and Singer
    ubuntu中使用 alien安装rpm包
    PipelineWise illustrates the power of Singer
    pipelinewise 基于singer 指南的的数据pipeline 工具
    关于singer elt 的几篇很不错的文章
    npkill 一个方便的npm 包清理工具
    kuma docker-compose 环境试用
    kuma 学习四 策略
  • 原文地址:https://www.cnblogs.com/wickedpriest/p/4315189.html
Copyright © 2011-2022 走看看