作者: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程序是这样的:
  1. int a(int x)
  2. {
  3. return x + ;
  4. }
  5.  
  6. int b(int x)
  7. {
  8. return a(x);
  9. }
  10.  
  11. int main(void)
  12. {
  13. return b() - ;
  14. }
       可以看到程序中有很多函数的调用和返回。为什么要这样设置呢?因为程序中的函数调用时计算机工作运行的关键,分析函数调用的具体实现能够帮助理解计算机运行的原理。
       我们将上述代码写入main.c文件中。然后使用
  1. gcc -S -o main.s main.c -m32

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

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

  1. a:
  2.  
  3. pushl %ebp
  4. movl %esp, %ebp
  5. movl (%ebp), %eax
  6. addl $, %eax
  7. popl %ebp
  8. ret
  9.  
  10. b:
  11.  
  12. pushl %ebp
  13. movl %esp, %ebp
  14. subl $, %esp
  15. movl (%ebp), %eax
  16. movl %eax, (%esp)
  17. call a
  18. leave
  19. ret
  20.  
  21. main:
  22.  
  23. pushl %ebp
  24. movl %esp, %ebp
  25. subl $, %esp
  26. movl $, (%esp)
  27. call b
  28. subl $, %eax
  29. leave
  30. 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. nav

    $(document).ready(function() { $(window).resize(function(){ var need=0; var ul_max_width = $(window) ...

  2. C# 进销存系统开发框架

    C/S系统开发框架-企业版 V4.0 (Enterprise Edition) 简介: http://www.csframework.com/cs-framework-4.0.htm 视频下载: 百度 ...

  3. libcurl的封装,支持同步异步请求,支持多线程下载,支持https

    最近在做一个项目,需要用到http get post等 需求分析需要做到同步和异步,异步请求的返回以可选的回调通知的方式进行. 本人以Linux为例,一步一步的来实现. 配置并且编译libcurl我以 ...

  4. My ECMAScript 7 wishlist

    With ECMAScript 6 now feature complete, any further changes to the core of JavaScript will happen in ...

  5. Corn Fields

    poj3254:http://poj.org/problem?id=3254 题意:给以n*m的方格,方格中有1或者0,在1的地方可以放置一个物品,但是在物品的上下左右不能有不物品,也可以不放,问你最 ...

  6. Agri-Net poj 1258

    WA了好多次,注意语言和数据范围 Description Farmer John has been elected mayor of his town! One of his campaign pro ...

  7. Linux Shell编程(9)——特殊变量类型

    局部变量局部变量只在代码块或一个函数里有效 (参考函数里的局部变量)环境变量这种变量会影响Shell的行为和用户接口 在大多数情况下,每个进程都会有一个"环境表", 它由一组由进程 ...

  8. 【最短路】Vijos P1022Victoria的舞会2

    题目链接: https://vijos.org/p/1022 题目大意: 给一张N个点的有向图,求有几块强连通分量.(N<=200) 题目思路: [动态规划] n比较小,可以用floyd暴力把每 ...

  9. openssl生成RSA格式,并转为pkcs8格式

    原文地址:http://www.thinkingquest.net/articles/391.html?utm_source=tuicool 支付宝接口开发相关:openssl 加密工具 支付宝“手机 ...

  10. 简单约瑟夫环的循环单链表实现(C++)

    刚刚接触C++以及数据结构,今天做了第一次尝试用C++和数据结构解决问题,问题是基于约瑟夫环问题的简单版. 先来看看约瑟夫环问题的介绍: 约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3.. ...