1,8086内存地址的形成经过:取得段寄存器的值,左移4位,加上16位的偏移地址(段内地址)。
在Microsoft C中,fan关键字表示指针存储了段寄存器的内容和偏移地址。
near关键字表示指针只存储16位的偏移地址,它的段地址使用当前数据或堆栈段寄存器中的值。
2,虚拟内存只是对多层存储进行扩充,使用磁盘而不是主存来保存运行进程的映像。
3,虚拟内存通过“页”的形式组织。页是操作系统在磁盘和内存之间转移或进行保护的基本单位。
4,换出:如果一个进程不会马上运行,操作系统可以暂时取回所有分配给它的物理内存资源,将相关信息备份到磁盘上。
在磁盘中有一个特殊的“交换区”,用于保存从内存中换出的进程。大小一般是物理内存的几倍。
5,进程只能操作位于物理内存中的页面。
当进程引用一个不在物理内存中的页面时,MMU就会产生一个页错误。内核对此事件做出响应,并判断该引用是否有效。
如果无效,内核向进程发出一个“segmentation violation”信号。
如果有效,内核从磁盘中取回该页换入到内存中。一旦页面进入内存,进程便被解锁,可以重新运行。
所有对内存的读取和写入操作都要经过Cache。当处理器需要从一个特定的地址提取数据时,这个请求首先递交给Cache。
如果数据已经存在于Cache中,它就立即被提取。
否则,Cache向内存传递这个请求,就要进行缓慢的访问内存操作。读取也是以行为单位,读取的同时也装入到Cache中。
6,堆经常会出现两种类型的问题:
(1)释放或改写仍在使用的内存。(“内存损坏”)
(2)未释放不再使用的内存。(“内存泄露”)
7,总线错误:
几乎都是由于未对齐的读或写引起的。
(1)之所以称为总线错误,是因为出现未对齐的内存访问请求时,被堵塞的组件就是地址总线。
(2)对齐的意思是数据项只能存储在地址是数据项大小的整数倍的内存位置上。
这样做可以极大的简化如Cache控制器和内存管理单元这样的硬件。
即:“数据项不能跨越页面或Cache边界”
8,段错误
引发:指针引用一个并不位于自己地址空间中的地址。
导致段错误的常见编程错误:
(1)坏指针值错误:
在指针赋值之前就用来引用内存。
对指针进行释放后,又访问它。
(2)改写错误:
越过数组边界写入数据或在动态分配的内存两端写入数据等。
(3)指针释放:
释放同一个内存块两次
释放一个未曾使用malloc分配的内存(无效指针)
释放仍在使用的内存
9,使用setjmp/longjmp从信号中恢复
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
jmp_buf buf;
void handler(int s)
{
if(s==SIGINT) printf("now get a SIGINT signal\n");
longjmp(buf,1);
}
int main()
{
signal(SIGINT, handler);
if(setjmp(buf))
{
printf("back in main\n");
return 0;
}
else
printf("first time through\n");
loop:
goto loop;
}
注:setjmp和longjmp使得程序难以调试和理解,如果不是出于特殊需要,最好避免使用它们。
10,捕获段错误信号的处理程序
#include <stdio.h>
#include <signal.h>
void handler(int s)
{
if(s==SIGBUS) printf("now get a bus error signal\n");
if(s==SIGSEGV) printf("now get a segmentation violation signal\n");
if(s==SIGILL) printf("now get an illegal instruction signal\n");
exit(1);
}
int main()
{
int *p=NULL;
signal(SIGBUS, handler);
signal(SIGSEGV,handler);
signal(SIGILL,handler);
*p=0;
return 0;
}