目录
我们先来思考一个问题:你写出的程序经过编译之后得到a.out是如何跑起来的呢?
a.out文件是二进制指令的集合。其内容经过了操作系统的管理调度被加载到了物理内存中,CPU再从物理内存中读取指令,并且执行,这样我们的程序就跑起来啦!
一、虚拟地址空间
1.虚拟地址空间的定义
这段文字告诉了我们:我们看到的地址是假的!是操作系统给我们看到的表象,我们借助这个表象的地址+操作系统存下的映射关系就能找到相应的物理内存,从而进行数据的访问了。
那么操作系统为什么要维护这样一个映射关系表呢?而不给我们实际的物理地址?
其实在计算机刚刚诞生的时候(还没有操作系统),人们就是这样做的,但是这样会有许许多多的麻烦事,人们要不断的计算所使用内存的大小,从而规划下一步在哪个地方使用内存,但是一旦操作不当就会导致内存泄漏或者程序崩溃。
于是人们便开发了操作系统这样的一个东西来帮助我们合理使用内存。而操作系统自己用了一个虚拟内存的东西来映射实际的物理内存,从而知道哪一块空间被使用了,哪一块空间没有被使用,自此我们对内存的操作大部分都交给了操作系统来处理,于是人力资源得到了极大的程度的解放。
32位:
(1)对于32位操作系统而言,每个进程都有4G大小的虚拟地址空间。
(2)所谓的地址空间就是一个地址范围,表示程序的寻址能力(但是并不意味着一定用到了4G大小的物理内存)我们所看到的虚拟地址都是在4G的这个范围内的
(3)对于32位操作系统,其虚拟地址空间范围是0X0000 0000到0XFFFF FFFF也就是4G
(4)其中的0到3G-1范围归用户使用,称为用户地址空间;3G到4G-1的范围归内核使用,称为内核地址空间。
64位:
(1)对于64位操作系统而言,目前的程序还没有那么大的内存需求,所以不支持完全的64位虚拟地址
(2)64位操作系统上,其用户的地址空间是0X0000 0000 0000 0000到0X0000 FFFF FFFF FFFF
(3)其内核的地址空间是0XFFFF 0000 0000 0000到0XFFFF FFFF FFFF FFFF
(4)内核地址空间和用户地址空间之间的是不规范地址空间,不允许使用
(5)用户地址空间的代码不能直接访问内核空间的代码和数据,但是可以通过系统调用进入内核,间接与系统内核交互
2.虚拟地址空间的布局
二、内存壁垒
1.内存壁垒的定义
从上面一段话中,我们可以看到:每一个进程中,只有内核空间的虚拟地址是一样的,别的用户地址空间部分都是各自独立的(可能在两个进程中,虚拟地址的值都是一样的,但是映射在物理内存中却有差别)
且内核的代码和数据只能通过系统调用来访问!!!
2.段错误
第一种情况是:对没有映射到物理内存的虚拟内存进行了访问,但是操作系统没有记录这个虚拟地址,所以就成了一个野指针类似的虚拟地址。
第二种情况是:在只读内存区域进行了写操作(比如在常量代码段进行修改)
三、内存映射的建立与解除
(1)mmap
一般情况下,我们第一个参数start都给的是NULL让系统自己来决定细虚拟地址;
length要求是按页圆整(即4096的整数倍个字节);
而prot必须在PROT_READ和PROT_WRITE以及PROT_EXEC和PROT_NONE中选择一个,作为我们映射区的操作权限;
最后,flags是映射标志,我们这里因为没有涉及到文件,所以是将虚拟内存映射到物理内存中,所以要选择MAP_ANONYMOUS(注意这里的flags都是可以多种选择的,比如我们可以用MAP_PRIVATE|MAP_ANONYMOUS让他既是匿名映射又是映射到缓冲区中)
fd是文件描述符,我们在后续讲到文件系统的时候再说