博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DEFINE_PER_CPU
阅读量:6246 次
发布时间:2019-06-22

本文共 2811 字,大约阅读时间需要 9 分钟。

转自 http://www.unixresources.net/linux/clf/linuxK/archive/00/00/47/91/479165.html

首先,在arch/i386/kernel/vmlinux.lds中有

/*will be free after init*/

.=ALIGN(4096);

__init_begin=.;

/*省略*/

.ALIGN(32);

__per_cpu_start=.;

.data.percpu:{*(.data.percpu)}

__per_cpu_end=.;

.=ALIGN(4096);

__init_end=.;

/*freed after init ends here*/

这说明__per_cpu_start和__per_cpu_end标识.data.percpu这个section的开头和结尾

并且,整个.data.percpu这个section都在__init_begin和__init_end之间,

也就是说,该section所占内存会在系统启动后释放(free)掉
因为有
#define DEFINE_PER_CPU(type, name) 
__attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name
所以
static DEFINE_PER_CPU(struct runqueue, runqueues); 
会扩展成
__attribute__((__section__(".data.percpu"))) __typeof__(struct runqueue)
per_cpu__runqueues;
也就是在.data.percpu这个section中定义了一个变量per_cpu__runqueues,
其类型是struct runqueue。事实上,这里所谓的变量per_cpu__runqueues,
其实就是一个偏移量,标识该变量的地址。
--------------------
其次,系统启动后,在start_kernel()中会调用如下函数

unsigned long __per_cpu_offset[NR_CPUS];static void __init setup_per_cpu_areas(void){	unsigned long size, i;	char *ptr;	/* Created by linker magic */	extern char __per_cpu_start[], __per_cpu_end[];	/* Copy section for each CPU (we discard the original) */	size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES);#ifdef CONFIG_MODULES	if (size < PERCPU_ENOUGH_ROOM)		size = PERCPU_ENOUGH_ROOM;#endif	ptr = alloc_bootmem(size * NR_CPUS);	for (i = 0; i < NR_CPUS; i++, ptr += size) {		__per_cpu_offset[i] = ptr - __per_cpu_start;		memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);	}}

在该函数中,为每个CPU分配一段专有数据区,并将.data.percpu中的数据拷贝到其中,

每个CPU各有一份。由于数据从__per_cpu_start处转移到各CPU自己的专有数据区中了,
因此存取其中的变量就不能再用原先的值了,比如存取per_cpu__runqueues
就不能再用per_cpu__runqueues了,需要做一个偏移量的调整,
即需要加上各CPU自己的专有数据区首地址相对于__per_cpu_start的偏移量。
在这里也就是__per_cpu_offset[i],其中CPU i的专有数据区相对于
__per_cpu_start的偏移量为__per_cpu_offset[i]。
这样,就可以方便地计算专有数据区中各变量的新地址,比如对于per_cpu_runqueues,
其新地址即变成per_cpu_runqueues+__per_cpu_offset[i]。
经过这样的处理,.data.percpu这个section在系统初始化后就可以释放了。
--------------------
再看如何存取per cpu的变量

/* This macro obfuscates arithmetic on a variable address so that gcc   shouldn't recognize the original var, and make assumptions about it */#define RELOC_HIDE(ptr, off)					  ({ unsigned long __ptr;					    __asm__ ("" : "=g"(__ptr) : "0"(ptr));		    (typeof(ptr)) (__ptr + (off)); })/* var is in discarded region: offset to particular copy we want */#define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu]))#define __get_cpu_var(var) per_cpu(var, smp_processor_id())#define get_cpu_var(var) (*({ preempt_disable(); &__get_cpu_var(var); }))

对于__get_cpu_var(runqueues),将等效地扩展为

__per_cpu_offset[smp_processor_id()] + per_cpu__runqueues
并且是一个lvalue,也就是说可以进行赋值操作。
这正好是上述per_cpu__runqueues变量在对应CPU的专有数据区中的新地址。
由于不同的per cpu变量有不同的偏移量,并且不同的CPU其专有数据区首地址不同,
因此,通过__get_cpu_var()便访问到了不同的变量。
--END

转载地址:http://izria.baihongyu.com/

你可能感兴趣的文章
我的友情链接
查看>>
森森的测试
查看>>
nat 转换 vrrp热备份 端口跟踪
查看>>
Linux crontab调用脚本中的ifconfig命令返回为空
查看>>
Linux解决Device eth0 does not seem to be present
查看>>
php 冒泡排序法
查看>>
seaJs原理分析和源码解读(上)
查看>>
docker学习记录(二)--安装docker并配置镜像源
查看>>
HTML5 localStorage本地存储实际应用举例
查看>>
python之装饰器
查看>>
华为ensp实验拓扑一熟悉常用的IP相关命令拓扑
查看>>
mysql双主复制模型
查看>>
U8860 华为荣耀各种刷机教程
查看>>
Java使用"指针"快速比较字节
查看>>
Tomcat配置性能管理服务--Elastic APM Server
查看>>
这些年正Android - Traveling 第一章001
查看>>
Oracle备份与恢复(五)
查看>>
处理电脑蓝屏步骤
查看>>
虚拟桌面的备份恢复最佳实践 第二部分
查看>>
恢复Innodb损坏的表
查看>>