从物理上需要关注物理架构,RAM的组织管理/分配/页结构,cache,cpu_cache。内存分配设计到伙伴系统、slab。从逻辑上涉及到虚拟内存,页表,地址空间。二者的使用涉及到缺页,回收,交互,共享内存等内容
物理架构
cpu之间走FSB,cpu与NB走FSB。与RAM走NB。
dma->nb->ram
DRAM,FB-DRAM(RAM与nd多条),一般用于主存,SRAM用于cpuchache
=》
NB的mc独立可多条并发
=》
mc内置于cpu
对称SMP,NUMA(非统一资源),NUMA因子(远端内存代价)
RAM
RAM
RAM分三个管理区:
ZONE_DMA:可用作DMA的内存区域。该类型的内存区域在物理内存的低端,主要是ISA设备只能用低端的地址做DMA操作。
ZONE_NORMAL:直接被内核直接映射到自己的虚拟地址空间的地址。
ZONE_HIGHMEM:不能被直接映射到内核的虚拟地址空间的地址。
在分配时的管理区分配器:buddy/slab。还有pre-cpu page。
图中为整体架构,进程映射到RAM,RAM的内存结构是memap,每个页是一个元素,还可能会映射到swap。三个管理区有自己的管理区分配器。
-
分配:
1.alloc_pages 整个页
2.kmalloc 字节 连续内存虚拟地址,物理地址也连续
3.vmalloc 虚拟连续,物理不一定,建立页表项需要一个一个的映射
4.slab层,防止每个进程自己单独创建free链,不能全局判断高低度缓冲组。kmem_cache。每个类型比如task_struct,inode,通用的。3个链表empty,full,parti kmem_Cache_alloc.内存紧缺/显示撤销才释放
5.高端内存(页并不一定永久映射,比如内核1G要访问8G物理内存,动态映射)
kmap。可睡眠,永久映射 kmap_atomic 临时,原子,不睡眠,不阻塞,禁止内核抢占
6.cpu上数据,alloc_percpu
page
内存结构最外层是pglist_data,对于一个RAM的就只有一个,SMP的多个RAM是个数组。分为三个区域来管理,每个包含很多页,所有RAM对应mem_map。
zone中有极值:pages_min, pages_low, pages_high;来控制zone中页面的回收
有等待区队列,一个区一个,页hash映射唤醒(每页一个占空间,一个zone一个惊群,所以hash映射唤醒,当有hash冲突时会环境多个进程)
Page有两个重要字段,address_space为cache的结构。非cache的private
struct {
unsigned long private;
struct address_space *mapping;
};
每个物理页一个结构 4G会占用20M 被用户空间进程/动态分配内核数据/静态内核代码/页高速缓存等引用
flag 脏/锁 ;count引用计数,0空闲 ;*virtual 虚拟地址
cache
cache是对外设或者文件或者其他的缓存,是RAM的一部分,从分类上大概有buffer /page/swap/硬件,还有Inode/目录等cache。这些都是在别处有副本的。buffer是针对块设备,page基本基于与文件或基于其他page管理的缓存,我们用free命令可以看到buff/cache,used。used指的就是非cache RAM的使用量。在mmap不同种类时用到的不一样,比如共享匿名映射时,这时是从cache中申请内存,在进行匿名私有映射时,并没有占用cache,文件映射,则都是将文件映射到cache中,然后根据共享还是私有进行不同的操作,私有的会再used增加一份;
page cache本质上都是文件的缓存,大多数可清理 这种时候放cache中作为缓存、那为何比如共享内存映射也在呢,因为他本质上也是特殊文件系统shm中的一个文件,shm的安装点在交换区上,类似的还有tmp.proc等这种基于内存的文件。对于私有内存映射 非基于文件 的不走cache。至于回收(无论cache与否都一样):页框回收算法必须处理多种属于用户态进程、磁盘高速缓存和内存高速缓存的页,而且必须遵照几条试探法准则只有走缓存的才有address_space否则就private
fd与磁盘的关联
fd->file->path->dentry->inode->address_space->page->buffer_head->磁盘块号
cpucache
缓存线:64字节长。组关联(外层查找,内层地址置换mmap) tag-cache set-offset
cpu都有独立调的1级,共享2/3级等可能有点诧异。l1一般是虚拟地址,其他是物理地址。sram
主存-l3-l2-l1-cpu。
数据结构:
性能
1.组关联,段大小,cache size,数据集大小(对不同cache的占用)
2.顺序访问单线程
数据大小,预取(下面图预取超过缓存,不生效),TLB
3.随机访问 无预取,大小,TLB
4.写入 写回/写入合并/不缓存等策略的影响
5.多处理器 MESI
6.多线程
7.超线程
写入:写回策略,打开dirty标记,缓存线丢弃时,通知写回主存,每个写通/删除对FSB压力大
多处理器一致性:MESI 状态变更,
If a Modified cache line is read from or written to on the local processor, the instruction can use the current cache content and the state does not change. If a second processor wants to read from the cache line the firs processor has to send the content of its cache to the second processor and then it can change the state to Shared.The data sent to the second processor is also received and processed by the memory controller which stores the content in memory. If this did not happen the cache line could not be marked as Shared. If the second processor wants to write to the cache line the first processor sends the cache line content and marks the cache line locally as Invalid. This is the infamous “Request For Ownership” (RFO) operation. Performing this operation in the last level cache, just like the I→M transition is comparatively expensive. For write-through caches we also have to add the time it takes to write the new cache line content to the next higher-level cache or the main memory, further increasing the cost.
numa
互连
内存利用率-》带条化,自由迁移进程
迁移
一致性
优化
1.不缓存
2.充分利用缓存
行预取
64字节。double可以8个一读
对齐 占尽量少的缓存行,经常用到的组合在一起
l1级指令。比如inline的取舍
TLB优化。减少页数/减少页目录级数
3.预取
硬件/软件/单独线程(超线程/dumber)/directcacheaccess 设置标记让DMA可以直接写入cpucache
4.多线程优化
组读写变量,局部变量等
原子、原子运算、cas
cpu亲和性等
工具
https://people.freebsd.org/~l...
buddy
为了便于页面的维护,将多个页面组成内存块
每个大小为2^n。当没有自己的size到大size拆分
每个区有自己的buddy
固定2^n这种内存分配会产生碎片化,但是在内存管理这不能随便有权限像其他的一样定期整理,因此采取反碎片化的分配方式,对页面进行分类,https://blog.csdn.net/goodluc...
slab
发现内核对象初始化代价比分配大,对对象进行缓存
一个cache管理一组大小固定的内存块(也称为对象实体),每个内存块都可用作一种数据结构。cache中的内存块来自一到多个slab。一个slab来自物理内存管理器的一到多个物理页
每个缓存结构都包括了两个重要的成员:
struct kmem_list3 **nodelists:kmem_list3结构中包含了三个链表头,分别对应于完全用尽的slab链表,部分用尽的slab链,空闲的slab链表,其中部分空闲的在最开始
struct array_cache *array[NR_CPUS + MAX_NUMNODES]:array是一个数组,系统中的每一个CPU,每一个内存节点都对应该数组中的一个元素。array_cache结构包含了一些特定于该CPU/节点的管理数据以及一个数组,每个数组元素都指向一个该CPU/节点刚释放的内存对象。该数组有助于提高高速缓存的利用率。
当释放内存对象时,首先将内存对象释放到该数组中对应的元素中
申请内存时,内核假定刚释放的内存对象仍然处于CPU高速缓存中,因而会先从该数组的对应数组元素中查找,看是否可以申请。
当特定于CPU/节点的缓存数组是空时,会用slab缓存中的空闲对象填充它
CPU访问内存时使用哪个cache line是通过低地址的若干位确定的,比如cache line大小为32,那么是从bit5开始的若干位。因此相距很远的内存地址,如果这些位的地址相同,还是会被映射到同一个cache line。Slab cache中存放的是相同大小的对象,如果没有着色区,那么同一个cache内,不同slab中具有相同slab内部偏移的对象,其低地址的若干位是相同的,映射到同一个cache line。访问cache line冲突的对象时,就会出现cache miss,通过着色区使对象的slab内偏移各不相同
虚拟内存
为何要有虚拟内存?
进程地址空间不隔离=》分段
程序运行的地址不确定 =》分段
内存使用效率低=》分页
地址映射
逻辑地址+段基地址(linux都是0,)=》线性地址
ds值为0x7b, 转换成二进制为 00000000 01111011,TI:0(GDT),GDT:15(用户数据段描述符)。
线性地址=》物理地址。页表转换
页表
内核临时页表
linux启动分两个阶段:第一个阶段(汇编)建立分段机制(忽略),建立一个临时页表进入分页机制。第二个阶段(C语言)初始化系统的各种资源(硬件/软件)。
需要映射进页表的内存包括:linux内核。临时页表(这个不一定需要映射进页表,但是为了方便也这么做了)。128k的临时内存管理系统(其实就是用的位图管理1G的低端内存,2^32/4096/8 = 128K)共占4M内核代码+其他 映射8M
linux内核的临时页表有两层。因为0xC和0x0都要访问内核代码,外层在512~1024之间需要10位表示,4K的页需要偏移位12位。剩10位足以表示4M内存(4M/4K=1024页)。因此外层两个-》1024个 10-10-12来映射
进程页表/内核最终页表
4M 2级
4G 需要3级
其他……
页表优化
随机化安全 VS 顺序性能
优化 TLB 标签附加扩展并唯一标识其涉及页表树的刷入刷出
页面大 TLB少,装入多。页表树级数少,页面偏移位部分增大,页目录少,内存要连续/对其会造成浪费
地址空间
这是一个完成的地址空间到物理页的关系图。右侧为进程地址空间,在32位中,上部3G-4G位共享的内核空间,下层为用户空间。
最外层结构是task_struct中的mm_struct。里边包含pgd和mmap。pdg的指针会存在cr3中。mmap由很多vm_are_struct组成,每个用户空间分区都是一个vm,堆申请也是一个vm。通过页表会定位到memap映射的pagge。
内核地址空间
分为直接映射/高端内存
高端内存用于vmalloc。持久映射区域/固定映射区域
虚拟地址中连续,但是物理地址不连续的内存区域可以从VMALLO区域分配
持久映射区域用于将高端内存中的非持久页映射到内核中
固定映射用于与物理地址空间中的固定页关联的虚拟地址页,但是物理地址页即页帧可以自由选择
Linux将内核地址空间划分为三部分ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM,高端内存HIGH_MEM地址空间范围为 0xF8000000 ~ 0xFFFFFFFF(896MB~1024MB)。那么如内核是如何借助128MB高端内存地址空间是如何实现访问可以所有物理内存?
当内核想访问高于896MB物理地址内存时,从0xF8000000 ~ 0xFFFFFFFF地址空间范围内找一段相应大小空闲的逻辑地址空间,借用一会。借用这段逻辑地址空间,建立映射到想访问的那段物理内存(即填充内核PTE页面表),临时用一会,用完后归还。这样别人也可以借用这段地址空间访问其他物理内存,实现了使用有限的地址空间,访问所有所有物理内存。
进程地址空间
在讲进程时已经详细讲解了。这里说下vm。以红黑树组织
缺页
当用户访问一个页时,MMU回去查询一个操作系统维护的页表,看看用户访问的页是否存在于内存当中,如果存在,就直接调用;如果没有,就需要开启中断,执行页错误处理程序,在磁盘中找到相应的数据页,并在内存中找到一块空闲,将其载入进来,然后中断返回,执行用户程序,在去访问内存中用户需要的页。
交换
非映射页在磁盘上提供备份(磁盘分为文件/交换/裸设备)
交换子系统的主要功能总结如下:
1) 在磁盘上建立交换区(swap area),用于存放没有磁盘映像的页。
2) 管理交换区空间。当需求发生时,分配与释放页槽(page slot)。
3) 提供函数用于从RAM 中把页换出(swap out)到交换区或从交换区换入(swap in)到RAM中。
4) 利用页表项(现已被换出的换出页页表项)中的换出页标识符跟踪数据在交换区中的位置。
标识一个换出页,在swap_info 数组中指定交换区的索引和在交换区内指定页槽的索引,
当页被换出时,其标识符就作为页的表项插入页表中
竟然看到有人提到自己老师哈哈。可信老师的课件已经不免费了
https://blog.csdn.net/iostrea...
页回收
回收时机
page_min:如果空闲页数目小于该值,则该zone非常缺页,页面回收压力很大。
page_low: 如果空闲页数目小于该值,kswapd线程将被唤醒,并开始释放回收页面。
page_high: 如果空闲页面的值大于该值,则该zone的状态很完美, kswapd线程将重新休眠。
回收策略
linux的LRU active/inactive
会根据不同页情况做不同的策略。比如:
进程映射的页:max_mapped为0 换出
锁定:调过
脏页:回写等
……
反向映射
解决如何看一个共享页是否可以被释放?
匿名页:双向链表
文件映射:优先搜索树
子节点的堆索引都不大于相应父节点的堆索引。任意一个节点的左子节点基索引也都不大于右子节点基索引,如果基索引相等,则按照大小索引排序。
基索引(radix index)和堆索引(heap index)两个索引来标识。基索引表示区间的起始点而堆索引表示终点
共享内存
我们在说共享内存的时候。包含mmap和posix共享内存。要做到共享mmap只能在磁盘创建文件,posix是会在tmpfs上,内核持久性,shm_open+mmap。更多参考https://segmentfault.com/a/11... ,https://segmentfault.com/a/11...
进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常
对于共享内存映射情况,缺页异常处理程序首先在swap cache中寻找目标页(符合address_space以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区(swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。进程最终将更新进程页表。
注:对于映射普通文件情况(非共享映射),缺页异常处理程序首先会在page cache中根据address_space以及数据偏移量寻找相应的页面。如果没有找到,则说明文件数据还没有读入内存,处理程序会从磁盘读入相应的页面,并返回相应地址,同时,进程页表也会更新。
5.所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面。
注:一个共享内存区域可以看作是特殊文件系统shm中的一个文件,shm的安装点在交换区上
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。