您的位置:首页 > 其它

对内存的思考

2015-05-29 16:00 169 查看
1、Intel 80x86内存模型以及它的工作原理

• 在UNIX中,段表示一个二进制文件相关的内容块。

• 在Intel x86的内存模型中,段表示一种设计的结果。在这种设计中,地址空间并非一个整体,而是分成一些64K大小的区域,称之为段。

作为80x86内存模型的最基本的形式,8086中的段是一块64KB的内存区域,由一个段寄存器所指向。内存地址的形成过程是:取得段寄存器的值,左移4位(相当于乘上16),或者换种思路,把段寄存器的值看成是20位的,也就是在值的右边扩充4个0.

然后就是16位的偏移地址,它表示段内的地址。如果把段寄存器的值(经过移位)加上偏移地址,就得到最终的地址。(注意:正如两个数加起来等于24的例子有很多一样,不同的段地址加上偏移地址所形成的值可能指向同一个内存地址。

2、虚拟内存

虚拟内存通过”页“的形式组织。页就是操作系统在磁盘和内存之间移来移去或进行保护的单位,一般为几K字节。可以通过键入 /usr/ucb/pagesize 来观察你的系统中的页面大小。当内存的映像在磁盘和物理内存间来回移动时,称它们是page in(移入内存)或 page out (移到磁盘)。

编程看看你在进程中可以分配多大的内存:

#include <stdio.h>
#include<stdlib.h>

main() {
// your code goes here
int MB=0;
while(malloc(1<<20)) ++MB;
printf("Allocated %d MB total\n",MB);
}


为了让这个程序能够在有内存限制的 MS-DOS 上运行,把每次分配的单元从1MB改为1KB(就是把1<<20改成1<<10,并用KB代替MB)。


3、Cache存储器

Cache存储器是多层存储概念的更深扩展。位于CPU和内存之间,是一种极快的存储缓冲区。

当前使用两种类型的cache:

• 全写法(write-through)Cache–每次写入Cache时总是同时写入到内存中,使内存和Cache始终保持一致。

• 写回法(write-back)Cache–当第一次写入时,只对Cache进行写入。如果已经写入过的Cache行需要写入时,此时第一次写入党结果尚未保存,所以要先把它写入到内存中。Cache中的所有数据也都要先写入到内存中。

Cache的组成

行(line) 行就是对cache进行访问的单位。每行由两部分组成:一个数据部分以及一个标签,用于指定它所代表的地址

块(block) 一个cache行内的数据被称作块。块保存来回移动于cache行和内存之间的字节数据。一个典型的块为32字节。

一个cache行的内容代表特定的内存块,如果处理器试图访问属于该块地址范围的内存,它就会作出反应,速度自然要比访问内存快得多。

在计算机行业中,对大多数人而言,“块”和“行”的概念分得并不特别清,两者常常可以交换使用

Cache 一个cache(一般为64K到1M之间,也可能更多)由许多行组成。有时也使用相关的硬件来加速对标签的访问。为了提高速度,cache的位置离CPU很近,而且内存系统和总线经过高度优化,尽可能地提高大小等于cache块的数据块的移动速度

体验cache:

#define DUMBCOPY for(i = 0;i < 65536;i++) \
destination[i] = source[i]
#define SMARTCOPY memcpy(destination, source, 65536)
main(){
char source[65536],destination[65536];
int i, j;
for(j = 0;j<100;j++)
SMARTCOPY;
}


1.0 seconds user time
改为DUMBCOPY,并重新编译
7.0 seconds user time


4、内存泄露

每次当使用malloc分配内存时,注意在以后要调用相应的free来释放它。

如果不知道如何调用free与先前的malloc相对应,那么很可能已经造成了内存泄露。

一种简单的方法就是在可能的时候使用alloca()来分配动态内存。但这并不适用于那些比创建它们的函数生命期更长的结构。

如何检测内存泄露:

观察内存泄露是一个两步骤的过程。首先,使用swap命令观察还有多少可用的交换空间:

/usr/sbin/swap -s


Eg:total:17228k bytes allocated(已分配) + 5396k reserved(保留) = 22624k used(已用), 29548k available(可用)
在一两分钟内键入该命令三到四次,看看可用的交换区是否在减少。还可以使用其他一些/usr/bin/*stat工具如netstat、vmstat等。如果发现不断有内存被分配且从不释放,一个可能的解释就是有个进程出现了内存泄露。


5、总线错误

总线错误:

总线错误几乎都是由于未对齐的读或写引起的。它之所以称为总线错误,是因为出现未对齐的内存访问请求时,被堵塞的组件就是地址总线。对齐(alignment)的意思就是数据项只能存储在地址是数据项大小的整数倍的内存位置上。

eg:

union{ char a[10];
int i;
}u;
int *p = (int *)&(u.a[1]);
*p = 17;     //p中未对齐的地址会引起一个总线错误


段错误:
由于内存管理单元(负责支持虚拟内存的硬件)的异常所致,而该异常则通常由解除引用一个未初始化或非法值的指针引起的。如果指针引用一个并不位于你的地址空间中的地址,操作系统便会对此进行干涉。
eg:


int *p = 0;
*p = 17;   //引起一个段错误


导致段错误的几个直接原因:
• 解除引用一个包含非法值的指针。
• 解除引用一个空指针(常常由于从系统程序中返回空指针,未经检查就使用)
• 在未得到正确的权限时进行访问。例如往一个只读的文本段存储值就会引起段错误。
• 用完了堆栈或堆空间(虚拟内存虽然巨大但并非无限)。
Tips:
通常情况下,总线错误意味着CPU对进程引用内存的一些做法不满,而段错误则是MMU对进程引用内存的一些情况发出抱怨。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: