1、Intel 80x86内存模型以及它的工作原理:
在Intel 80x86内存模型中,段是内存模型设计的结果,在86x86的内存模型中,各处理器的地址空间并不一致(因为要保持兼容性),但它们都被分割成以64KB为单位的区域,每个这样的区域便称为段。
作为80x86内存模型最基本的形式,8086中的段是一块64KB的区域,由一个段寄存器所指向。内存地址的形成经过是:取得段寄存器的值,左移4位(相当于乘上16),或者看成把段寄存器的值看成是20位的,也就是在值的右边扩充4个0。
然后就是16位的偏移地址,它表示段内的地址。如果把段寄存器的值(经过移位)加上偏移地址,就得到最终的地址。(不同的段地址加上偏移地址所形成的值可能指向同一个内存地址,如:A0000 + FFFF = AFFFF; AFFF0 + 000F = AFFFF)
2、内存媒介的速度与成本关系:
慢速访问 <--------------------------------快速访问
磁带 磁盘 内存 Cache存储器 CPU寄存器
成本低、容量大------------------------>成本高、容量小
3、用程序查看可以分配多大的内存:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int MB = 0;
while(malloc(1 << 20))
++MB;
printf("Allocated %d MB total\n", MB);
return 0;
}
4、堆经常会出现两种类型的问题:
1)释放或改写仍在使用的内存(称为“内存损坏”)。
2)未释放不再使用的内存(称为“内存泄漏”)。
5、总线错误(bus error)
union {
char a[10];
int i;
}u;
int *p = (int *)&(u.a[1]);
*p = 17; /*p中未对齐的地址会引起一个总线错误*/
错误原因:假设数组a 的地址从0x1000开始,强制类型转换将0x1001--0x1004的内容拷贝给 p ( p 为 int 型),而数据项只能存储在地址是数据项大小的整数倍的内存位置上,此处 int 型数据地址必须为4的整数倍,0x1001不满足。
然而有的不显示总线错误,原因是:
x86体系结构会把地址对齐之后,访问两次,然后把第一次的尾巴和第二次的头拼起来。如果不是x86,那种体系结构下的机器不肯自动干这活,就会产生core。如果在代码中将对齐检查功能打开,运行后能显示buserror
将程序改成下面形式,可以显示总线错误:
#include <stdlib.h>
int main(int argc, char **argv) {
#if defined(__GNUC__)
# if defined(__i386__)
/* Enable Alignment Checking on x86 */
__asm__("pushf\norl $0x40000,(%esp)\npopf");
# elif defined(__x86_64__)
/* Enable Alignment Checking on x86_64 */
__asm__("pushf\norl $0x40000,(%rsp)\npopf");
# endif
#endif
union{
char a[10];
int i;
}u;
int *p =(int*)&(u.a[1]);
*p =17;
}
6、导致段错误的几个直接原因:
1)解除引用一个包含非法值的指针。(解除引用:*)
2)解除引用一个空指针(常常由于从系统程序中返回空指针,并未经检查就使用)。
3)在未得到正确的权限时进行访问。例如,试图往一个只读的文本段存储值就会引起段错误。
4)用完了堆栈或堆空间(虚拟内存虽然巨大但绝非无限)。
可能导致段错误的常见编程错误:
1)坏指针值错误:在指针赋值之前就用它来引用内存,或者向库函数传送一个坏指针。第三种是对指针进行释放之后再访问它的内容。可以修改 free语句,在指针释放后将它置为空值。
free(p); p = NULL;
2)改写错误:越过数组边界写入数据,在动态分配的内存两端之外写入数据,或改写一些堆管理数据结构(在动态分配内存之前的区域写入数据就很容易发生这种情况)
p = malloc(256); p[-1] =0; p[256] = 0;
3)指针释放引起的错误:释放同一个内存块两次,或释放一块未曾使用 malloc 分配的内存,或释放仍在使用中的内存,或释放一个无效的指针。一个常见错误:for(p = start; p; p = p->next)这样的循环中迭代一个链表,并在循环体内使用 free(p) 语句。
7、使用setjmp/longjmp从信号中恢复:程序在收到一个 control-C(作为 SIGINT 信号)时将重新启动,而不是退出。
#include<setjmp.h>
#include<signal.h>
#include<stdio.h>
jmp_bufbuf;
voidhandler(int s)
{
if(s == SIGINT)tff
{
printf("nowgot a SIGINT signal\n");
longjmp(buf,1);
}
}
intmain()
{
signal(SIGINT,handler);
if(setjmp(buf)!= 0)
{
printf("backin main\n");
return0;
}
else
{
printf("firsttime through\n");
}
while(1); //循环等待ctrl-C
}
运行结果:
first time through
now got a SIGINT signal
back in main