《Linux内核分析》之计算机是如何工作的 实验总结
前言
前两天在家电脑win7系统一直打不开网站,今天换了个网反而好了,具体原因未知。 马马虎虎学完了Python课程,一直想学下linux,看到里面有个linux的就选上了。当初没细看,如今听完第一节课有点傻眼,竟然糊里糊涂给自己找了一科汇编语言的课程,静心看下去庆幸自己还知道点堆栈的知识并出现了轻微的自虐倾向。闲话少说,现开正题。注:本文具有总结兼作业性质,如有雷同,纯属巧合。
实验及总结
本实现代码及汇编部分均在实验楼完成。
实验代码
int g(int x)
{
return x + 2;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 1;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
汇编后代码及部分代码截图
使用gcc –S –o main.s main.c -m32
命令编译成汇编代码
代码
.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 $2, %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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
纯净的汇编代码
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $2, %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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
汇编代码分析
[caption id=“” align=“aligncenter” width=“409”] 堆栈信息变更参考图[/caption] 和一般代码执行一样从main函数开始。 [infobg class=“notice” closebtn=“” color=“” bgcolor=“”] esp堆栈的栈顶 ebp堆栈的基址(栈底) eip当前的指令指针,eip寄存器不能被直接修改,只能通过特殊指令间接修改,故call/ret 例子中eip(*)表示伪指令 eax 函数的返回值默认使用eax寄存器来返回给上级函数。 假设一开始为空栈,初始位置为0,ebp=esp,如图:
[/infobg] [infobg class=“success” closebtn=“” color=“” bgcolor=“”]
- 将当前ebp压栈同时esp的值被修改,即esp指向位置(1)
- ebp指向esp的位置,即ebp指向位置(1)
- esp-4即将esp向下移动一个编号,即esp指向位置(2)
- 将8放到当前esp的位置,即将8放在位置(2)
- 调用f函数 call f 对应的指令:push1 %eip(*) 和movl f,%eip(*)
此时eip实际指向下一行即第23行代码,将eip(标号23)存入位置(3)
同时esp指向位置(3),eip指向f
- 将ebp的地址(位置1)存入位置(4),esp指向位置(4),此时代码进入f函数(模块)并执行其第一语句
- 使ebp指向与esp相同的地址,即位置4
- esp指向位置(5)
- 将ebp+8变址寻址,即向上2个标号的位置,由于该位置的值为8,即eax=8
- 将eax的值放到esp的位置,即将8放到位置(5)
- 调用g函数 call g对应的指令:push1 %eip(*) 和movl g,%eip(*) esp移到位置(6),此时eip实际指向下一行即第15行代码,将eip(15)存入位置(6),同时eip指向g
- 将ebp值ebp(4)存入位置(7),esp指向位置(7),此时代码进入g函数(模块)并执行其第一语句
- 使ebp指向与esp相同的地址,即位置7
- 将ebp+8变址寻址,即向上2个标号的位置,由于该位置的值为8,即eax=8
- 将2加到eax中,即2+8=10,此时eax=10
- ebp指向位置(4),esp指向位置(6)
- esp指向位置(5),同时eip重新指向行号15
- esp指向ebp的地址(位置4),即esp指向位置(4)
- ebp指向位置(1),esp指向位置(3)
- eip重新指向行号23
- eax的值加上1,eax=11
- esp指向ebp的地址(位置1),即esp指向位置(1)
- ebp指向位置(0),esp指向位置(0)
- main函数返回,eip重新指向该main函数调用前中断的地址,运行其他指令
[/infobg]
计算机是如何工作的
采用冯·诺依曼体系结构,使用存储程序方式,cpu和内存用总线连接。
从硬件角度
cpu中含有寄存器,其中寄存器ip总是指向内存的某一区域(内存cs即代码段) cup从ip指向的内存地址取出一条指令执行,执行完后ip自加1,取下一条指令再执行,如此循环。
程序员的角度
CPU为for循环,总是从内存中取出指令解释和执行 内存保存指令和数据 CPU负责解释和执行这些指令 cpu和内存之间用总线连接
附录
mov寻址指令
b,w,l,q分别代表8位、16位、32位和64位。此处以32为例
cup对内存的操作方法
mov指令
[toggle hide=“yes” title=“常用mov寻址命令” color=“#5bc0de”] movl %eax,%edx edx =eax; 解释:register mode 寄存器寻址 %表寄存器,即寄存器模式,以%开头的寄存器标识符。和内存无关 本句含义:将eax寄存器里的内容放到edx寄存器中,相当于后面的edx =eax movl $0x123,%edx edx = 0x123; 解释: immediate 立即寻址 $+16进制的数字即:立即数是以¥开头的数值。和内存无关 本句含义:将数值直接放在edx中,相当于后面的edx = 0x123 movl 0x123,%edx edx=*(int32_t*)0x123; 解释: direct 直接寻址 直接访问一个指定的内存地址的数据。无$,表一个地址 本句含义:将内存地址16进制的123所指向的内存数据放到edx中,相当于后面 edx=*(int32_t*)0x123即c语言中将数值强制转换为32位int变量的指针,在用一个*的指针取其指向的值 movl (%ebx),%edx edx =*(int32_t*)ebx; 解释:indirect 间接寻址 将寄存器的值作为一个内存地址来访问内存 本句含义:寄存器%ebx加()表ebx存的值(是个内存地址),加括号表示将内存地址所存储的数据放到edx中,即将ebx强制转换为一个地址,再加*取它的值。
movl 4(%ebx),%edx edx=*(int32_t*)(ebx+4); 解释:displaced 变址寻址
在间接寻址之时改变寄存器的数值 本句含义:在间接寻址的基础上,先给地址+个立即数,然后转换成int类型的指针,再取值 [/toggle]
常用汇编指令简记
[toggle hide=“yes” title=“常用汇编指令” color=“#0C7B14”] **push1 %eax ** 将 寄存器%eax压栈到栈顶,栈的位置在增长 what it does subl $4,%esp 压栈 esp-4,将堆栈的栈顶 movl %eax,(%esp) 间接寻址将eax放置到esp处 ***提示:***push1 %ebp 将当前ebp压栈同时esp的值被修改
**popl %eax ** 出栈,栈的位置在收缩 what it does movl (%esp),%eax 将栈顶的数值放在eax里 addl $4,%esp 将esp+4相当于回退一个位置
call 0x12345 函数调用地址(函数调用堆栈) 理解C代码在CPU上执行的关键 what it does push1 %eip(*) 对当前指令压栈 movl $0x12345,%eip(*) 使下一条指令执行位置为0x12345
ret 与call对应,将call时保存的eip还原到eip寄存器中即执行call之前的下条指令 what it does popl %eip(*)
leave 撤销堆栈 what it does movl %ebp,%esp popl %ebp
enter 将栈置为一个新的空栈 what it does pushl %ebp movl %esp,%ebp [/toggle]
小知识点
[toggle hide=“yes” title=“必知小知识点” color=“#C00”] 1、栈是向下增长的,向2000H的地址存入数据实际存储数据的是2000H-2003H这四个字节,也就是指针指向的是单元格的下边界来表示指向一个单元格。 2、对x86计算机大多数的指令可直接访问一个内存地址 AT&T汇编格式与Intel汇编格式略有不同 Linux内核使用的是AT&T汇编格式 [/toggle] windCoder原创作品转载请注明出处
参考资料
本文部分资料与图片参考自 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
除特别注明外,本站所有文章均为 Windcoder网 原创,转载请注明出处来自: linux-nei-he-fen-xi-zhi-ji-suan-ji-shi-ru-he-gong-zuo-di-shi-yan-zong-jie

那个图片画得不错···· :oops:
回复 @Me.稀奇: 就当成赞赏了= =
学习了,讲得很详细
回复 @圆月博客: O(∩_∩)O谢谢,共同学习