linux每处理器内存分配

一、每处理器

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

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

二、编程接口

1、静态


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

2、动态

void _percpu•_ alloc_percpu_gfp (size  size, size_t align, gfp t gfp); 
       宏 alloc_percpu_gfp(type,gfp),是上一个的简化形式,参数size取sizeof(type);
_alloc_percpu 
       宏 alloc_percpu(type)是函数__alloc_percpu 的简化形式 

//常用
alloc__percpu(type) 
//释放
void free_percpu(void _percpu *_pdata); 

3、访问每处理器变量 


宏 this_cpu_ptr(ptr)用来得到当前处理器的变量副本的地址,展开如下;
	unsigned long _ ptr;
	_ptr = (unsigned long) (ptr);
	(typeof (ptr)) (_ptr + percpu_offset (raw_smp_processor_id ())); 
	当前处理器的变量副本的地址等于基准地址加上当前处理器的偏移;
宏 get_cpu_var(var)用来得到当前处理器的变量副本的值;

per_cpu_ptr(ptr, cpu) 用来得到指定处理器的变量副本的地址;
per_cpu(var,cpu)用来得到指定处理器的变量副本的值;

get_cpu_ptr(var) 禁止内核抢占并且返回当前处理器的变量副本的地址;
 put_cpu_ptr(var)开启内核抢占;与上一个成对使用,确保当前进程在内核模式下访问当前处理器的变量副本的时候不会被其他进程抢占

 get_cpu_var(var) 禁止内核抢占并且返回当前处理器的变量副本的值,
 pur_cpu_var(var) 开启内核抢占;与上一个成对使用,确保当前访问处理器变量副本的时候不会被其他进程抢占

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

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

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

块分配

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

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

this_cpu_ptr(ptr)访问每处理器变量,ptr是每处理器变量分配内存时返回的虚拟地址;
this_cpu_ptr (ptr)
= ptr + __per_cpu offset[cpu] ;• cpu是当前处理器的编号*/
= ptr +(delta+ pcpu_unit_offsets[cpu])
= (ptr + delta) + pcpu unit offsets[cpu]
= (chunk->base addr + offset) + pcpu_unit_offse 七 s[cpu]
= (chunk->base_addr + pcpu_unit_offsets[cpu]) + offset 

pcpu_unit_offsets[cpu]是处理器对应的单元的偏移;
chunk->base_addr + pcpu_unit_offsets[cpu] 处理器对应单元的起始地址
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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
NUMA(Non-Uniform Memory Access)是一种计算机硬件架构,它通过将内存划分为多个节点(node)来提高系统性能。在 NUMA 架构中,每个节点都有自己的本地内存处理器,同时还可以访问其他节点的内存处理器。因此,NUMA 架构可以实现更高的可扩展性和性能。 在 Linux 系统中,NUMA 内存分配策略主要有两种:首选节点(Preferred Node)和本地节点(Local Node)。 首选节点策略指定一个节点作为内存分配的首选节点,如果该节点上没有足够的空闲内存,则会从其他节点中选择一个可用的节点进行分配。这种策略适用于需要在特定节点上运行的应用程序,例如数据库或虚拟机。 本地节点策略则优先在请求内存的进程所在的节点上分配内存。如果该节点上没有足够的内存,则会从其他节点中选择一个可用的节点进行分配。这种策略适用于需要快速访问本地内存的应用程序,例如科学计算或图形处理。 Linux 系统还提供了其他一些 NUMA 内存分配策略,例如交错(Interleave)和远程节点(Remote Node)等。交错策略将内存均匀地分配到所有节点上,而远程节点策略则将内存分配到远程节点上,以减少节点之间的数据传输。 可以使用 numactl 命令来管理 NUMA 内存分配策略,例如设置首选节点、查看节点信息、绑定进程等。在编写 NUMA 应用程序时,也可以使用一些库函数来控制内存分配策略,例如 numa_alloc_local() 和 numa_alloc_onnode() 等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为了维护世界和平_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值