当前课程知识点:Linux 内核分析与应用 > 第4章 内存管理 > 4.2 进程用户空间管理机制 > Video
大家好 这一讲呢我们介绍一下进程用户空间的创建
前面我们介绍了每个进程都有自己独立的地址空间
那么进程的地址空间到底是什么时候创建的呢
实际上呢当fork系统调用在创建进程的时候呢
也就为该进程创建了完整的用户空间
如图所示那么这个用户空间
是如何被创建出来的
实际上是通过拷贝或者呢
共享父进程的用户空间来实现的
也就是说内核调用copy_mm()函数来实现的
那么这样的话就为新进程建立了
所有的页表和mm_struct结构
通常来说呢每个进程都有自己的用户空间
但是呢如果调用clone()函数的话
创建的内核线程它是共享
父进程的用户空间
那么Linux利用写时复制技术来快速创建进程
那么写时复制技术到底是如何来实现的呢
当子进程共享父进程的地址空间的时候
只要其中任何一个进程要进行写入
则该页面就被复制一份 如图所示
子进程要写C页 则该页就被复制了一份
我们说fork能快速的创建进程 那么这是为什么呢
从上面的介绍我们可以看出呢
进程用户空间的创建呢
主要依赖于父进程
而且在创建的过程中呢
所作的工作仅仅是mm_struct结构的建立
vm_area_struct结构的建立
以及页目录和页表的建立
并没有真正的复制一个物理页面
这也就是为什么Linux内核能够迅速创建进程的原因之一
下面我们来介绍一下虚存映射
当我们调用exec系统调用开始执行一个进程的时候
进程的可执行映像其中包括
代码段 数据段
堆和栈等等就必须装入到进程的用户地址空间中
如果该进程用到了任何一个共享库呢
则共享库也必须装入到进程的用户空间
由此可以看出呢Linux并不将映像装入到物理内存
相反呢可执行文件只是被映射到了进程的用户空间中
这种将可执行文件映像映射到
进程用户空间的方法被叫做虚存映射
那么内核通过怎样的方式新建一个个的虚存区VMA呢
下面我们来看一下VMA的新建方法
在用户空间我们可以通过mmap()系统调用呢
获取do_mmap()的功能
那么在内核空间的时候呢
可以直接调用do_mmap()呢
创建一个新的虚存区
它的调用关系如图所示
那么虚存区到底有那些呢实际上有三种虚存区
一种叫共享的 一种是私有的 一种是匿名的
那么什么是共享的虚存区呢
就是有几个进程共享这一映射 也就是说呢
如果一个进程对共享虚存区进行写的话
那么其它进程就能感觉到它的写
而且还会修改磁盘上对应的文件
那么文件的共享就可以采用这种方式
那么什么是私有的虚存区呢
那么进程创建这种映射只是为了读文件而不是为了写文件
因此对虚存区的写操作就不会修改磁盘上的文件
由此可以看出私有映射的效率实际上是比共享映射的效率要高的
那么除了这两种映射外呢
如果映射与文件无关的话 我们把它就叫做匿名映射
当可执行文件映射到进程的用户空间的时候呢
它就产生一组vm_area_struct结构
来描述各个虚存区的
起始地址和终止地址
那么这张图呢给出了整个虚存区的映射关系
现在我们通过一个简单的例子来描述
Linux内核是如何把共享库
以及各个程序段映射到进程的用户空间的
那么我们考虑一个最简单的C程序
在这个C程序里面我们只调用了malloc()函数
那么假设程序执行以后呢
其对应的pid为9413
那么这个进程对应的虚存区如表所示
如何进行查看呢我们可以进入proc目录
在proc目录下每一个进程的pid有对应的一个文件
然后我们再进入9413这个目录
在这个目录下我们可以查看maps
可以得到这些信息
这里列出的所有的区都是私有虚存映射
为什么说它是私有的呢 在许可权列出现的是字母P
这是因为这些虚存区的存在仅仅是为了给进程提供数据
当进程执行指令的时候呢
就可以修改这些虚存区的内容
但是与它们相关的磁盘上的文件保持不变
这就是私有虚存区映射所起的保护的作用
下面我们来介绍一下与用户空间相关的主要的系统调用
首先我们看一下fork()系统调用
它呢创建具有新的用户空间的进程
那么这个时候创建的用户空间中的
所有的页就被标记为写时复制
而且呢是由父子进程所共享的
那么第二个系统调用叫做mmap()
这个系统调用就是在进程的用户空间呢
创建一个新的虚存区
那么与mmap()相对应的有munmap()这个系统调用
它呢是销毁一个完整的虚存区或者其中的一个部分
如果要取消的虚存区位于某个虚存区的中间
则这个虚存区被划分为两个虚存区
第四个系统调用叫exec
它装入新的可执行文件以代替
当前的用户空间
最后一个系统调用是退出函数
它销毁进程的用户空间以及所有的虚存区
下面我们来编写一下虚存区的内核模块
在编写虚存区的内核模块之前呢我们来梳理一下
这几个数据结构之间的关系 就是task_struct结构
mm_struct结构以及vm_area_struct结构
这张图给出了这三种数据结构之间的关系
那么编写一个内核模块我们打印进程的虚存区
其中我们通过模块的参数把进程的PID传递给模块
在该模块中 我们通过传递的pid可以获得对应的PCB
从而就可以打印出这个进程各个区的起始地址
首先我们先运行前面写的程序
然后把进程的PID作为参数代入 如下所示
那么可以看出输出的信息与前面从proc文件系统中所读取的信息实际上是一致的
那么进程的用户空间实际上就是由一个个的虚存区组成的
所以说对进程用户空间的管理在很大程度上呢
就依赖于对虚存区的管理了
那么下面呢我们来介绍一下请页机制
我们说请页机制是实现虚存管理的重要手段
当一个进程运行的时候 CPU访问的是用户空间的虚地址
那么Linux仅把当前要使用的少量页面装入到内存
需要的时候通过请页机制将特定的页面调入到内存
那么当访问的页不在内存的时候就产生一个页故障
并报告故障的原因
下面,我们给出缺页异常处理的流程
这个图是一个主要框架的流程
Linux缺页异常处理程序区分以下两种情况
首先呢这个错误看看是由编程错误引起的呢
还是由缺页引起的异常
如果是编程引起的异常而且还发生在内核态
那么这个时候就要毫不含糊的杀死该进程
如果是发生在用户态呢
说明它是一个无效的内存引用 程序呢就要停止执行
如果是一个真正的缺页引起的异常而且呢
是有合法的权限呢 这个时侯呢
才进入到缺页异常处理程序
这个图就是缺页异常处理程序的处理
那么只有两个分支是真正的
请求分页的处理
也就是图中给出的橘色的部分
大家在看这张图的时候呢
首先查看这两个正常情况的处理分支
然后再查看异常情况的处理分支
那么在对整体的流程有了了解以后
你想真正的理解异常处理程序呢
还是要阅读源代码
哪一个函数呢 这个函数的名字叫do_page_fault()函数
下面,我们对于用户进程访问内存进行一个分析
我们说用户态进程独占虚拟地址空间
那么两个进程的虚拟地址空间完全可能是相同的
那么在访问用户态虚拟地址空间的时候呢
如果没有映射到物理地址
这个时候就要通过请页机制发出缺页异常的请求
缺页异常陷入内核 分配物理地址空间
与用户态虚拟地址空间就建立起映射关系
那么到底如何分配物理内存呢
请大家关注下一讲
这一讲我们给的参考资料还是《深入理解Linux内核》(第三版)的第八 九章
这一讲以后呢大家也带着思考离开
这里给出三个问题
第一个问题 什么是内存的泄露
为什么会发生内存的泄露
什么是野指针 为什么会出现野指针
前面我们给出了mmap()这样一个系统调用进行内存映射
那么当有多个进程的时候呢 这样的映射是否安全
谢谢大家
-1.1 Linux操作系统概述
-1.2 Linux内核结构以及内核模块编程
--Video
-1.3 Linux内核源码中的双链表结构
--Video
-1.4 源码分析-内核中的哈希表
--Video
-1.5 动手实践-Linux内核模块的插入和删除
--Video
-第1章 概述--章节测验
-2.1 内存管理之内存寻址
--Video
-2.2 段机制
--Video
-2.3分页机制
--Video
-2.4 动手实践-把虚拟地址转换成物理地址
--Video
-第2章 内存寻址--章节测验
-3.1 进程概述
--Video
-3.2 Linux进程创建
--Video
-3.3 Linux进程调度
--Video
-3.4 动手实践-打印进程描述符task_struct中的字段
--Video
-3.5工程实践-基于内核模块的负载监控
--Video
-第3章 进程管理--章节测验
-4.1 Linux内存管理机制
--Video
-4.2 进程用户空间管理机制
--Video
-4.3 物理内存分配与回收机制(上)
--Video
-4.4 物理内存分配与回收机制(下)
--Video
-4.5 动手实践-Linux内存映射基础(上)
--Video
-4.6 动手实践-Linux内存映射实现(中)
--Video
-4.7 动手实践-Linux内存映射测试(下)
--Video
-4.8 初学者对内存管理的常见疑惑
-第4章 内存管理--章节测验
-5.1 中断机制概述
--Video
-5.2 中断处理机制
--Video
-5.3 中断下半部处理机制
--Video
-5.4 时钟中断机制
--Video
-5.5 动手实践-中断上半部的代码分析及应用
--Video
-5.6 动手实践-中断下半部的代码分析及应用
--Video
-第5章 中断--章节测验
-6.1 Linux中的各种API
--Video
-6.2 系统调用机制
--Video
-6.3 动手实践-添加系统调用(系统调用日志收集系统)
--Video
-第6章 系统调用--章节测验
-7.1 内核同步概述
--Video
-7.2 内核同步机制
--Video
-7.3 动手实践-内核多任务并发实例(上)
--Video
-7.4 动手实践-内核多任务并发实例(下)
--Video
-第7章 内核同步--章节测验
-8.1 虚拟文件系统的引入
--Video
-8.2 虚拟文件系统的主要数据结构
--Video
-8.3 文件系统中的各种缓存
--Video
-8.4 页高速缓存机制以及读写
--Video
-8.5 动手实践-编写一个文件系统(上)
--Video
-8.6 动手实践-编写一个文件系统(中)
--Video
-8.7 动手实践-编写一个文件系统(下)
--Video
-第8章 文件系统--章节测验
-9.1 设备驱动概述
--Video
-9.2 I/O空间管理
--Video
-9.3 设备驱动模型
--Video
-9.4 字符设备驱动程序简介
--Video
-9.5 块设备驱动程序简介
--Video
-9.6 动手实践-编写字符设备驱动程序
--Video
-9.7工程实践-编写块设备驱动的基础(上)
--Video
-9.8 工程实践-块设备驱动程序分析(中)
--Video
-9.9 工程实践-块设备驱动程序实现(下)
--Video
-第9章 设备驱动--章节测验
-致谢与说明
--Video