程序运行时的内存-----《程序员自我修养》第十章笔记

先贴下第十章的目录:
第10 章 内存 283
10.1 程序的内存布局 284
10.2 栈与调用惯例 286
10.3 堆与内存管理 305
10.4 本章小结 315

从目录就能看出来这章的内容很爽,主要涉及了程序运行时栈的状态、程序如何对栈进行处理、堆内存如何分配、虚拟内存等话题。
这章中很多内容是知道的,不过作者在讲解完一个知识点之后都会总结一下。我这份笔记其实就是对这些“比较重要”的内容的摘录,方便以后查阅。

栈:
在数据结构书中,栈被定义为一个容器,用户可以将数据压入栈(push)中,也可以从栈中把内容弹出来(pop),但这个容器必须遵守先入后出的规则。
在计算机系统中,栈则是一个具有以上属性的动态内存区域。程序可以将数据压入栈中,也可以将数据从栈顶弹出。压栈操作使栈增大,弹出使栈缩小。

栈总是向下增长的。在I386下,栈顶由成为esp的寄存器进行定位。压栈的操作使栈顶的地址减小,弹出操作使栈地址增大。

栈在程序中,最重要的作用体现是:栈保存一个函数调用所需要的维护信息,这被称为堆栈帧(函数活动记录)。

堆栈帧包括以下几个内容:
函数的返回地址和参数。
临时变量:包括函数和非静态局部变量以及编译器自动生成的其他临时变量。
保存的上下文:包括在函数调用前后需要保持不变的寄存器

在I386中,一个函数活动的堆栈帧用ESP和EBP两个寄存器划定范围。ESP寄存器始终指向栈的顶部,同时也就指向了当前函数活动记录的顶部。相对的,ebp指向活动记录的一个固定位置,ebp寄存器又被称为堆栈帧指针。

函数调用规范(重要):
函数调用(以下按先后顺序)
1、把所有或一部分参数压入栈中,如果有其他参数没有压入栈,那么使用某些特定寄存器进行传递。
2、把当前指令的下一条指令的地址压入栈中。
3、跳转到函数执行。
(第2、3条步骤由汇编指令call来一起实现)

执行:
push ebp:把ebp压入栈中(保存环境)
mov ebp,esp:ebp=esp(这时ebp指向栈顶,栈顶就是刚刚保存的地址)
【可选】sub esp,XXX(在栈中分配XXX字节的临时空间)
【可选】push regn(保存寄存器)

调用完毕:
【可选】pop regn:如果保存,则需恢复刚保存的寄存器
mov esp,ebp:恢复esp,回收刚刚分配的空间
pop ebp:从栈中恢复保存的ebp值
ret:从栈中取得返回地址,继续执行。

函数在调用和定义的时,都要遵守相同的调用约定,否则会挂掉,具体几个部分:
1、函数参数的传递顺序和方式
2、栈的维护方式
3、名字修饰(也叫名字粉碎,就是编译过,链接前目标文件里面的名字)

返回值
函数将返回值存储到eax中,返回后函数的调用方再读取eax来接收返回值。但eax只有四字节,在32位下,如果返回值大于4字节会做如下处理:
1、大于4字节,小于等于8字节:
使用eax和edx两个寄存器来保存返回值。eax保存低4位,edx保存高4位
2、大于8字节:
在调用方的函数中,会生成一个长度符合返回值要求的临时变量,并在函数调用时隐式传递给目标函数。
目标函数在其中对这个传进来的值进行处理,然后让eax等于这个临时变量的地址作为返回值。如果调用方需要保存返回值,还需要再通过类似memcpy函数的内存拷贝,复制一份这个返回的地址其中的值。





堆是一块巨大的内存空间,常常占据整个虚拟空间的绝大部分。在这片空间里,程序可以请求一块连续内存,并自由使用,这块内存在程序主动放弃之前都会一直保持有效。

malloc的实现:
1、调用系统分配内存函数来实现
windows:VirtualAlloc
linux:mmap

特点:
管理算法由系统库完成,适合分配大块内存或在程序运行初由程序分配大块内存自己实现内存管理
频繁分配、释放效率很低,因为是按内存分页来分配的
依赖平台接口

2、由运行库来实现
特点:
只要平台库跨平台,那代码就可以跨平台
如果分配算法不高效,那代码效率没什么保障


堆常见的分配算法:
这个。。。放狗一找就能找到,就不敲了

注:这章感觉总结的很好,敲了半天,保存了下来,方便自己,也方便别人参考
posted @ 2010-09-07 14:29  飘啊飘  阅读(586)  评论(0编辑  收藏  举报