作者: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. [前端笔记]第三篇:JavaScript

    JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的处理. 一.代码存放位置 J ...

  2. Day10 网络编程(续)

    作用域 Python的作用域是函数,没有块级作用域 if 1 == 1: name = 'wang' print(name) #wang   for i in range(10): name = i ...

  3. openshif ssh proxy

    最近google又被墙了.没办法 1:注册一个openshift账号.申请注册一个app,获取一个免费主机.   https://www.openshift.com/ 2:去PuTTY官方网站下载pL ...

  4. Python Tutorial 学习(八)--Errors and Exceptions

    Python Tutorial 学习(八)--Errors and Exceptions恢复 Errors and Exceptions 错误与异常 此前,我们还没有开始着眼于错误信息.不过如果你是一 ...

  5. 获取工程的exe文件的所在目录

    Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName; 例如结果为:           C:\Documents and ...

  6. uboot的devices_init函数分析

    一.函数说明 函数功能: 完成设备的初始化 函数位置: common/devices.c 二.程序分析 int devices_init (void) { #ifndef CONFIG_ARM /* ...

  7. gcc链接g++编译生成的静态库和动态库的makefile示例

    使用c++开发程序或者库时,将库提供给其他人使用. 然而使用者是使用c开发的程序,链接g++编译生成的库时,于链接gcc生成的库,有所不同. 首先是静态库,以链接g++编译生成的libmylib.a为 ...

  8. Node.js Web模块

    什么是Web服务器? Web服务器是处理由HTTP客户端发送的,如web浏览器的HTTP请求的软件应用程序,并返回响应于客户端网页. Web服务器通常伴随着图片,样式表和脚本的HTML文档. 大多数W ...

  9. Unity3d 读取网络xml

    Unity3d 读取网络xml Unity3d 读取网络xml,这个xml文件需要不包含BOM信息,可以用UltraEdit打开xml文件,并且另存为的时候,选择不包含BOM的utf-8格式存储!

  10. hashCode() 和equals() 区别和作用

    HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键. 那么Java运行时环境是如何判断HashSet中相同对象.Ha ...