作者:r1ce
       原创作品转载请注明出处
      《Linux内核分析》 MOOC课程http://mooc.study.163.com/course/USTC-1000029000
 
       关于计算机是如何工作的,这是一个容易概括却难以详解的问题。大家非常清楚的冯诺依曼体系,以存储程序为最重要的特性,实际上就是CPU像一个大管家一样,通过种种方式在浩如烟海的内存中,找出需要执行的指令,和需要使用的数据。那么CPU如何区分指令和数据,如何知道确定指令执行的顺序呢?
       我们先从上至下来看计算机。普通用户使用计算机上的软件,软件是由程序员编写的,一般使用高级语言,如Python、C、Java等,这些语言易于人类理解、阅读和编写,但是计算机却不能直接识别。无论是Python还是C,前者需要通过解释器来执行,后者需要编译器编译为可执行文件。计算机最底层的实现是基于电路实现0和1的识别,这也是可执行文件的真貌——一大堆0和1的表示。那么高级语言到0和1之间,看起来好像隔着很大的一条鸿沟,于是汇编语言作为二者的中介,便显得十分重要了。向上而言,高级语言可以用汇编语言表示;向下而言,每一个汇编语言的指令都可以用二进制0和1表示,从而被计算机CPU识别。理解了汇编语言的操作过程,也就能够理解计算机究竟是如何工作的。
       汇编语言究竟是什么东西呢?想要理解汇编语言,要先理解计算机的组成。为了简化,只提CPU和内存。CPU是处理器,内存存放着指令和数据,处理器就像一个管家,从内存中取指令执行,对数据进行出来,并将数据储存起来。对于CPU来说,每一个程序的执行要解决三个问题:1. 待处理的数据在哪里;2. 如何处理数据;3. 处理好的数据放在哪里。为了解决这三个问题,CPU需要借助一些工具的帮助,这些工具就是各种寄存器。汇编语言实际上就是对这些寄存器进行处理,通俗点说,就是把一大堆数据在寄存器和内存倒腾过来倒腾过去,做一些复制和加加减减的运算。其实学习汇编语言很简单,只要记住十几条汇编指令和各种寄存器以及堆栈的用法就可以了。
       在这篇文章中,我们通过对一个简单的C程序反汇编得到汇编代码,分析汇编代码来了解计算机工作的基础。
       这段C程序是这样的:
 int a(int x)
{
return x + ;
} int b(int x)
{
return a(x);
} int main(void)
{
return b() - ;
}
       可以看到程序中有很多函数的调用和返回。为什么要这样设置呢?因为程序中的函数调用时计算机工作运行的关键,分析函数调用的具体实现能够帮助理解计算机运行的原理。
       我们将上述代码写入main.c文件中。然后使用
gcc -S -o main.s main.c -m32

命令生成汇编代码。结果如下图。后面加-m32是为了让其按照32位的方式反汇编。

我们只需要看汇编代码的关键部分,可以把点开头的语句全部删去,得到如下的汇编指令。

 a:

     pushl    %ebp
movl %esp, %ebp
movl (%ebp), %eax
addl $, %eax
popl %ebp
ret b: pushl %ebp
movl %esp, %ebp
subl $, %esp
movl (%ebp), %eax
movl %eax, (%esp)
call a
leave
ret main: pushl %ebp
movl %esp, %ebp
subl $, %esp
movl $, (%esp)
call b
subl $, %eax
leave
ret
       接下来我们分析C代码和汇编程序究竟是如何对应起来的,以及汇编语言是如何工作的。
       我们先看C程序,从main函数看起,它返回了一个函数b再进行运算的结果。那么我们来看函数b,它返回的是函数a的结果,而函数a的作用是将传递给它的参数x加5。所以对于这个程序,最后得到的数值应该是5+5-2=8。
       再来看汇编代码,我们还是从main函数看起,一条指令一条指令地分析。第n条表示指令执行的顺序,后面列出了代码的行号和执行的指令。
       第1条:23 pushl %ebp
       一看到push,我们就知道这是在对栈进行操作。ebp是栈顶指针,esp是栈当前位置指针,栈是自上向下生长的,后进先出。先把ebp压栈,实际上是先将esp-4再将ebp放到栈当前位置。
       第2条:24 movl %esp, %ebp
       将esp的值放到ebp中,也就是说现在ebp的指向改变为esp的指向。
       第3条:25 subl $4, %esp
       将esp-4。
       第4条:26 movl $5, (%esp)
       将5移入esp指向的地址中。
       第5条:27 call b
       调用函数b,这里等于两个操作,一个是先将现在的eip入栈,此时eip应为subl $2,%eax这条指令的位置,我们记为28。另一个操作是将b函数的地址放入eip,也就是说此时程序要从10开始执行。
       第6条:12 pushl %ebp
       第7条:13 movl %esp, %ebp
       第8条:14 subl $4, %esp
       此时已跳转到b函数,指令之前已经讲过了,与7、8条一起不再赘述。
       第9条:15 movl 8(%ebp), %eax
       movl 8(%ebp), %eax,是将ebp的值+8指向的内容放入eax,实际上就是eax = 5。
       第10条:16 movl %eax, (%esp)
       将eax的内容放入现在esp指向的内容中。
       第11条:17 call a
       调用函数a,与前面的步骤类似。
       第12条:3 pushl %ebp
       第13条:4 movl %esp, %ebp
       第14条:5 movl 8(%ebp), %eax
       是a函数的pushl %ebp,与13、14条一同省略。
       第15条:6 addl $5, %eax
       将eax中的值+5得到10。
       第16条:7 popl %ebp
       将现在esp指向的内容放入ebp,esp+4,所以现在ebp=4。
       第17条:8 ret
       是ret,即popl %eip,也就是现在的eip更改为18,回到函数b,从leave开始执行。
       第18条:18 leave
       leave,表示两条指令,movl %ebp,%esp和popl %ebp。
       第19条:19 ret
       ret回到main函数,从28处执行。
       第20条:28 subl $2, %eax
       将eax中的内容-2,即8。
       第21条:29 leave
       第22条:30 ret
       如图所示。从图中我们可以看到,栈又回到了初始的位置。

       至此,汇编代码就分析完了。
       从上面的过程可以看出,计算机最本质的工作原理,是对存储的数据进行处理,并把结果保存,然后不断循环这个处理数据的过程。指令就是对数据进行处理的依据。具体的方法就是借助CPU中的寄存器,以及内存中的栈,依据一个约定的步骤对数据进行操作。计算机其实很简单,它是一个认死理的家伙,只要确定了每一步要做什么,它就会严格地按照步骤把操作完成,绝对不打折扣。因此,相比与人打交道,与计算机打交道可是要轻松多了。

从C到汇编:栈是计算机工作的基础的更多相关文章

  1. linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作

    一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...

  2. 通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

    秦鼎涛  <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验一 通过汇编一个简单的C程序,分析汇编代码 ...

  3. 从C简单程序的汇编代码入手,以理解计算机工作原理。

    贺邦  原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000#/info 知识准备 ...

  4. 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

    实验一:通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的 学号:20135114 姓名:王朝宪 注: 原创作品转载请注明出处   <Linux内核分析>MOOC课程http: ...

  5. Linux内核设计(第一周)——从汇编语言出发理解计算机工作原理

    Linux内核设计(第一周)——从汇编语言出发理解计算机工作原理 计算机工作原理 汇编指令 C语言代码汇编分析 by苏正生 原创作品转载请注明出处 <Linux内核分析>MOOC课程htt ...

  6. Linux内核设计第一周 ——从汇编语言出发理解计算机工作原理

    Linux内核设计第一周 ——从汇编语言出发理解计算机工作原理 作者:宋宸宁(20135315) 一.实验过程 图1 编写songchenning5315.c文件 图2 将c文件汇编成32位机器语言 ...

  7. Linux内核分析第一周学习博客 --- 通过反汇编方式学习计算机工作过程

    Linux内核分析第一周学习博客 通过反汇编方式学习计算机工作过程 总结: 通过这次对一个简单C程序的反汇编学习,我了解到计算机在实际工作工程中要涉及大量的跳转指针操作.计算机通常是顺序执行一条一条的 ...

  8. Linux操作系统工作的基础

    简介: 本文根据 Linux™ 系统工作基础的分析,对存储程序计算机.堆栈(函数调用堆栈)机制和中断机制进行概述.文中将为您提供操作系统(内核)如何工作的细节,进一步从宏观概述结合关键点进行微观(CS ...

  9. 浅析Linux操作系统工作的基础

    环境:lubuntu 13.04   kernel 3.9.7 作者:SA12226265 katao 简介: 本文根据 Linux™ 系统工作基础的分析,对存储程序计算机.堆栈(函数调用堆栈)机制和 ...

随机推荐

  1. jquery元素查找方法集锦

    jQuery常用的元素查找方法总结 $("#myELement")    选择id值等于myElement的元素,id值不能重复在文档中只能有一个id值是myElement所以得到 ...

  2. ubuntu更新源

    源一定要找对应的版本 14.04对应 trusty deb http://mirrors.163.com/ubuntu/ trusty main restricted universe multive ...

  3. TatukGIS-TGIS_ShapeArc.GetPointOnLine

    function GetPointOnLine(const _distance: Double; const _offset: Double; const _part: Integer): TGIS_ ...

  4. bzoj3047: Freda的传呼机 && 2125: 最短路

    Description 为了随时与rainbow快速交流,Freda制造了两部传呼机.Freda和rainbow所在的地方有N座房屋.M条双向光缆.每条光缆连接两座房屋,传呼机发出的信号只能沿着光缆传 ...

  5. 转 四大Java EE容器(Tomcat、JBoss、Resin、Glassfish)之简单比较

                                      现在流行的Java EE容器有很多:Tomcat.JBoss.Resin.Glassfish等等.下面对这四种Java EE容器进行 ...

  6. Android 5.0 技术新趋势

    由于 Android 的版本分裂比较严重,整个新系统升级可能需要一两年甚至更长时间.所以目前使用 Android 5.0 的大部分是喜欢尝鲜的用户,同时现在市场上能够很好支持 Android 5.0 ...

  7. bcb6 中安装 delphi7 的控件包

    其实只要新建一个包,然后把 d7 中的包包含的 pas 文件也包含进去就行了. 同时它会把对应的 bpi 文件也自动包含. 对于 bcb6 中没有的 PosEx 函数可以这样处理,把 d7 中的实现复 ...

  8. Spring的OpenEntityManagerInViewFilter

    在Java Web项目中使用Hibernate经常会遇到LazyInitializationException .这是因为controller和model层(java代码)将通过JPA的一些启用了延迟 ...

  9. Java native(转)

    Java不是完美的,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能. 可以将 ...

  10. Poetize6: IncDec Sequence

    3043: IncDec Sequence Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 233  Solved: 132[Submit][Statu ...