Linux内核设计(第一周)——从汇编语言出发理解计算机工作原理
Linux内核设计(第一周)——从汇编语言出发理解计算机工作原理
从2月22日起,本学期的linux课程开始了。通过这两天的学习,觉得孟宁老师讲的真不错,条理清晰,举例适当。本周从计算机工作原理出发,回顾了冯诺依曼计算机结构,也回顾了汇编寄存器、汇编指令、C语言程序的汇编分析技巧,很是受用。
一.知识点回顾
1.冯诺依曼理论的要点是:数字计算机的数制采用二进制;计算机应该按照程序顺序执行。
2.以Intel 8086和8088为例有十四个16位寄存器,比如AX, BX, CX, DX到了32位处理器时代,相对于16位处理器进行了扩展,在16位的寄存器基础上加上E前缀,比如AX变成了EAX,在后来,AMD出了64位处理器,采用的R前缀。
3.汇编指令在32位机器中都以l结尾,AT&T格式的汇编指令是“源操作数在前,目的操作数在后”,而intel格式是反过来的,即如下:
AT&T格式:movl %eax, %edx
Intel格式:mov edx, eax
二、实验过程
1.在实验楼Linux系统实验平台编写c代码:
int g(int x)
{
return x + 3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 1;
}

2.反编译
在实验楼平台下,使用 gcc -S -o main.s main.c -m32将它反汇编成main.s。注意,我们所用的实验平台是X86-64的操作系统,所以为了产生32位的汇编代码,我使用了-m32选项让它生成32位汇编指令
生成如下汇编代码:
.file "main.c"
.text
.globl g
.type g, @function
g:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 8(%ebp), %eax
addl $3, %eax
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size g, .-g
.globl f
.type f, @function
f:
.LFB1:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1:
.size f, .-f
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $4, %esp
movl $8, (%esp)
call f
addl $1, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits

代码中有许多以.开头的代码行,属于链接时候的辅助信息,在实际中不会执行,把它删除,得到下列的代码就是纯汇编代码了:
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $3, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $8, (%esp)
call f
addl $1, %eax
leave
ret

三.汇编代码分析
经过观察,我们可以看出,每一个函数基本上都有一个几乎相同的汇编格式:
函数名:
pushl %ebp
movl %esp, %ebp
+函数中间过程
leave(或者popl %ebp)
ret
【注意】leave和下面代码等价
movl %ebp, %esp
popl %ebp
enter和下面代码等价
pushl %ebp
movl %esp, %ebp
1.函数执行
通过查阅资料,我们知道在计算机内部执行代码的时候,每当调用一个函数的时候,函数总是先把当前的栈底指针压入堆栈,然后把栈底指针移动到当前的栈顶,这样子做,相当于在旧的栈上新起了一个栈。然后在新栈上执行函数。
当函数执行结束的时候,如果堆栈有变化,我们可以用movl %ebp,%esp来恢复堆栈。如果函数结束后,堆栈没有变化,那么这句话就可以不要。
函数调用结束后,就要使用ret返回到调用它的函数。同时,我们还需要回复栈底指针,以便于函数返回值的传递。于是popl %ebp。通常相对叫简洁的汇编代码中,会用leave来代替刚才所用的两句话。
2.函数调用
函数执行一定得是有函数调用了。
call f
addl $4, %esp
这是调用f函数的过程。
等到ret后,返回了现在的call的下一行汇编代码。这时候,esp和ebp是一个值,所以这以后如果压栈的时候,会覆盖了栈底指针,把esp往栈顶上移动1个单位也就是4个字节,这时候就完美解决了调用后的问题,才是真正调用完成了。
3.函数参数取得
这时候,得回头看一下f函数了。这时候,我们发现它用了
pushl 8(%ebp)
call g
addl $4, %esp
它把增加了8个字节的地址压栈了,然后调用了g函数。
分析一下为什么是8个字节,我们可以用sizeof关键字来测试得到int占4个字节……所以,它却加了8个字节取值,那么必然是有什么怪东西又入栈了。pushl %ebp是每次函数执行的时候使用的,就是ebp寄存器还占用了4个字节,如果是32位芯片,寄存器(32位=8位/字节\times 4字节)。
所以,又发现了ebp寄存器的一个好处,能够让我们方便取得函数的参数……否则后面再去参数,栈位置变了好多,就不方便了。
4.图解
(1)main函数

此时ebp入栈,将esp的值赋给ebp,相当于一个旧的栈底指针。
然后esp-4,在栈中向下一个空格,然后在此位置放入8.

然后此时调用f函数。call F ,函数调用指令,首先把当前eip的值[当前eip指向第四条指令,即movl $8, %esp]入栈,然后跳转到F函数的第一条指令开始执行。
此时栈中的情况如下如所示:

(2)f函数
这里前条指令和main函数的头两条指令作用相同,保存当前栈环境,为F函数开辟新的栈空间。然后将esp的值减4,跳到下一格。
pushl 8(%ebp),该指令把当前ebp中的数值加8后作为内存地址,并把该内存地址指向的内存空间内的数值"8"放入栈中。其实就是把调用函数是传入的参数入栈。
然后将eax中的值——8传给当前esp位置,相当于返回值。
此时,调用函数g。call g,函数调用指令,当前eip入栈后,跳转到G函数的第一条指令执行

(3)g函数
g函数和之前的f函数基本一致。
popl %ebp,从栈中获取旧的esp值,并放入ebp寄存器。[这里之所以没有再加上一条movl %ebp, %esp是因为函数中esp的值并没有改变,依然指向存放旧esp值的内存空间]
ret 等价于pop eip,从当前栈顶,即esp所指内存处获取值,作为eip,然后跳转到eip中存放的地址继续执行。
到这里,函数G已经返回,其返回值存储在eax寄存器中,即返回值为11

(4)返回到函数F中

(4)返回到main中
1 ...
2 leave
3 ret
leave,等价于 如下两条指令
movl %ebp, %esp
pop %ebp
即函数结语,释放F函数使用的栈空间,此时栈中情况如图:

再接着是ret指令,该指令执行后,函数F返回,程序回到main函数继续执行
此时eax中存放的是函数exF的返回值,即11
回到main函数继续执行
1 ...
2 addl $1, %eax
3 leave
4 ret
addl $1, %eax 此时eax中的值是main函数调用函数F的得到的返回值,即11,本条指令将eax中的值加2后放回eax,执行后eax中的值为12
ret main函数返回
总结
通过本次试验,我们对于计算机对于程序的执行有了一些新的认识。计算机每次都是各种取指针执行,在程序中各种跳转。在函数执行前要enter,函数执行后要leave(如果没有改变esp就可以省去把ebp赋值给esp的步骤了),ret函数取值可以靠ebp很方便做到,函数调用结束后要记住恢复堆栈指针(esp)。当然还有很多不足,有一些问题都是通过查阅互联网来分析和理解的,难免有不正确的地方,希望之后和老师沟通和确认,也希望大家来指正。
参考资料:
1.七种寻址方式(直接寻址方式) - 李龙江 - 博客园
http://www.cnblogs.com/lilongjiang/archive/2011/06/14/2080551.html
2.汇编基础知识 - [C/C++] - 杨德龙的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/yangdelong/article/details/2594660
3.Linux内核分析 - 网易云课堂
http://mooc.study.163.com/learn/USTC-1000029000?tid=2001214000#/learn/content
Linux内核设计(第一周)——从汇编语言出发理解计算机工作原理的更多相关文章
- Linux内核设计第一周 ——从汇编语言出发理解计算机工作原理
Linux内核设计第一周 ——从汇编语言出发理解计算机工作原理 作者:宋宸宁(20135315) 一.实验过程 图1 编写songchenning5315.c文件 图2 将c文件汇编成32位机器语言 ...
- Linux内核设计第一周学习总结 计算机如何工作
北京电子科技学院 20135310陈巧然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-100002 ...
- Linux内核分析第一周学习博客 --- 通过反汇编方式学习计算机工作过程
Linux内核分析第一周学习博客 通过反汇编方式学习计算机工作过程 总结: 通过这次对一个简单C程序的反汇编学习,我了解到计算机在实际工作工程中要涉及大量的跳转指针操作.计算机通常是顺序执行一条一条的 ...
- Linux内核设计第二周——操作系统工作原理
Linux内核设计第二周 ——操作系统工作原理 作者:宋宸宁(20135315) 一.实验过程 图1 执行效果 从图中可以看出,每执行my_ start_ kernel函数两次或一次,my_ time ...
- LINUX内核分析第一周学习总结——计算机是如何工作的
LINUX内核分析第一周学习总结——计算机是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/ ...
- linux内核分析第一周学习笔记
linux内核分析第一周学习笔记 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.co ...
- LINUX内核分析第二周学习总结——操作系统是如何工作的
LINUX内核分析第二周学习总结——操作系统是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...
- Linux内核分析——第一周学习笔记20135308
第一周 计算机是如何工作的 第一节 存储程序计算机工作模型 1.冯·诺依曼结构模型:冯·诺依曼结构也称普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构.程序指令存储地址和数据存储 ...
- 20135320赵瀚青LINUX内核分析第一周学习笔记
赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.概述 第一周的学习内容主 ...
随机推荐
- ConstraintLayout使用手册
1. 解决痛点 主要用拖拽 解决嵌套过多 2. 简易使用手册 增加约束 四个角直接拖拽就好了 删除约束 match_constraint 属性 这个属性类似于match_parent,去掉margin ...
- Mac上Hive环境搭建
本文介绍在Mac上搭建Hive环境. 建议首先配置好Hadoop,搭建与配置可以参考我之前的博文Mac Hadoop的安装与配置. 当然你也可以选择使用Docker搭建环境,本文不作介绍. 安装 对于 ...
- javascript实现百度地图鼠标滑动事件显示、隐藏
其实现思路是给label设置样式,我们来看下具体做法吧 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var label = new BMap.Labe ...
- PhpStorm破解版及使用教程
本文引自网络,仅供本人学习使用之用,感谢网友的分享 PhpStorm PhpStorm 是 JetBrains 公司开发的一款商业的 PHP 集成开发工具,旨在提高用户效率,可深刻理解用户的编码,提 ...
- Flume学习之路 (一)Flume的基础介绍
一.背景 Hadoop业务的整体开发流程: 从Hadoop的业务开发流程图中可以看出,在大数据的业务处理过程中,对于数据的采集是十分重要的一步,也是不可避免的一步. 许多公司的平台每天会产生大量的日志 ...
- Odoo作为App后端时如何调试App
转载请注明原文地址:https://www.cnblogs.com/cnodoo/p/9307340.html 一:Odoo可以作为app后台+后台管理系统使用 Odoo作为一个可供二次开发的框架, ...
- 爬虫header和cookie
def on_start(self): self.crawl('http://bbs.byr.cn/board/Python', headers={'X-Requested-With': 'XMLHt ...
- monitorix(linux)系统和网络监控公工具
一.monitorix Monitorix是一款功能非常强大的免费开源轻型工具,目的在于监测Linux中的系统和网络资源.它可以定期收集系统和网络数据,并使用自己的Web界面,通过图形显示相关信息.M ...
- 搞个组装机:D
时间:2016年7月 主机:就是主机 整机:主机+显示器 推荐:自己组装,淘宝或者京东,或者去淘宝上的宁美国度.攀升兄弟看看. 4000多块配个电脑: 处理器:i5 4590 散片(发热量小) 111 ...
- iOS9中http不能使用的解决
用xcode7写程序的时候发现webview不能显示http的链接网页,发现原来是由于ios9的一个新特性,iOS9引入了新特性App Transport Security (ATS),新特性要求Ap ...