IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux 内存管理简介 -> 正文阅读

[系统运维]Linux 内存管理简介

  1. struct page :
    描述的是一个物理页, 内存管理的最小单元, 只描述的是物理页面本身, 程序和系统运行,存在数据的换入换出, 因此page中的数据和page无本质的联系.

     数据结构定义在 :
     		include/linux/mm_types.h
     			主要内容:
     					(1) 页面状态(page-flags.h);
     					(2) 页的类型:
     						a. page cache/anonymous page;
     						b. page_pool used by netstak(DMA address);
     						c. slab, slob and slub;
     						d. slab kmem_cache (高速缓存);
     						e. compound page(复合页, 多个物理页组成的大页);
     						f. Page table page;
     						g. ZONE_DEVICE pages;
     					(3) 页面引用计数(page_ref.h);
     					(4) memory cgroup;
     					(5) 页面虚拟地址(asm/page.h);	
    
  2. struct zone :

     由于硬件的限制, 内核并不能堆所有的内存页都一视同仁, 有些页位于内存中的特定物理地址上, 用于特定用途, 内核将具有相同特性的页分组, 形成不同的zone.
     
     硬件缺陷或者局限性,导致的寻址问题:
     	(1) 一些硬件只能在特定地址执行DMA ;
     	(2) 一些体系结构下物理内存比虚拟内存大得多, 一些内存就不能永久映射到内存;
     		ZONE_DMA : 包含的页执行DMA 操作;
     		ZONE_DMA32: 32位 DMA 操作;
     		ZONE_NORMAL: 正常映射的页;
     		ZONE_HIGHMEM: 高端内存, 不能永久映射到内核地址空间的页;
     
     数据结构定义在 :
     		include/linux/mmzone.h
    
     各个zone的初始化:
     	kernel/mm/page_alloc.c
    
  3. 获得页

     include/linux/gfp.h:
     	(1) alloc_pages() // 获取2的幂函数个连续的物理页;
     	(2) page_address() // 物理地址转换为虚拟地址
     	(3) __get_free_pages() // 获取第一个页的虚拟地址(和alloc_pages()作用相同);
     	(4) alloc_page() / __get_free_page() //获取一页
     	(5) get_zeroed_page() // 获取填充为0 的页(和__get_free_pages()类似);
     	(6) 以上都有对应的释放函数;
    
  4. 获取以字节为单位的内核内存

     kmalloc()
     	分配的内存是物理上连续的.
    
     分配器标志:
     		定义在 :
     			linux/gfp.h
     			linux/slab.h
     		
     	(1) 行为修饰符
     		分配器是否可以睡眠, 是否可以阻塞, 是否可以执行磁盘IO , 文件系统IO等.
     	
     	(2) 区修饰符
     		分配器从 DMA/DMA32/HIGHMEM/NORMAL zone上分配
     		
     	(3) 类型修饰符
     		综合了以上行为/区修饰符, 定义的一种简易的修饰符, 也即常用的标识符.
     		GFP_KERNEL / GFP_ATOMIC等 
    
     	vmalloc()
     		
     		定义:
     			linux/vmalloc.h
     			mm/vmalloc.c
     			
     			和kmalloc()类似, 申请内存在虚拟地址上是连续的,在物理地址上不连续.
     			可通过非连续的物理地址,  修改页表, 确保在虚拟地址上连续.
     			一般,只有硬件设备需要得到物理地址连续的内存, 软件使用的是连续的虚拟地址即可.
     			
     			内核编程中, 一般使用kmalloc(), 而不是vmalloc(), 处于性能考虑:
     			(1) malloc申请的内存物理地址不连续, 为了虚拟地址上连续, 需要将物理地址上不连续的物理地址转换为虚拟地址上连续的;
     			(2) 需重新建立页表;
     			(3) 比直接内存映射更多的TLB 抖动;
     			 
     			一般, 在不得已下使用vmalloc(), 需要获取大块内存时, 比如动态装载内核模块
    
  5. slab

    背景:
    内核中数据结构的分配和释放是最常用的操作之一, 为了便于数据的频繁分配和回收, 使用到了空闲链表, 事先分配好的,可供使用的数据块结构, 需要使用时候, 从链表获取, 不用时, 放回链表,不释放, 从某种程度上讲, 实现了高速缓存的目的.

     使用过程中, 面临着问题:
     	(1) 不能全局控制, 当内存不足时, 内核无法通知每个空闲链表 , 并让他释放链表, 回收内存空间;
     	(2) 空闲链表也只是内存空间中的一些活跃的内存数据, 也无法特别识别他们;
    

    综上, slab分配器诞生了, 设计时应当遵循如下基本原则:
    (1) 频繁使用的数据结构也会频繁分配和释放, 缓存他们;
    (2) 缓存会连续的放, 释放后会放回缓存, 所以即使频繁分配和释放, 也不会导致内存碎片;
    (3) 缓存能提高性能, 释放的内存对象会立即投入下一次分配, 频繁的分配和释放, 不会导致性能衰减;
    (4) 如果分配器明确内存对象的大小,页大小和高速缓存的大小等信息, 可以作出更加明智的决策;
    (5) 如果部分缓存专属于单个处理器, 分配和释放就可以不用加SMP锁;
    (6)如果分配器和NUMA相关, 可以从相同的内存节点进行分配;
    (7) 对存放的对象进行着色, 防止多个内存对象映射到 相同的高速缓存行;

  6. slab设计
    slab将内存对象分为不同的高速缓存组, 每个组作为不同类型的用途, 相同使用类型的申请者 可在对应类型的高速缓存组中申请和释放高速缓存对象.
    高速缓存被划分为 slab, 每个slab由一个或多个物理页(成员对象)组成,一般是一个页, 每个高速缓存由多个slab组成.

     slab三种状态:
     (1)满: 没有空闲对象;
     (2)部分满: 有部分空闲对象;
     (3) 空: 空闲对象均可使用
    

    使用时,一般顺序是: 部分满 , 空, 新创建

    在这里插入图片描述

    struct slab {}
    定义: mm/slab.c

    slab分配器分配slab, 使用kmem_getpages()/kmem_freepages()分配/释放,调用的低级内存分配函数__get_free_pages().

    高速缓存及其内存的slab复杂的管理,完全依赖于内部机制, 尽量避免频繁的分配和释放:
    (1) 页分配:
    当高速缓存中既没有满页咩有空的时候, 调用页分配函数;
    (2) 页释放
    a. 内存紧缺时, 系统释放内存;
    b. 高速缓存显式撤销时;

    slab分配器的外部使用接口 :
    在kenrel中的各个子系统模块中, 可以看到高速缓存的相关接使用,
    比如 fork.c模块, f2fs等:

     	(1) 高速缓存创建/销毁
     			kmem_cache_create()/kmem_cache_destory()
     	(2) 	从高速缓存中获取/释放内存对象:
     			kmem_cache_alloc() / kmem_cache_free()
    

    因此, 我们要频繁的使用相同类型的内存对象时, 可以考虑使用 slab高速缓存.

  7. 内核栈
    程序执行周期中, 必然会陷入内核, 新的线程创建时, 同时也会创建线程的内核栈, 实现上是通过slab 分配thread_info_cache, thread_info_cache是kmem_cache_create创建. 大小是THREAD_SIZE, 一般是2 page size.
    每个内核栈大小, 依赖于体系结构和编译选项.
    在kernel2.6版本之前, 内核栈采用的是单页, 主要原因是:
    (1) 可以减少内存消耗;
    (2) 随着机器的运行时间的增加, 难以获取到未分配, 连续的页, 物理内存的碎片, 新进程分配虚拟内存的压力也增大;

     	无论内核页是1页还是2页 , 在编写程序时, 函数的变量使用, 都应该注意节省栈空间, alloca()应该谨慎使用(alloca()可以申请栈空间, 自动释放). 
     	内核对栈的管理有限, 如果栈溢出, 最好的结果是悄无声息的覆盖别的堆栈空间, 最坏的情况是直接宕机. 在kernel4.9版本以前, 栈溢出, 最受考验的是 thread_info, kernel4.9之后, thread_info直接放在task_struct中了.
     	https://baijiahao.baidu.com/s?id=1712620414089250801&wfr=spider&for=pc
    
  8. 高端内存映射
    高端内存不能永久映射到内核地址空间, alloc_pages()的__GFP_HIGHMEM标志不可能有虚拟地址对应.
    (1)永久映射/解除映射
    kmap() / kunmap()
    –> 接口可休眠, 因此只能用在进程上下文中;
    –> 可映射高端/低端内存, 如果是低端内存, 直接返回虚拟地址即可, 如果是高端内存, 会建立永久映射, 并返回地址.
    (2)临时映射
    kmap_atomic()/ kunmap_atomic() // asm/kmap_types.h
    –> 当必须 一个映射而当前上下文不能 休眠时, 内核提供了临时映射, 有一组保留的映射, 用于新创建的临时映射, 内核可以原子的高端内存中的一个页映射到一个保留的映射中,
    函数不会阻塞, 因此可以用在中断上下文和其他不能重新调度的地方. 实现上也禁止了内核抢占.
    https://zhuanlan.zhihu.com/p/74218758

  9. SMP数据访问(percpu)
    对于给定的处理器, 数据访问是唯一的, 如果存在任务抢占或者重新调度, get_cpu()/put_cpu()已经是禁止/激活内核抢占, 此时的数据访问是安全的, 不用在另外去禁止抢占或者激活抢占.

     linux/percpu.h
     mm/slab.c
     asm/percpu.h
     linux/percpu-defs.h
    
    1. percpu :
      新的每个cpu接口
      简化了上述操作行为, 简化了创建和操作每个cpu上的数据

    2. 编译时percpu数据

       DEFINE_PER_CPU(type, name) / DECLARE_PER_CPU(type, name)
       定义或者释放一个type类型的name变量
       
       get_cpu_var() / put_cpu_var() 可操作变量, 且可以禁止或激活抢占
       .data.percpu用于保存变量定义
      
    3. 运行时percpu数据
      alloc_percpu() / free_percpu() 用于动态申请释放cpu数据,
      get_cpu_var() / put_cpu_var() 可操作变量, 且可以禁止或激活抢占

    4. 使用cpu数据的原因和好处
      (1) 减少了数据访问锁, percpu本身就能保证当前cpu只访问自己的唯一数据;
      (2) 大大减少缓存失效, 失效发生在 多个cpu 的缓存同步上, 多cpu间的较多缓存同步, 必将影响性能, 而percpu的数据访问, 可以保证只使用自己的数据, 并且有cache对齐, 将多cpu缓存影响降到最低
      (3)唯一需要保证的禁止抢占, 比上锁开销要小;

  10. 分配函数的选择

    可以根据实际的需求, 各个分配函数的特性来选择分配函数. 
    
    	(1) 物理页连续
    			kmalloc() 及其flag也对应不同的应用场景
    	(2) 高端内存分配
    		alloc_pages() --> 得到struct page 结构指针(高端内存不一定映射, 不一定有虚拟地址)
    		kmap() --> 得到真正的指针
    	(3) 虚拟地址连续的页
    			vmalloc() 有一定的性能损失
    	(4) 创建和撤销 大的数据结构
    		slab高速缓存			
    
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-11-12 19:59:45  更:2021-11-12 20:02:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 23:24:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码