Linux 核心--5.Linux进程( 六 )








图4.2 进程的虚拟内存

Linux核心需要管理所有的虚拟内存地址 , 每个进程虚拟内存中的内容在其task_struct结构中指向的 vm_area_struct结构中描叙 。进程的mm_struct数据结构也包含了已加载可执行映象的信息和指向进程页表 的指针 。它还包含了一个指向vm_area_struct链表的指针 , 每个指针代表进程内的一个虚拟内存区域 。

此链表按虚拟内存位置来排列 , 图4.2给出了一个简单进程的虚拟内存以及管理它的核心数据结构分布图 。由于那些虚拟内存区域来源各不相同 , Linux使用vm_area_struct中指向一组虚拟内存处理过程的指针来抽 象此接口 。通过使用这个策略 , 所有的进程虚拟地址可以用相同的方式处理而无需了解底层对于内存管理的区别 。如当进程试图访问不存在内存区域时 , 系统只需要调用页面错误处理过程即可 。

为进程创建新虚拟内存区域或处理页面不在物理内存错误时 , Linux核心重复使用进程的vm_area_struct数据结构集合 。这样消耗在查找vm_area_struct上的时间直接影响了系统性能 。Linux把vm_area_struct数据结构以AVL(Adelson-Velskii and Landis)树结构连接以加快速度 。在这种连接中 , 每个vm_area_struct结构有一个左指针和右指针指向vm_area_struct结构 。左边的指针指向一个更低的虚拟内存起始地址节点而右边的指针指向一个更高虚拟内存起始地址节点 。为了找到某个的节点 , Linux从树的根节点开始查找 , 直到找到正确的vm_area_struct结构 。插入或者释放一个vm_area_struct结构不会消耗额外的处理时间 。

当进程请求分配虚拟内存时 , Linux并不直接分配物理内存 。它只是创建一个vm_area_struct 结构来描叙此虚拟内存 , 此结构被连接到进程的虚拟内存链表中 。当进程试图对新分配的虚拟内存进行写操作时 , 系统将产生页面错 。处理器会尝试解析此虚拟地址 , 但是如果找不到对应此虚拟地址的页表入口时 , 处理器将放弃解析并产生页面错误异常 , 由Linux核心来处理 。Linux则查看此虚拟地址是否在当前进程的虚拟地址空间中 。如果是Linux会创建正确的PTE并为此进程分配物理页面 。包含在此页面中的代码或数据可能需要从文件系统或者交换磁盘上读出 。然后进程将从页面错误处开始继续执行 , 由于物理内存已经存在 , 所以不会再产生页面异常 。


4.6进程创建
系统启动时总是处于核心模式 , 此时只有一个进程:初始化进程 。象所有进程一样 , 初始化进程也有一个由堆栈、寄存器等表示的机器状态 。当系统中有其它进程被创建并运行时 , 这些信息将被存储在初始化进程的task_struct结构中 。在系统初始化的最后 , 初始化进程启动一个核心线程(init)然后保留在idle状态 。如果没有任何事要做 , 调度管理器将运行idle进程 。idle进程是唯一不是动态分配task_struct的进程 , 它的 task_struct在核心构造时静态定义并且名字很怪 , 叫init_task 。

由于是系统的第一个真正的进程 , 所以init核心线程(或进程)的标志符为1 。它负责完成系统的一些初始化设置任务(如打开系统控制台与安装根文件系统) , 以及执行系统初始化程序 , 如/etc/init, /bin/init 或者 /sbin/init  , 这些初始化程序依赖于具体的系统 。init程序使用/etc/inittab作为脚本文件来创建系统中的新进程 。这些新进程又创建各自的新进程 。例如getty进程将在用户试图登录时创建一个login进程 。系 统中所有进程都是从init核心线程中派生出来 。

新进程通过克隆老进程或当前进程来创建 。系统调用fork或clone可以创建新任务 , 复制发生在核心状态下的核心中 。在系统调用的结束处有一个新进程等待调度管理器选择它去运行 。系统从物理内存中分配出来一个新的task_struct数据结构 , 同时还有一个或多个包含被复制进程堆栈(用户与核心)的物理页面 。然后创建唯一地标记此新任务的进程标志符 。但复制进程保留其父进程的标志符也是合理的 。新创建的task_struct将被放入task数组中 , 另外将被复制进程的task_struct中的内容页表拷入新的task_struct中 。

推荐阅读