目录
链接
文件系统各组件的关系
处理器查询页表的过程
cache的一致性
内存管理大致流程
内存管理的伙伴系统
线性映射区
释放内存页面
假设不考虑libc的因素,malloc分配100Byte,那么实际上内核是为其分配100Byte吗?
假设使用printf打印指针bufA和bufB指向的地址是一样的,那么在内核中这两块虚拟内存是否“打架”了呢?
缺页中断
链接
-
链接可以认为是一个目录项,其中除了指向文件名的指针,并不存在其他数据。
-
目标文件删除时,符号链接仍然继续保存。
-
对每个符号链接都使用了一个独立的inode。相应inode的数据段包含了一个字符串,给出了链接目标的路径。
文件系统各组件的关系
处理器查询页表的过程
-
处理器根据页表基地址控制寄存器TTBCR和虚拟地址来判断使用哪个页表基地址寄存器,是TTBR0还是TTBR1。页表基地址寄存器中存放着一级页表的 基地址。
-
处理器根据虚拟地址的bit[31:20]作为索引值,在一级页表中找到页表项,一级页表一共有4096个页表项。
-
第一级页表的表项中存放有二级页表的物理基地址。处理器根据虛拟地址的bit[19:12]作为索引值,在二级页表中找到相应的页表项,二级页表有256个页表项。
-
二级页表的页表项里存放有4KB页的物理基地址,因此处理器就完成了页表的查询和翻译工作。
cache的一致性
处理器根据页表基地址控制寄存器TTBCR和虚拟地址来判断使用哪个页表基地址寄存器,是TTBR0还是TTBR1。页表基地址寄存器中存放着一级页表的 基地址。
处理器根据虚拟地址的bit[31:20]作为索引值,在一级页表中找到页表项,一级页表一共有4096个页表项。
第一级页表的表项中存放有二级页表的物理基地址。处理器根据虛拟地址的bit[19:12]作为索引值,在二级页表中找到相应的页表项,二级页表有256个页表项。
二级页表的页表项里存放有4KB页的物理基地址,因此处理器就完成了页表的查询和翻译工作。
MESI协议。
总线监听协议依赖于这样的事实,即所有的总线传输事务对于系统内所有的其他单元是可见的,因为总线是一个基于广播通信的介质,因而可以由每个处理器的 cache来进行监听。
内存管理大致流程
-
当了解物理内存和物理页面时,会接触到struct pg_ data_ t、struct zone和struct page等数据结构,这3个数据结构描述了系统中物理内存的组织架构。struct page数据结构除了描述一个4KB大小(或者其他大小)的物理页面外,还包含很多复杂而有趣的成员。
-
当了解怎么分配物理页面时,会接触到伙伴系统机制和页面分配器(pageallocator),页面分配器是内存管理中最复杂的代码之一。
-
有了物理内存,那怎么和虚拟内存建立映射关系呢?在Linux内核中,描述进程的虚拟内存用struct vm_area_struct 数据结构。虚拟内存和物理内存采用建立页表的方法来完成建立映射关系。为什么和进程地址空间建立映射的页面有的叫匿名页面,而有的叫pagecache页面呢?
-
当了解malloc()怎么分配出物理内存时,会接触到缺页中断,缺页中断也是内存管理中最复杂的代码之一。
-
这时,虚拟内存和物理内存已经建立了映射关系,这是以页为基础的,可是有时内核需要小于一个页面大小的内存,那么slab机制就诞生了。
-
上面已经建立起虚拟内存和物理内存的基本框图,但是如果用户持续分配和使用内存导致物理内存不足了怎么办?此时页面回收机制和反向映射机制就应运而生了。
-
虚拟内存和物理内存的映射关系经常是建立后又被解除了,时间长了,系统物理页面布局变得凌乱不堪,碎片化严重,这时内核如果需要分配大块连续内存就会变得很困难,那么内存规整机制(Memory Compaction)就诞生了。
当了解物理内存和物理页面时,会接触到struct pg_ data_ t、struct zone和struct page等数据结构,这3个数据结构描述了系统中物理内存的组织架构。struct page数据结构除了描述一个4KB大小(或者其他大小)的物理页面外,还包含很多复杂而有趣的成员。
当了解怎么分配物理页面时,会接触到伙伴系统机制和页面分配器(pageallocator),页面分配器是内存管理中最复杂的代码之一。
有了物理内存,那怎么和虚拟内存建立映射关系呢?在Linux内核中,描述进程的虚拟内存用struct vm_area_struct 数据结构。虚拟内存和物理内存采用建立页表的方法来完成建立映射关系。为什么和进程地址空间建立映射的页面有的叫匿名页面,而有的叫pagecache页面呢?
当了解malloc()怎么分配出物理内存时,会接触到缺页中断,缺页中断也是内存管理中最复杂的代码之一。
这时,虚拟内存和物理内存已经建立了映射关系,这是以页为基础的,可是有时内核需要小于一个页面大小的内存,那么slab机制就诞生了。
上面已经建立起虚拟内存和物理内存的基本框图,但是如果用户持续分配和使用内存导致物理内存不足了怎么办?此时页面回收机制和反向映射机制就应运而生了。
虚拟内存和物理内存的映射关系经常是建立后又被解除了,时间长了,系统物理页面布局变得凌乱不堪,碎片化严重,这时内核如果需要分配大块连续内存就会变得很困难,那么内存规整机制(Memory Compaction)就诞生了。
如果采用页表映射的方式,段映射表就变成一级映射表(在Linux内核中称为PGD),其表项提供的不再是物理段地址,而是二级页表的基地址。32位虚拟地址的高12位(bit[31:20])作为访问一级页表的索引值,找到相应的表项,每个表项指向一个二级页表。以虚拟地址的次8位(bit[19:12])作为访问二级页表的索引值,得到相应的页表项,从这个页表项中找到20位的物理页面地址。最后将这20位物理页面地址和虚拟地址的低12位拼凑在一起,得到最终的32位物理地址。
内存管理的伙伴系统
Linux系统采用伙伴系统解决外部碎片问题,采用slab解决内部碎片的问题。
伙伴系统的宗旨就是用最小的内存块来满足内核对于内存的请求。在最初,只有一个块,也就是整个内存,假如为1M大小,而允许的最小块为64K。那么当我们申请一块200k大小的内存时,就要先将1M的块分裂成两等分,各为512K,这两份之间的关系就称为伙伴,然后再将第一个512K的内存块分裂成两等分,各为256K,将第一个256K的内存块分配给内存,这样就是一个分配的过程。
线性映射区
内核空间只有1GB大小。这1GB的映射空间,其中有一部分用于直接映射物理地址,这个区域称为线性映射区。在ARM32平台上,物理地址[0:760MB]的这一部分内存被线性映射到[3GB:3GB+760MB]的虚拟地址上。
内核通常把物理内存低于760MB的称为线性映射内存,而高于760MB以上的称为高端内存。
vmalloc区域在ARM32内核中,从VMALLOC_START开始到VMALLOC_END结束,即从0xf0000000到0xff000000,大小为240MB。在VMALLOC_START开始之前有一个8MB的洞,用于捕捉越界访问。
释放内存页面
释放内存页面的核心功能是把页面添加到伙伴系统中适当的free_area链表中。在释放内存块时,会查询相邻的内存块是否空闲,如果也空闲,那么就会合并成一个大的内存块,放置到高一阶的空闲链表free_area中。如果还能继续合并并邻近的内存块,那么就会继续合并,转移到更高阶的空闲链表中,这个过程会一直重复下去,直至所有可能合并的内存块都已经合并。
假设不考虑libc的因素,malloc分配100Byte,那么实际上内核是为其分配100Byte吗?
假设不考虑libc库的因素,malloc分配100Byte,那么内核会分配多少Byte呢?处理器的MMU硬件单元处理最小单元是页,所以内核分配内存、建立虚拟地址和物理地址映射关系都是以页为单位,PAGE_ALIGN(addr)宏让地址addr按页面大小对齐。
假设使用printf打印指针bufA和bufB指向的地址是一样的,那么在内核中这两块虚拟内存是否“打架”了呢?
使用printf打印两个进程的malloc分配的虛拟地址是-一样的,那么内核中这两个虚拟地址空间会打架吗?其实每个用户进程有自己的一份页表,mm_ struct 数据结构中有一个pgd成员指向这个页表的基地址,在fork新进程时会初始化--份页表。每个进程有一个mm_struct数据结构,包含一个属于进程自己的页表、一个管理VMA的红黑树和链表。进程本身的VMA会挂入属于自己的红黑树和链表,所以即使进程A和进程B。使用malloc分配内存返回的相同的虛拟地址,但其实它们是两个不同的VMA,分别被不同的两套页表来管理。
缺页中断
在malloc()和mmap()两个用户态API函数的内核实现时,我们发现它们只建立了进程地址空间,在用户空间里可以看到虚拟内存,但没有建立虚拟内存和物理内存之间的映射关系。当进程访问这些还没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常,Linux内核必须处理此异常。



