• linux每处理器内存分配


    一、每处理器

    多处理器系统中,每处理器变量为每个处理器生成一个变量副本,每个处理器访问自己的副本;

    优点:避免处理器之间和处理器缓存之间的同步,提高程序的执行速度。

    二、编程接口

    1、静态

    1. DEFINE_PER_CPU(type,name);//定义
    2. DECLARE_PER_CPU(type,name);//声明
    3. 宏定义展开
    4. _ attribute ((section(".data .. percpu"))) _ typeof_ (type) name
    5. 每处理器变量放在.data..percpu
    6. DEFINE_PER_CPU_FIRST(type, name) ;//定义必须出现在每处理器变量集合中最先出现的每处理器变量
    7. DEFINE_PER_CPU_SHARED_ALLGNED(type,name);//定义和处理器缓存行对齐的每处理器变量,仅在SMP系统中需要
    8. DEFINE_PER_CPU_ALLGNED(type,name);//定义和处理器缓存行对齐的每处理器变量,都需要对齐
    9. DEFINE_PER_CPU_PAGE_ALIGNED(type,name);//定义和页长度对齐的每处理器变量;
    10. DEFINE_PER_CPU_READ_MOSTLY(type,name)定义以读为主的每处理器变量;
    11. 允许任何模块引用 EXPORT_PER_CPU_SYMBOL(var);把静态每处理器变量导出到符号表;
    12. 只允许使用GPL许可的内核模块引用,使用EXPORT_PER_CPU_SYMBOL_GPL(var)

    2、动态

    1. void _percpu•_ alloc_percpu_gfp (size size, size_t align, gfp t gfp);
    2. 宏 alloc_percpu_gfp(type,gfp),是上一个的简化形式,参数size取sizeof(type);
    3. _alloc_percpu
    4. 宏 alloc_percpu(type)是函数__alloc_percpu 的简化形式
    5. //常用
    6. alloc__percpu(type)
    7. //释放
    8. void free_percpu(void _percpu *_pdata);

    3、访问每处理器变量 

    1. 宏 this_cpu_ptr(ptr)用来得到当前处理器的变量副本的地址,展开如下;
    2. unsigned long _ ptr;
    3. _ptr = (unsigned long) (ptr);
    4. (typeof (ptr)) (_ptr + percpu_offset (raw_smp_processor_id ()));
    5. 当前处理器的变量副本的地址等于基准地址加上当前处理器的偏移;
    6. 宏 get_cpu_var(var)用来得到当前处理器的变量副本的值;
    7. per_cpu_ptr(ptr, cpu) 用来得到指定处理器的变量副本的地址;
    8. per_cpu(var,cpu)用来得到指定处理器的变量副本的值;
    9. get_cpu_ptr(var) 禁止内核抢占并且返回当前处理器的变量副本的地址;
    10. put_cpu_ptr(var)开启内核抢占;与上一个成对使用,确保当前进程在内核模式下访问当前处理器的变量副本的时候不会被其他进程抢占
    11. get_cpu_var(var) 禁止内核抢占并且返回当前处理器的变量副本的值,
    12. pur_cpu_var(var) 开启内核抢占;与上一个成对使用,确保当前访问处理器变量副本的时候不会被其他进程抢占

    三、每处理器的内存如何分配的

    每处理器区域是按块分配的,每个分为多个长度相同的单元(unit,每个处理器对应一个单元;

    在NUMA系统上,把单元按内存节点分组,同一个内存节点的所有处理器对应的单元属于同一组;

    块分配

    1、vmalloc;从虚拟地址空间分配虚拟内存区域,然后映射到物理页;适用于 多处理器系统

    2、基于内核内存的块分配。直接从页分配器分配,使用直接映射的内核虚拟地址;适用于单处理器系统

    1. this_cpu_ptr(ptr)访问每处理器变量,ptr是每处理器变量分配内存时返回的虚拟地址;
    2. this_cpu_ptr (ptr)
    3. = ptr + __per_cpu offset[cpu] ;• cpu是当前处理器的编号*/
    4. = ptr +(delta+ pcpu_unit_offsets[cpu])
    5. = (ptr + delta) + pcpu unit offsets[cpu]
    6. = (chunk->base addr + offset) + pcpu_unit_offse 七 s[cpu]
    7. = (chunk->base_addr + pcpu_unit_offsets[cpu]) + offset
    8. pcpu_unit_offsets[cpu]是处理器对应的单元的偏移;
    9. chunk->base_addr + pcpu_unit_offsets[cpu] 处理器对应单元的起始地址
    10. offset 内部单元的偏移,就是变量副本的地址

    分配图,放在每个小块的偏移和分配状态,使用最低位表示小块的分配状态;

     

    假设系统有4个处理器,一个块分为4个单元,

    分配图的初始状态,第一项存放第一个小块的偏移0,空闲,第二项存放单元的结束标记,偏移单元长度pcpu_unit_size,最低位被设置;块的初始状态,空

    分配一个长度32字节的动态每处理器变量以后,

    分配图使用三项:第一项存放第一个小块的偏移0,已分配;第二块存放第二个小块的偏移32,空闲;第三项存放单元的结束标记,偏移是单元长度pcpu_uint_size,最低被设置。

    块的状态,每个单元中偏移0,长度32字节的小块被分配出去;

    分配器根据空闲长度把块组织成链表,把每条链表称为块插槽,插槽数量是pcpu_nr_slots,

    1、根据长度n计算插槽号的方法

     

    • 1如果空闲长度小于整数长度,或者最大的连续空闲字节数小于整数长度,那么插槽号是0;

    2、如果块全部空闲,那么取最后一个插槽号,即pcpu_nr_slots - 1

    3、其他情况:插槽号 = fln(n) - 3,并且不能小于1,fls(n)取n被设置的最高位。减3的目的是让空闲长度是1~15字节的块共享插槽1

    2、确定块的参数

    start_kernel -> setup__per_cpu_areas -> pcpu_embed_first_chunk -> pcpu_build_alloc i nfo

    pcpu_build_alloc_info 计算分组信息和单元长度

    •  静态长度:内核中所有静态每处理器变量的长度总和;数据段结束地址 - 起始地址 即(__per_cpu_end  - __per_cpu_start)
    • 保留长度:为内核模块的静态每处理器变量保留,使用宏PERCPU_MODULE_RESERVE定义,值是8KB
    • 动态长度:为动态每处理器准备,使用宏PERCPU_DYNAMIC_RESERVE定义,在64位系统中值是28KB

     size_sum = 静态长度 + 保留长度 + 动态长度

     

    最小单元长度 min_unit_size = size_sum,并且不允许小于宏PCPU_MIN_UNIT_SIZE 值是32KB
    分配长度 alloc_size = min_unit_size向上对齐到原子长度的整数倍,原子长度是页长度;
    最大倍数 max = alloc_size /  min_unit_size
    单元长度 = alloc_size  / 倍数 n,需要从最大倍数max到最小倍数1中找到一个最优的倍数n 


     学习链接:

    kernel学习链接

     参考

    https://course.0voice.com/v1/course/intro?courseId=2&agentId=0

  • 相关阅读:
    【EMC专题】电磁兼容学科的发展
    google abseil c++ tips[55] name counting and unique_ptr 变量名计数和unique_ptr
    Java手写最长递增子序列算法和最长递增子序列算法应用拓展案例
    数据结构与算法_排序算法_四个基础排序算法性能对比
    沁恒CH32V003F4P6 开发板上手报告和Win10环境配置
    Python库之pandas的简介、安装、使用方法详细攻略
    【教程】超详细通过Shizuku转生支付宝集成XQ_Crystal来自动收能量
    JS基础笔记-关于对象
    长安链源码学习v2.2.1--ioc机制(十)
    构建自动化测试环境:使用Docker和Selenium!
  • 原文地址:https://blog.csdn.net/WANGYONGZIXUE/article/details/133251729