2012-07-19 22:25:27| 分类: Linux | 标签:linux 存储系统 虚拟内存 |字号大中小订阅linux
Linux为每个进程单独维护了一个单独的虚拟地址空间,形式如图所示。面试
其中内核虚拟存储器包含内核中的代码和数据结构。内核虚拟存储器中的某些区域被映射到全部进程共享的物理页面。例如每一个进程共享内核的代码和全局数据结构。数据结构
内核虚拟存储器的其余区域包含每一个进程都不相同的数据,好比页表,内核在进程上下文中执行代码时用到的栈。tcp
Linux将虚拟存储器组织成一些区域(也叫作段)的集合。一个区域就是已经存在的(已分配的)虚拟存储器的连续片,这些页是以某种方式相关联的。好比说代码段,数据段,堆,共享库段以及用户栈都是不一样的区域。每一个存在的虚拟页面都保存在某个区域中,而不属于某个区域的虚拟页是不存在的,而且不能被进程使用。ide
在Linux系统中发生缺页异常时,致使控制转移到内核的异常处理程序,这个处理程序将执行如下步骤:函数
Linux经过将虚拟存储器区域与一个磁盘上的对象关联起来,以初始化这个虚拟存储器区域的内容,这个过程就叫作存储器映射。ui
一旦一个虚拟页面被初始化了,它就在一个由内核维护的专门的交换文件之间换来换去。交换文件也叫作交换空间。须要意识到的很重要的一点是,在任什么时候候,交换空间都限制着当前运行着的进程可以分配的虚拟页面的总数。spa
共享对象翻译
存储器映射的概念其实来源于一个聪明的发现:若是虚拟存储器系统能够集成到传统的文件系统中,那么就可以提供一种简单而高效的把程序和数据加载到存储器中的方法。orm
进程这一抽象的概念可以为每一个进程提供本身私有的虚拟地址空间,能够避免受其余进程的错误读写。不过有许多进程有一样的只读文本区域,并且许多程序须要访问只读运行时库代码的相同拷贝。好比每一个C程序都须要来自标准C库的诸如printf这样的函数。那么若是每一个进程都在物理存储器中保持这些经常使用代码的复制拷贝,那就是一种极大的浪费。因此,咱们须要存储器映射给咱们提供一种清晰的机制,来控制多个进程如何共享对象。
一个对象能够被映射到虚拟存储器的一个区域,要么做为共享对象,要么做为私有对象。若是一个进程将一个共享对象映射到它的虚拟之地空间的一个区域内,那么这个进程对这个区域的任何写操做,对于那些也把这个共享对象映射到他们虚拟存储器的其余的进程来讲也是可见的。并且这些变化会反应在磁盘上的原始对象中。
对一个映射到私有对象的区域作改变的时候,对于其余进程来讲是不可见的,而且进程对这个区域所作的任何写操做都不会反应在磁盘上的对象中。
fork函数
理解了虚拟存储器和存储器映射以后,咱们就能够清晰的知道fork函数是如何建立一个带有本身独立虚拟地址空间的新进程的了。
当fork函数被当前进程(也就是父进程)调用的时候,内核为新进程(子进程)建立各类数据结构,并分配给它一个惟一的PID。为了给这个新进程建立虚拟存储器,它建立了当前进程的mm_struct结构、区域结构、和页表的原样拷贝。它将两个进程的页面都标记为只读,并将两个进程中的每一个区域结构都标记为私有的写时拷贝。
当fork在新进程中返回的时候,新进程如今的虚拟存储器恰好和调用fork时存在的虚拟存储器相同。当这父子进程中的任何一个后来进行写操做的时候,写时拷贝机制就会建立新页面,所以,子进程对任何数据的操做(好比更改数据大小),对父进程是没有影响的。
execve函数
虚拟存储器和存储器映射在将程序加载到存储器到的过程当中扮演者重要的角色。假设当前的进程中执行了以下的调用: execve(“a.out”, NULL, NULL);
execve函数在当前的进程中加载并运行包含在可执行目标文件a.out中的程序,用a.out程序有效的替代了当前程序。加载并运行a.out须要如下几个步骤:
#include <unistd.h>#include <sys/mman.h>void *mmap(void *start, size_t length, int prot, int flagint fd, off_t offset);
mmap函数要求内核建立一个新的虚拟存储器区域,最好是从地址start开始的一个区域,并将文件描述符fd指定的对象的一个连续的片chunk映射到这个新的区域。连续的对象的片的大小为length字节,从距文件开始处偏移量为offset字节的地方开始。start地址仅仅是一个暗示,一般被定义为NULL(表明由内核来决定)。
参数prot包含描述新映射的虚拟存储器区域的访问权限位(在相应结构中的vm_prot位)
- PROT_EXEC:这个区域内的页面由能够被CPU执行的指令组成。
- PROT_READ:这个区域内的页面可读。
- PROT_WRITE:这个区域内的页面可写。
- PROT_NONE:这个区域内的页面不能被访问。
参数flag由描述被映射对象类型的位组成。若是设置了MAP_ANON标记为,那么被映射的对象就是一个匿名对象,而相应的虚拟页面是请求二进制零的。MAP_PRIVATE表示被映射的对象是一个私有的,写时拷贝的对象。而MAP_SHARED表示是一个共享的对象。
能够写一个C程序,使用mmap函数将一个任意大小的磁盘文件拷贝到stdout。
int main(int argc, char **argv){struct stat stat;
int fd;
if(argc != 2)
{printf("Usage Wrong");
exit(0);}fd = Open(argv[1], O_RDONLY, 0);fstat(fd, &stat);char * bufp;
bufp = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE,fd, 0);Write(1, bufp, stat.st_size);exit(0);}