教材中的内容及学习

这次学习的内容是教材第三章的内容,第三章的内容是程序的机器化表示。

历史观点

ntel处理器的换代:8086——80286——i386——i486——Pentium——PentiumPro——PentiumII——PentiumIII——Pentium4——Pentium4E——Core2——Corei7。

这些所有的代都是Intel系列的,Intel系列本身有很多名字,比如x86,比如IA32,Intel系列中64位扩展称为x86-64。最常用的是x86。也就是说x86就是Intel每一代处理器的统称。

8086和80286的存储器模型都已经过时了,从i386开始提供的平坦寻址模式也是linux使用的模式,这是将存储空间看做一个大的字节数组。

看来i386是一个转折点,从这里开始系统扩展为32位,同时,GCC为32位执行的默认调用仍然假设是为i386机器产生的代码,Intel系列,x86,是向后兼容的,所以i386的可以执行的代码,后面的都可以执行。

ntel处理器系列俗称x86,开始时是第一代单芯片、16位微处理器之一。

每个后继处理器的设计都是后向兼容的——较早版本上编译的代码可以在较新的处理器上运行。
X86 寻址方式经历三代:

  1. DOS时代的平坦模式,不区分用户空间和内核空间,很不安全
  2. 8086的分段模式
  3. IA32的带保护模式的平坦模式

程序编码

这一小节主要是对一些命令进行阐述。
gcc -O1 -s xxx.c 输出 汇编代码

gcc -O1 -c xxx.c 输出 目标代码

objdump -d xxx.o 反汇编机器代码(包括目标代码和可执行代码,两者的区别在于偏移地址)

gdb 可以直接对机器代码使用(包括目标代码和可执行代码)

gcc -s产生的汇编代码中,所有以点开头的行都是用于指导汇编器和链接器的。

gcc和objdump产生的汇编代码是ATT风格,微机原理里面学习的是Intel风格。有所不同,但本质没有改变。

机器级代码

1.程序计数器
2.整数寄存器
3.条件码寄存器
4.浮点寄存器

代码示例

int accum = 0;
 
int sum(int x, int y)
{
     int t = x+y;
     accum+=t;
     return t;
}

在命令行上使用 “-S”选项

unix〉gcc -02 -S code.c

就能看到编译器产生的汇编代码:

sum:
       pushl %ebp
       movl %esp , %ebp
       movl 12(%ebp) , %eax
       addl 8(%ebp) , %eax
       addl %eax , accum
       movl %ebp , %esp
       popl %ebp
       ret

如果对code.c 使用“-c”选项则会产生一个二进制的文件code.o

unix〉gcc -02 -c code.c

数据格式

访问信息

这是IA32中央处理器所包含的一组八个存储单元的32位存储器。前六个是通用寄存器,对它们的使用没有限制。前三个寄存器(%eax,%ecx,%edx)的保存和恢复惯例不同于接下来的三个寄存器(%ebx,%esi,%edi)。最后两个寄存器保存着指向程序栈重要位置的指针,称为栈指针和帧指针。数据存放在寄存器中进行加减乘除等一些操作。原来的寄存器是16位的所以如图所示蓝色部分是0-15,之后寄存器进行了扩充,变成了32位的即0-31。

(1) 操作数指示符

操作数被分成三种类型:立即数、寄存器、存储器引用。还有不同的寻址模式。
其次还有常见的汇编程序的数据传送指令

示例代码

int swap_add(int *xp, int *yp)
{
    int x = *xp;
    int y = *yp;
    *xp = y;
    *yp = x;
    return x+y;
}
 
int caller()
{
    int arg1 = 534;
    int arg2 = 1057;
    int sum = swap_add(&arg1, &arg2);
    return sum;
}

汇编代码为:

swap_add:
.LFB0:
    .cfi_startproc
    pushl   %ebx
    .cfi_def_cfa_offset 8
    .cfi_offset 3, -8
    movl    8(%esp), %ebx
    movl    12(%esp), %ecx
    movl    (%ebx), %edx
    movl    (%ecx), %eax
    movl    %eax, (%ebx)
    movl    %edx, (%ecx)
    addl    %edx, %eax
    popl    %ebx
    .cfi_def_cfa_offset 4
    .cfi_restore 3
    ret
    .cfi_endproc
caller:
.LFB1:
    .cfi_startproc
    subl    $24, %esp
    .cfi_def_cfa_offset 28
    movl    $534, 16(%esp)
    movl    $1057, 20(%esp)
    leal    20(%esp), %eax
    movl    %eax, 4(%esp)
    leal    16(%esp), %eax
    movl    %eax, (%esp)
    call    swap_add
    addl    $24, %esp
    .cfi_def_cfa_offset 4
    ret
    .cfi_endproc

教材中的问题以及解决

1.首先这次的虚拟机又出现了新的问题,并且尝试解决问题,出现的问题如下图所示。

在出现这种情况的时候,你不能进行任何操作,而且电脑一直处于运行状态,当你点击最右边的按键进行取消的时候,电脑也会用很长的时间来取消。在发现这种问题之后,我上网搜索了答案。(https://cnzhx.net/blog/wont-upgrade-virtualbox-for-a-long-time/)
我将电脑的虚拟机恢复到之前的版本然后解决了问题。

2.栈破坏检测是如何运行的?

原来碰到本来运行很正常的程序,忽然弹出错误对话框,“

"0X"指令引用的"0X**"内存。该内存不能为"read"或"written"”。接着程序就崩溃退出。
这样的错误是程序存在BUG引起堆栈被破坏导致的。不要小看这样的问题,这种导致堆栈破坏的BUG因为不知道下一次什么时候就会出现,
具有很强的隐蔽性。所以调试起来也很麻烦。

主要是一些不安全的库函数的使用,特别是对特定大小的数组进行的一些操作,特别要注意.。

代码调试中的问题以及解决

1.在转移过程中出现了问题

后来发现是自己没有注意一些小问题。

首先是调用函数时pc的位置发生了变化而自己却没有注意。并且在返回值的函数上出现了问题。

代码托管

学习与感悟

本周老师在蓝墨云班课上留了37道题。做这几十道题我用了几个小时的时间,用几个小时的时间来学习教材上的知识。虽然时间很长,但是感觉自己收获的东西也很多。这次第三章的学习有100多页,刚看的时候头脑确实很迷茫,但是在看到前人的总结与经验之后,发现整体的框架还是非常好的。所以现在来看能够理解的东西也变得多了。

学习时间条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 10 /10 1/1 10/10
第二周 40 /70 2/4 18/38
第三周 150/200 3/7 15/60
第四周 180/250 4/8 13/70

参考:软件工程软件的估计为什么这么难,软件工程 估计方法

计划学习时间:13小时

实际学习时间:17小时

改进情况:多提出问题,多解决问题。

(有空多看看现代软件工程 课件 软件工程师能力自我评价表)

参考资料
《深入理解计算机系统V3》学习指导
...