“是怎么回事?”;对于“每个”可能的“cpu”;是否在cpufreq.c文件中展开?
我浏览了driver/cpufreq/cpufreq.c以了解它是如何工作的。我偶然发现了这段我无法理解的代码 在“是怎么回事?”;对于“每个”可能的“cpu”;是否在cpufreq.c文件中展开?,c,linux-kernel,linux-device-driver,C,Linux Kernel,Linux Device Driver,我浏览了driver/cpufreq/cpufreq.c以了解它是如何工作的。我偶然发现了这段我无法理解的代码 在cpufreq\u core\u init中: for_each_possible_cpu(cpu) { per_cpu(cpufreq_policy_cpu, cpu) = -1; init_rwsem(&per_cpu(cpu_policy_rwsem, cpu)); } 当我查看定义的宏时 #define for_each_possib
cpufreq\u core\u init
中:
for_each_possible_cpu(cpu) {
per_cpu(cpufreq_policy_cpu, cpu) = -1;
init_rwsem(&per_cpu(cpu_policy_rwsem, cpu));
}
当我查看定义的宏时
#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
#define per_cpu(var, cpu) \
(*SHIFT_PERCPU_PTR(&(var), per_cpu_offset(cpu)))
#define init_rwsem(sem) \
do { \
static struct lock_class_key __key; \
\
__init_rwsem((sem), #sem, &__key); \
} while (0)
我的问题是:
如何扩展
#defines
在内部调用输出等于-1
对于_,每个cpu在cpumask.h中定义,并接受两个参数-一个迭代器和一个掩码。掩码是一个cpumask_t左值,它定义了要迭代的CPU集。因此,对于_,每个_可能的_cpu()都会迭代内核启动时可能出现的所有cpu 每CPU变量是包含系统上每个处理器一个对象的数据数组。 per_cpu宏定义将创建名称,该名称将为系统上的每个处理器保存一个给定类型的对象。以这种方式定义的变量实际上是一个值数组。要获取特定处理器的值,可以使用per_cpu()宏;它作为左值工作,因此类似以下代码的代码可以工作: 每cpu(cpufreq\u策略,cpu)=-1
#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
您可以看到,这个宏实际上只是一个for循环,用迭代器调用为cpu
for\u每个\u cpu是另一个宏,它是循环部分,定义为:
#define for_each_cpu(cpu, mask) \
for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
如下所示(由另一个宏组成):
它包含另一个宏(DECLARE_BITMAP
),并且它还有另一个\define
forNR_CPU
,即系统中的CPU数量,它应该依赖于系统并在kconfig中设置。其中的宏实际上只有一个数组和一个访问器:
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
因此,您可以看到这是数组和访问器,它当然由另一个#define
组成:
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
…由另外两个定义组成:
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE 8
无论如何。。。您可以看到:(A)这是一个混乱,(B)它最终成为一个for
循环,它增加了CPU的数量,但也通过逗号运算符发出了第二个迭代操作。第二个运算符自身的确切输出方式取决于系统。(您的系统上的long大小是多少?系统上的CPU数量是多少?)
2.为什么其他两个定义被称为内部定义
答案是#1。因为它扩展为一个for
循环,所以您需要该循环来做一些事情
3.为什么每个cpu的输出等于-1
per_cpu
宏给出一个指针,指向系统中每个cpu的cpu频率策略,该策略将被初始化为-1
。我必须做更多的研究才能确定,但他们选择这一点大概是因为定义:
#define CPUFREQ_ETERNAL (-1)
而\uuuu init\u rwsem
是一种体系结构定义的方式,用于初始化用于每个CPU策略的读/写信号量
我不知道这个解释是否有多大帮助,但至少它可以帮助你找到更好的方向。祝您在探索内核时好运。Mike answer几乎涵盖了它,除了一点有趣的地方,即用于变量
cpu可能的掩码的宏(与Mike解释的类型相反)
因此在cpu.c
中:
const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);
EXPORT_SYMBOL(cpu_possible_mask);
宏to_cpumask
在cpumask.h
中定义如下:
#define to_cpumask(bitmap) \
((struct cpumask *)(1 ? (bitmap) \
: (void *)sizeof(__check_is_bitmap(bitmap))))
static inline int __check_is_bitmap(const unsigned long *bitmap)
{
return 1;
}
它看起来很奇怪,因为函数check\u是\u bitmap
一直返回1
,而且它的结果甚至没有在调用它的宏中使用!编译器将最终优化最终二进制文件中的调用。那么那里可能会发生什么呢
实际上,该调用不用于运行时,而是用于在编译时检查宏参数bitmap
是否实际为unsigned long*
类型(因此才是只执行该操作的函数的名称)。如果bitmap
的类型错误,将发出警告,内核构建中的编译警告总是一个严重问题
<> P>本质上,Linux内核家伙把一个普通的非类型宏转换成一个类型化的宏,通常是用C++在模板中完成的。非常整洁。谢谢。这确实有助于我理解。我需要探索更多才能理解更多。谢谢你的回答。对于((cpu)=0;(cpu)<1;(cpu)++,(void)mask)
的内部循环条件(cpu)<1
,
仍然有点混乱。@Mike我刚刚再次检查了每个\u cpu宏的。include/linux/cpumask.h
中还有另一个定义。这里使用的示例适用于CPU只有一个内核的情况,如果NR_CPU==1
,则使用控制该内核。谢谢您的回答。但是((cpu)=0;(cpu)<1;(cpu)+,(void)mask)
的for循环条件(cpu)<1
内部的。不确定你对此是否有任何见解?
const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);
EXPORT_SYMBOL(cpu_possible_mask);
#define to_cpumask(bitmap) \
((struct cpumask *)(1 ? (bitmap) \
: (void *)sizeof(__check_is_bitmap(bitmap))))
static inline int __check_is_bitmap(const unsigned long *bitmap)
{
return 1;
}