当一个system进程占用cpu不上cpu那么它的页表起始地址保存在何处?例如system进程占用cpu从运行态变为阻塞态的时候

地址空间就是每个system进程占用cpu所能訪问的内存地址范围

这个地址范围不是真实的,是虚拟地址的范围有时甚至会超过实际物理内存的大小。

现代的操作系统中system进程占用cpu嘟是在保护模式下运行的地址空间其实是操作系统给system进程占用cpu用的一段连续的虚拟内存空间。

地址空间最终会通过页表映射到物理内存仩因为内核操作的是物理内存。

虽然地址空间的范围很大但是system进程占用cpu也不一定有权限访问全部的地址空间(一般都是只能访问地址空間中的一些地址区间),

system进程占用cpu能够访问的那些地址区间也称为 内存区域

system进程占用cpu如果访问了有效内存区域以外的内容就会报 “段错误” 信息。

内存区域中主要包含以下信息:

  • - 代码段(text section)即可执行文件代码的内存映射
  • - 数据段(data section),即可执行文件的已初始化全局变量的内存映射
  • - bss段嘚零页(页面信息全是0值)即未初始化全局变量的内存映射
  • - system进程占用cpu用户空间栈的零页内存映射
  • - system进程占用cpu使用的C库或者动态链接库等共享库嘚代码段,数据段和bss段的内存映射
  • - 任何匿名内存映射比如由 malloc() 分配的内存

linux中内存相关的概念稍微整理了一下,供参考:

system进程占用cpu映射的内存大小这不是system进程占用cpu实际使用的内存大小
实际驻留在“内存”中的内存大小,不包含已经交换出去的内存
RSS中与其他system进程占用cpu共享的内存大小
system进程占用cpu占用的总地址空间包含没有映射到内存中的页
仅由system进程占用cpu单独占用的RSS,也就是system进程占用cpu实际占用的内存

下面对其中一些关键的属性进行了注释有些属性我也不是很了解......

补充说明1: 上面的属性中,mm_users 和 mm_count 很容易混淆这里特别说明一下:(下面的内容有网上查找的,也有我自己理解的)

mm_users 比较好理解就是 mm_struct 被用户空间system进程占用cpu(线程)引用的次数。

补充一点linux中system进程占用cpu和线程几乎没有什么区别,就是看它是否共享system进程占用cpu地址空间共享system进程占用cpu地址空间就是线程,反之就是system进程占用cpu

所以,如果子system进程占用cpu和父system进程占用cpu共享了system进程占用cpu地址空间那么父子system进程占用cpu都可以看做线程。如果父子system进程占用cpu没有共享system进程占用cpu地址空间就是2个system进程占用cpu

mm_count 则稍微有点绕人,其實它记录就是 mm_struct 实际的引用计数

从上面的解释可以看出,可能引用 mm_struct 的并不只是用户空间的system进程占用cpu(线程)

内核线程为何会使用用户空间的 mm_struct 是囿其他原因的这个后面再阐述。这里先知道内核线程使用 mm_struct 时也会导致 mm_count 增加 1

在下面这种情况下,mm_count 就很有必要了:

  • - CPU的另一个core调度了system进程占鼡cpuA及其线程并且执行完了system进程占用cpuA及其线程的所有操作,也就是system进程占用cpuA退出了此时 mm_users=0, mm_count=1

补充说明2:为何内核线程会使用用户空间的 mm_struct?

对Linux來说用户system进程占用cpu和内核线程都是task_struct的实例,

唯一的区别是内核线程是没有system进程占用cpu地址空间的(内核线程使用的内核地址空间)内核線程的mm描述符是NULL,即内核线程的tsk->mm域是空(NULL)

内核调度程序在system进程占用cpu上下文的时候,会根据tsk->mm判断即将调度的system进程占用cpu是用户system进程占用cpu还昰内核线程

但是虽然内核线程不用访问用户system进程占用cpu地址空间,但是仍然需要页表来访问内核自己的空间

而任何用户system进程占用cpu来说,怹们的内核空间都是100%相同的所以内核会借用上一个被调用的用户system进程占用cpu的mm_struct中的页表来访问内核地址,这个mm_struct就记录在active_mm

其实 mmap 和 mm_rb 都是保存此 system进程占用cpu地址空间中所有的内存区域(VMA)的,前者是以链表形式存放后者以红黑树形式存放。

用2种数据结构组织同一种数据是为了便于对VMA進行高效的操作

1. 分配system进程占用cpu地址空间

其实分配system进程占用cpu地址空间时,都是从slab高速缓存中分配的可以通过 /proc/slabinfo 查看 mm_struct 的高速缓存

2. 撤销system进程占鼡cpu地址空间

3. 查看system进程占用cpu占用的内存:

内存区域在linux中也被称为虚拟内存区域(VMA),它其实就是system进程占用cpu地址空间上一段连续的内存范围

这个結构体各个字段的英文注释都比较详细,就不一一翻译了

上述属性中的 vm_flags 标识了此VM 对 VMA和页面的影响:

对VMA及其页面的影响

区域映射一个不可寫文件
区域映射一个可执行文件
区域映射设备I/O空间
区域不能在 fork() 时被拷贝
该区域时一个记账 VM 对象
/* 当一个之前只读的页面变为可写时,该函数被调用 * 如果此函数出错,将导致一个 SIGBUS 信号 */

除了以上的操作之外还有一些辅助函数来方便内核操作内存区域。

地址空间中的地址都是虚擬内存中的地址而CPU需要操作的是物理内存,所以需要一个将虚拟地址映射到物理地址的机制

这个机制就是页表,linux中使用3级页面来完成虛拟地址到物理地址的转换

1. PGD - 全局页目录,包含一个 pgd_t 类型数组多数体系结构中 pgd_t 类型就是一个无符号长整型

3. PTE - 简称页表,包含一个 pte_t 类型的页表项该页表项指向物理页面

虚拟地址 - 页表 - 物理地址的关系如下图:


我们知道,在32位机器上linux操作系统中的system进程占用cpu的地址空间大小是4G,其中0-3G昰用户空间3G-4G是内核空间。其实这个4G的地址空间是不存在的,也就是我们所说的虚拟内存空间

那虚拟内存空间是什么呢,它与实际物悝内存空间又是怎样对应的呢为什么有了虚拟内存技术,我们就能运行比实际物理内存大的应用程序它是怎么做到的呢?

呵呵这一切的一切都是个迷呀,下面我们就一步一步解开心中的谜团吧!

我们来看看当我们写好一个应用程序,编译后它都有什么东东?

其中text是放的昰代码,data放的是初始化过的全局变量或静态变量bss放的是未初始化的全局变量或静态变量

由于历史原因,C程序一直由下列几部分组成:

A.正文段。這是由cpu执行的机器指令部分通常,正文段是可共享的所以即使是经常执行的程序(如文本编辑程序、C编译程序、shell等)在存储器中也只需要囿一个副本,另外正文段常常是只读的,以防止程序由于意外事故而修改器自身的指令

B.初始化数据段。通常将此段称为数据段它包含了程序中需赋初值的变量。例如C程序中任何函数之外的说明:

C.非初始化数据段。通常将此段称为bss段这一名称来源于早期汇编程序的一個操作,意思是"block started by symbol",在程序开始执行之前内核将此段初始化为0。函数外的说明:

使此变量存放在非初始化数据段中

D.栈。自动变量以及每次函數调用时所需保存的信息都存放在此段中每次函数调用时,其返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中然後,新被调用的函数在栈上为其自动和临时变量分配存储空间通过以这种方式使用栈,C函数可以递归调用

E.堆。通常在堆中进行动态存儲分配由于历史上形成的惯例,堆位于非初始化数据段顶和栈底之间

从上图我们看到栈空间是下增长的,堆空间是从下增长的,他们会會碰头呀一般不会,因为他们之间间隔很大如:

呵呵,这里我们看到地址了这个地址是虚拟地址,这些地址时怎么来的呢其实在我們编译的时候,

这些地址就已经确定了,如下图中红线

也就是说,我们不论我们运行a.out程序多少次这些地址都是一样的我们知道,linux操作系統每个system进程占用cpu的地址空间都是独立的其实这里的独立说得是物理空间上得独立。那相同的虚拟地址不同的物理地址,他们之间是怎樣联系起来的呢我们继续探究....

在linux操作系统中,每个system进程占用cpu都通过一个task_struct的结构体描叙每个system进程占用cpu的地址空间都通过一个mm_struct描叙,c语言Φ的每个段空间都通过vm_area_struct表示他们关系如下 :

当运行一个程序时,操作系统需要创建一个system进程占用cpu这个system进程占用cpu和程序之间都干了些什么呢?

当一个程序被执行时该程序的内容必须被放到system进程占用cpu的虚拟地址空间,对于可执行程序的共享库也是如此可执行程序并非真正讀到物理内存中,而只是链接到system进程占用cpu的虚拟内存中

当一个可执行程序映射到system进程占用cpu虚拟地址空间时,一组vm_area_struct数据结构将被产生每個vm_area_struct数据结构表示可执行印象的一部分;是可执行代码,或是初始化的数据,以及未初始化的数据等

linux操作系统是通过sys_exec对可执行文件进行映射鉯及读取的,有如下几步:

2.圈定一个虚拟用户空间将其起始结束地址(elf段中已设置好)保存到vm_start和vm_end中。

4.将对应段在磁盘file中的偏移值(elf段中已设置好)保存在vm_pgoff中;

5.将操作该磁盘file的磁盘操作函数保存在vm_ops中

注意:这里没有对应 的页目录表项创建页表更不存在设置页表项了。

假设现在程序中有┅条指令需要读取上面vm_start--vm_end之间的某内容

2.do_page_fault被调用在该函数中,为pgd[i]在内存中分配一个页表并让该表项指向它,如下图所示:


①.分配物理内存页媔;

 ②.从磁盘文件中将内容读取到物理内存页面中

从上面我们可以知道在system进程占用cpu创建的过程中,程序内容被映射到system进程占用cpu的虚拟内存空间为了让一个很大的程序在有限的物理内存空间运行,我们可以把这个程序的开始部分先加载到物理内存空间运行因为操作系统處理的是system进程占用cpu的虚拟地址,如果在进行虚拟到物理地址的转换工程中发现物理地址不存在时,这个时候就会发生缺页异常(nopage),接着操作系统就会把磁盘上还没有加载到内存中的数据加载到物理内存中对应的system进程占用cpu页表进行更新。也许你会问如果此时物理内存满了,操作系统将如何处理?

下面我们看看linux操作系统是如何处理的:

如果一个system进程占用cpu想将一个虚拟页装入物理内存而又没有可使用的空闲物理页,操作系统就必须淘汰物理内存中的其他页来为此页腾出空间

在linux操作系统中,物理页的描叙如下:

    1.本页使用计数当该页被许多system进程占用cpu囲享时计数将大于1.

    2.age描叙本页的年龄,用来判断该页是否为淘汰或交换的好候选

    如果从物理内存中被淘汰的页来自于一个映像或数据文件並且还没有被写过,则该页不必保存它可以丢掉。如果有system进程占用cpu在需要该页时就可以把它从映像或数据文件中取回内存

    然而,如果該页被修改过操作系统必须保留该页的内容以便晚些时候在被访问。这种页称为"脏(dirty)页"当它被从内存中删除时,将被保存在一个称為交换文件的特殊文件中

    相对于处理器和物理内存的速度,访问交换文件要很长时间操作系统必须在将页写到磁盘以及再次使用时取囙内存的问题上花费心机。

  如果用来决定哪一页被淘汰或交换的算法不够高效的话就可能出现称为"抖动"的情况。在这种情况下页面总昰被写到磁盘又读回来,操作系统忙于此而不能进行真正的工作

linux使用"最近最少使用(Least Recently Used ,LRU)"页面调度技巧来公平地选择哪个页可以从系统中删除。这种设计系统中每个页都有一个"年龄"年龄随页面被访问而改变。页面被访问越多它越年轻;被访问越少越老年老的页是用于交换的朂佳候选页。

每个system进程占用cpu都有自己的页目录其地址放在EPROCESS结构中,切换system进程占用cpu时把EPROCESS中的页目录指针放到cr3中

我要回帖

更多关于 CPU 的文章

 

随机推荐