疲劳期来了,看不动了
硬件
内存硬件包括主存、总线、CPU缓存和MMU(内存管理单元)。
对于这个硬件的划分我更倾向于是内存涉及到这些组件,不然多少和我理解的计算机结构有点出入,但这个不重要
延时 主存的访问时间可以用CAS(列地址控制器)计量:从发送需要读取的地址(列)给一个内存模块,到数据可以被读取之间的时间。这个数值取决于内存类型(DDR3大约是3ns)。对于内存I/O传输,内存总线(如64b宽)为了传输一个缓存行(64B宽)会发生多次此类延时。
主存架构
UMA
下图所示为普通双处理器均匀访存模型系统的主存架构。通过共享系统总线,每个CPU访问所有内存都有均匀的访存延时。
在此机构上运行的是单个操作系统实例,并可以在所有处理器上同一运行时,又称为对称多处理器架构(SMP)
NUMA
下图所示为双处理器非均匀访问模型系统,其中CPU互联是内存架构的一部分,在此种架构中对主存的访问时间随着相对CPU的位置不同而变化。
本地内存 CPU可以通过它内存总线直接发起I/O操作的内存,如DRMA A对于CPU1
远程内存 CPU通过CPU互联以及连接的其他CPU发起I/O操作的内存,如DRMA B 对于CPU1
连接到每个CPU的内存组被称为内存节点,基于处理器提供的信息,操作系统可以获得内存节点拓扑。操作系统可以根据内存本地性分配和调度线程,尽可能倾向于本地内存以提高性能。
总线
物理上主存如何连接系统取决于主存架构,实际的实现会涉及额外的CPU与内存之间的控制器和总线。
可能的访问方式:
- 共享系统总线:单个或多个处理器通过一个共享的系统总线,一个内存桥控制器以及内存总线
- 直连:单个处理器通过内存总线直接连接内存
- 互联:多个处理器中的每一个通过一条内存总线与各自的内存直连,并且处理器之间通过一个CPU互联连接起来。
DDR SDRAM
就一张图带过吧,参考
多通道、CPU缓存 这两个就略了
MMU
内存管理单元(MMU)负责虚拟到物理地址的转换,它按页做转换,而页内的偏移量直接映射。
下图截取自易百教程
多种页大小、TLB(Translation Lookaside Buffer)也略了吧
软件
内存管理软件包括虚拟内存系统、地址交换、交换、换页和分配。与性能密切相关的内容包括:内存释放、空闲链表、页扫描、交换、进程地址空间和内存分配器
内存释放
系统中可用内存过低时,内核有多种方法释放内存并添加到页空闲链表中,下图展示了可用内存降低时这些方法通常的调用次序
- 空闲链表:一个未使用的页列表(空闲内存),能立刻用于分配;通常的实现时多个空闲页链表,每个本地组(NUMA)一个;
- 回收(收缩):内存低于某个阈值,内核模块和内核分配器会立刻释放任何可以轻易释放的内存
linux中具体方法如下:
- 页缓存:文件系统缓存;通过调节交换倾向参数决定时通过换页还是交换来释放内存
- 交换:页面换出守护进程(kswapd)执行的换页;找出最近不使用的页并加入到空闲链表,其中包括应用程序内存。页面换出涉及写入文件系统或者一个交换设备,仅在配置了交换文件或设备时才可用
- OOM:内存耗尽搜索并杀死可牺牲的进程以释放内存;采用select_band_process()搜索,用oom_kill_process()杀死进程;在系统日志/var/log/message中以“Out of memory: Kill process”表现
空闲链表
释放的内存添加到表头以便将来分配
通过页面换出守护进程释放的内存,可能包含有价值的文件系统页缓存——被添加到表尾;如果在未被重用前有对任一页的请求,他能被取回并从空闲链表中移除。
这个空闲链表的用法还很巧妙哈,理解成一个双向链表结构,正常的使用情况应该是类似栈,后进先出,但是如果是系统文件缓存页则是添加到链表尾,在用到这个缓存页时是从链表中删除,这个还挺有意思的
回收
从内核的slab分配器缓存释放内存。
页扫描
内核页面换出守护进程管理利用换页释放内存,当主存中可用的空闲链表低于阈值时,页面换出守护进程会开始页扫描。
页扫描仅按需启动,通常平衡的系统不会经常做页扫描并且仅以短期爆发方式扫描
进程地址空间
进程地址空间是一段范围的虚拟页,有硬件和软件同时管理,按需映射到物理页。这些地址被划分为段以存放线程栈、进程可执行(文件)、库和堆。
应用程序可执行段包括分离的文本和数据段;库也由分离的可执行文件和数据段组成。
- 可执行文本:包括可执行的jinchengCPU指令。由文件系统中的二进制应用程序文本段映射而来,它是只读并带有执行权限;
- 可执行数据:包括已初始化的变量,由二进制应用程序的数据段映射而来,有读写权限
- 堆:应用程序的临时工作内存并且是匿名内存(无文件系统位置),按需增长并且用mollac()分配
- 栈:运行中的线程栈,映射为读写
堆增长
对于大多数分配器,free()不会将内存还给操作系统,相反会保留它们以备将来分配。这意味着进程的常驻内存只会增长,并且是正常现象。
进程所见内存的方法:
- Re-exec:从空的地址空间调用exec()
- 内存映射:使用mmap() 和 munmap()会归还内存到系统
分配器 应该是很重要的一个点,但是原著只是简单的进行描述,我这里就略了吧