《Linux内核原理与分析》第二周作业

这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资料,看看别人的解答,慢慢的也就理解了,最终形成自己的知识脉络。

实验分析

先创建文件,通过vim将C代码写到文件中去,如图。

再编译成可执行程序和反编译成汇编代码。为什么反编译是这个代码呢?

gcc -S -o main.s main.c

原来gcc命令中 -S 参数表示仅仅汇编而不进行编译及链接,也就是将源代码翻译成汇编指令。-o filename 指明输出文件名。这里表示将生成的汇编代码保存到main.s中去。汇编并整理之后的代码如图所示:

这个代码跟我之前学的汇编代码有稍许的不一样,查了一下原因,才知道之前那个是Windows平台下的汇编代码,这个是linux下的汇编代码,大同小异,稍微查一下资料并仔细阅读还是能读懂,比如%表示寄存器里面的内容。本函数只有一个参数,所以在汇编上体现出来的就是直接的一个入栈操作,那么如果是两个或者以上的参数呢,应该先压栈哪个?带着这样的疑问我又去查资料,得知,参数的入栈顺序时候从右往左的,也就是最右边的参数是最先入栈的。为何参数入栈的顺序是从右往左而不是从左往右呢?

要回答这个问题,就不得不谈一谈printf()函数,printf函数的原型是:printf(const char* format,…),没错,它是一个不定参函数,那么我们在实际使用中是怎么样知道它的参数个数呢?这就要靠format了,编译器通过format中的%占位符的个数来确定参数的个数。现在我们假设参数的压栈顺序是从左到右的,这时,函数调用的时候,format最先进栈,之后是各个参数进栈,最后pc进栈,此时,由于format先进栈了,上面压着未知个数的参数,想要知道参数的个数,必须找到format,而要找到format,必须要知道参数的个数,这样就陷入了一个无法求解的死循环了!!

而如果把参数从右到左压栈,情况又是怎么样的?函数调用时,先把若干个参数都压入栈中,再压format,最后压返回地址,压EBP,这样一来,栈顶指针加2便找到了format,通过format中的%占位符,取得后面参数的个数,从而正确取得所有参数。

下面开始代码的具体分析:

首先从main函数开始,因为在main函数开始前,程序已经做了一堆准备工作,所以指令肯定不是从这才开始的,前面已经执行了很多的指令,所以先将原先的ebp压栈,ebp压栈之后,esp-=4,并在esp新指向的内存地址存入ebp,形成一个调用main函数产生的新的相对的栈,然后ebp指向当前栈顶指针esp所指向的位置。

subl $4,%esp
movl $10,(%esp)

表示将esp自减4,即向下移动四个字节,然后将立即数10放入到esp目前所保存的地址中的内存单元中去,这两行代码亦可以用

pushl $10

来代替。接下来要调用f函数了,本来不调用f的话,程序会按照顺序执行下去,也就是执行addl $3,%eax这条命令,当调用f的时候,程序便更改了EIP的值,改为跳转到f的地址,所以在这之前,要将addl $3,%eax这条语句的地址也就是返回地址压栈,然后进入f函数中去。

进入f函数之后同样会形成一个新的相对的栈,所以首先是将ebp压栈,保存之前的ebp的值,ebp再被esp赋值之后,再指向当前的栈顶。这个时候esp-4,即下移一个内存单元,movl 8(%ebp),eax这句命令的意思是将当前ebp所指向的内存地址+8之后的内存单元所存的内容取出来放到eax中去,那么当前ebp+8之后是什么呢。由之前的分析可得到,当前ebp+4是得到f函数的返回地址,那么+8之后便是立即数10,所以这一步是将10取出来放到eax中去。由于刚刚栈顶指针esp只是做下移操作,并没有存放任何东西,所以这里有一句movl %eax,(%esp),将eax中的值即10放到当前esp所指向的内存单元中去,然后调用g函数。同样,先将g函数的返回地址压栈,形成一个新的相对的栈之后,ebp压栈(esp-4并存放ebp),ebp指向esp当前所指向的位置。这里又有一句movl 8(%ebp),eax,一样的,取出上面第二个单元格中的10出来放到eax中,eax再+5,再把当前的栈顶指针所指向的内存单元的值(即之前存放的ebp的值)取出来放到ebp中去,再返回,到函数之前的f中应该执行的位置(返回其实也对应着一个将EIP压栈,改变EIP值的操作,这里一般省略了),这里对g函数的分析就对应g函数中的c语言代码:return x+5;同样g调用完了返回到f函数之后,f也开始准备清理工作然后返回了(leave指令相当于一直清栈的指令,不过当然清当前这个相对的栈),最后回到主函数,将EAX中的值+3,由于之前的指令之后的eax中保存的是15,所以加3之后eax是18了,整个main函数也就差不多分析结束了,剩下的就是程序自己之后的一些工作了。

遇到的问题及解决方案

  1. 关于堆栈的问题:对计算机中的堆栈的知识感到陌生,这一块完全需要自己去补补。

    通过上网查询资料,了解到了,栈的生长跟内存地址的生长刚好相反,栈是由内存高地址向内存低地址的方向生长的,所以每进行一个入栈操作的时候,栈顶指针所指向的内存地址自减4(为什么是自减4呢?因为一个内存单元是四个字节,所以当进行一个压栈操作的时候,就把指针自减4个字节,然后再把数据放进去)。
  2. 关于各种寄存器的问题:梳理一遍比较重要的几个寄存器的作用

    以32位机为例,cpu中有一个很关键的寄存器叫EIP,它保存的是下一条待执行指令的地址,然后从相应的内存地址中找到指令并执行,再指向下一个内存地址。EIP可以被call ret jump 条件跳转等间接修改,但不能被程序员直接修改。除此之外还有EAX,EBX,ECX,EDX,ESP,EBP。关于ESP,EBP一直都没有理解的很透彻,不知道这两个寄存器各自的作用,通过这一次清楚的知道了,每当发生一个函数(包括main函数)调用的时候,都会产生一个新的相对的栈(在这之前会把返回地址压栈,原先的EBP压栈),然后这个新的相对的栈中,EBP指向栈底,ESP指向栈顶,当一个东西要进行入栈操作,ESP先自减4,再把要入栈的东西放到以当前ESP所指向的地址为其实的内存单元中去。下面一段代码通过自己的理解给出注释
//假设执行函数前堆栈指针ESP为NN
push para2 ;参数2入栈, ESP -= 4h , ESP = NN - 4h
push para1 ;参数1入栈, ESP -= 4h , ESP = NN - 8h
call test ;压入返回地址 ESP -= 4h, ESP = NN - 0Ch
;//进入test函数内
test
{
push ebp ;保护先前EBP指针,EBP入栈,ESP-=4h,ESP=NN - 10h
mov ebp, esp ;设置EBP指针指向栈顶 NN-10h
mov eax, dword ptr [ebp+0ch] ;ebp+0ch为NN-4h,即参数2的位置
mov ebx, dword ptr [ebp+08h] ;ebp+08h为NN-8h,即参数1的位置
sub esp, 8 ;局部变量所占空间ESP-=8, ESP = NN-18h
...
add esp, 8 ;释放局部变量, ESP+=8, ESP = NN-10h
pop ebp ;出栈,恢复EBP, ESP+=4, ESP = NN-0Ch
ret 8 ;ret返回,弹出返回地址,ESP+=4, ESP=NN-08h, 后面加操作数8为平衡堆栈,ESP+=8,ESP=NN, 恢复进入函数前的堆栈.
}

所以,ESP就是一直指向栈顶的指针,而EBP只是存取某时刻的栈顶指针,以方便对栈的操作,如获取函数参数、局部变量等。

3. 书上讲的内核压缩默认以gzip和bzip2的形式发布,但是我下载下来的却是以tar.xz为后缀名的压缩包(可能是书比较老的缘故吧),于是上网查询这形式的压缩包怎么解压,但是既然学到这,就得去了解.xz是怎么产生的。于是深入的了解了下,xz 是一个使用 LZMA压缩算法的无损数据压缩文件格式。一般可以使用tar -xvf filename.tar.xz来解压。

总结

通过本章的学习,我理解了很多关于计算机工作原理的知识。以前一直不知道计算机怎么运作起来的,CPU,内存他们有什么联系。现在知道了,CPU中所执行的每一条指令都是从内存(CPU和内存之间通过总线连接)中读取的,而内存又可以看成是由一连串连续的地址的储存单元组成的,每个单元要么存放数据要么存放指令,所以计算机就反复通过cpu读取指令,执行指令,指向下一个内存地址的模式让我们computer运行起来啦。

20169212《Linux内核原理与分析》第二周作业的更多相关文章

  1. 2019-2020-1 20199303<Linux内核原理与分析>第二周作业

    2019-2020-1 20199303第二周作业 1.汇编与寄存器的学习 寄存器是中央处理器内的组成部份.寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令.数据和位址.在中央处理器的控制部件中 ...

  2. 20169219 linux内核原理与分析第二周作业

    "linux内核分析"的第一讲主要讲了计算机的体系结构,和各寄存器之间对数据的处理过程. 通用寄存器 AX:累加器 BX:基地址寄存器 CX:计数寄存器 DX:数据寄存器 BP:堆 ...

  3. 2019-2020-1 20199314 <Linux内核原理与分析>第二周作业

    1.基础学习内容 1.1 冯诺依曼体系结构 计算机由控制器.运算器.存储器.输入设备.输出设备五部分组成. 1.1.1 冯诺依曼计算机特点 (1)采用存储程序方式,指令和数据不加区别混合存储在同一个存 ...

  4. Linux内核原理与分析-第二周作业

    写之前回看了一遍秒速五厘米:如果

  5. Linux内核原理与分析-第一周作业

    本科期间,学校开设过linux相关的课程,当时的学习方式主要以课堂听授为主.虽然老师也提供了相关的学习教材跟参考材料,但是整体学下来感觉收获并不是太大,现在回想起来,主要还是由于自己课下没有及时动手实 ...

  6. 2019-2020-1 20199314 <Linux内核原理与分析>第一周作业

    前言 本周对实验楼的Linux基础入门进行了学习,目前学习到实验九完成到挑战二. 学习和实验内容 快速学习了Linux系统的发展历程及其简介,学习了下的变量.用户权限管理.文件打包及压缩.常用命令的和 ...

  7. 2018-2019-1 20189221《Linux内核原理与分析》第一周作业

    Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...

  8. 2020-2021-1 20209307 《Linux内核原理与分析》第九周作业

    这个作业属于哪个课程 <2020-2021-1Linux内核原理与分析)> 这个作业要求在哪里 <2020-2021-1Linux内核原理与分析第九周作业> 这个作业的目标 & ...

  9. 20169210《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...

随机推荐

  1. cellspacing,cellpadding什么区别

    cellspacing设置为“0”,显示的结果就是第一个表格的每个单元格之间的距离为0.若将表格边框设为“0”,则单元格 的距离就是0了cellpadding属性用来指定单元格内容与单元格边界之间的空 ...

  2. 基于eBox的LTC1446驱动

    LTC1446 是linear出品的双通道12bit轨对轨DAC芯片,采用SPI接口,内部基准电压,满量程输出4.095v,单电源供电(4.5-5v).8Pin封装.            使用时非常 ...

  3. CSS图片列表

    1.效果图: 2.Example Source Code <h3><a href="http://www.52css.com/">我爱CSS画廊</a ...

  4. python核心编程学习记录之函数与函数式编程

    @func function 意思是func(function) @func(a) function 意思是func(a)这是个函数对象,在去调用function函数 如果要传额外的值,只传值用*tu ...

  5. Java虚拟机学习(3): 类加载机制

    类加载机制 JVM把class文件加载的内存,并对数据进行校验.转换解析和初始化,最终形成JVM可以直接使用的Java类型的过程就是加载机制. 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命 ...

  6. SUDTOJ 3323园艺问题 (线段树)

    园艺问题 Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目描述 本巨养了一盆双色茉莉.这种花有一种特点:第i朵花在第Di天盛开,刚开时是紫色的 ...

  7. Unity中Collider和刚体Collider性能对比

    测试方式: 每个对象做大范围正弦移动,创建1000-5000个对象,保证场景分割树的实时更新,并测试帧率 测试脚本: 移动脚本: using UnityEngine; using System.Col ...

  8. 为什么要在html和body加上“height:100%;”

    元素中有内容的时候div才能被撑起来所以我给div加了背景但是也不显示,就是因为没有内容,这个时候的解决办法就是 html,body{ height:100%; }

  9. OS X 下iso刻录U盘

    1. 查看盘 $diskutil list /dev/disk0 #: TYPE NAME SIZE IDENTIFIER : GUID_partition_scheme *320.1 GB disk ...

  10. GMF中,删除节点和连线的另一种实现

    问题 在GMF中,如果需要programmatically删除节点或连线,在google中我们很容易搜索到<GMF中,删除节点和连线的实现>一文(我并不确定这是原创作者的原始链接),很多人 ...