日期:2014-05-16  浏览次数:20765 次

linux内核--进程地址空间(三)

引言:上篇博文中,我们简单的介绍了Linux虚拟存储器的概念及组成情况,下面来分析分析进程的创建和终结及跟进程地址空间的联系。

这里首先介绍一个比较重要的概念:存储器映射

在Linux系统中,通过将一个虚拟存储器区域与一个磁盘上的对象关联起来,以初始化这个虚拟存储器区域的内容,这个过程称为存储器映射。存储器映射为共享数据、创建新的进程以及加载程序提供了一种高效的机制。

虚拟存储器区域可以映射到两种类型对象中:

1)普通文件:一个虚拟区域可以映射到普通磁盘文件的连续部分,例如可执行目标文件。虚拟区域分为若干的虚拟页面,这些虚拟页面初始化时并没有实际交换进物理存储器,直到CPU第一次引用页面时才真正的加载进物理内存。如果虚拟区域比映射的文件要大,则剩下的部分用零填充。

2)匿名文件:匿名文件是由内核创建的,包含的全部是二进制零。映射到匿名文件的区域中的页面有时称为 请求二进制零的页。

注意:无论映射到何种文件,一旦一个虚拟页面被初始化了,它就在一个由内核维护的专门的交换文件之间换来换去。这里的交换文件也称为 交换空间。由此可见任何时候,交换空间限制着当前运行着的进程能够分配的虚拟页面的总数。

有了前面的一些概念的基础下面我们开始看进程的创建、执行、退出。

一:进程创建

        Unix的进程创建比较特别。许多其他操作系统提供了产生机制。首先在新的地址空间里创建进程,读入可执行文件,最后开始执行。Unix采用了不同方式:它把上述步骤分为两步,分解到两个单独的函数中执行:fork( ) 和 exec( )。

        fork( )函数被当前进程调用时,内核为新进程创建各种数据结构(例如内核栈、thread_info结构、task_struct结构)并分配给它一个唯一的PID。为了给新进程创建虚拟存储器,它创建了当前进程所有资源的原样拷贝。它将两个进程的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为 私有的写时拷贝。

        Linux的fork( )使用写时拷贝页实现。写时拷贝是一种可以推迟甚至免除拷贝数据的技术。内核此时并不复制整个进程地址空间,而是让父进程和子进程共享一个拷贝。

       只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝,从而为父子进程保持了私有地址空间的抽象概念。资源的复制只有在需要写入的时候才进行,在此之前,只是以只读的方式共享。这中技术使得地址空间上的页的拷贝被推迟到实际发生写入的时候才进行。在页根本不会写入的情况下(例如fork()之后立即调用exec() )它们就无须复制了。

       下面看个实例:

      

复制代码
 1 #include <stdlib.h>
 2 #include <unistd.h>
 3 #include <stdio.h>
 4 int main( )
 5 {
 6   int pid;
 7   int x =