0x0*_*7FC 3 c linux-device-driver linux-kernel
我正在浏览driver/cpufreq/cpufreq.c以了解它是如何工作的.我遇到了一段我无法理解的代码.
在cpufreq_core_init:
for_each_possible_cpu(cpu) {
per_cpu(cpufreq_policy_cpu, cpu) = -1;
init_rwsem(&per_cpu(cpu_policy_rwsem, cpu));
}
Run Code Online (Sandbox Code Playgroud)
当我浏览定义的宏时,
#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)
Run Code Online (Sandbox Code Playgroud)
我的问题:
for_each_possible_cpu扩展?#defines在里面打电话?per_cpu输出等于-1?
- 它是如何扩展的:
请记住,无论什么时候你问这样的Linux内核,答案都不容易......所以......我们走了:
#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
Run Code Online (Sandbox Code Playgroud)
你可以看到这个宏实际上只是一个for循环,用迭代器调用,因为cpu它for_each_cpu是另一个宏,它是定义为的循环部分:
#define for_each_cpu(cpu, mask) \
for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
Run Code Online (Sandbox Code Playgroud)
而cpu_possible_mask是一个指向结构的指针:
extern const struct cpumask *const cpu_possible_mask;
Run Code Online (Sandbox Code Playgroud)
这里看到的(由另一个宏组成):
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
Run Code Online (Sandbox Code Playgroud)
它包含另一个宏(DECLARE_BITMAP),它有另一个#define用于NR_CPUS,即系统中的CPU数量,它应该是系统相关的并在kconfig中设置.那里的宏实际上只是一个数组和一个存取器:
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
Run Code Online (Sandbox Code Playgroud)
所以你可以看到数组和访问者当然包含另一个#define:
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
Run Code Online (Sandbox Code Playgroud)
......由两个以上组成#define:
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE 8
Run Code Online (Sandbox Code Playgroud)
无论如何......你可以看到(A)这是一团糟,(B)它最终成为一个for循环,增加了CPU的数量,但也通过逗号运算符发出第二个迭代动作.第二个操作员自己说出来的确切程度取决于系统.(系统上的长度是多少?系统上的cpu数量是多少?)
2.为什么另外两个#defines被调用?
这是#1的回答.由于它扩展为for循环,因此您需要该循环来执行某些操作.
3.为什么per_cpu输出等于-1?
该per_cpu宏指向系统中正在初始化为的每个CPU的CPU频率策略-1.我必须做更多的研究才能确定,但可能是因为定义他们选择了:
#define CPUFREQ_ETERNAL (-1)
Run Code Online (Sandbox Code Playgroud)
这__init_rwsem是一种体系结构定义的初始化用于每个CPU策略的读/写信号量的方法.
我不知道这个解释是否有多大帮助,但至少可能有助于指出你的方向更好.好运,探索内核.
Mike的答案几乎涵盖了它,除了一个小小的有趣的位,即用于变量的宏cpu_possible_mask(与Mike确实解释的类型相反).
所以在cpu.c:
const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);
EXPORT_SYMBOL(cpu_possible_mask);
Run Code Online (Sandbox Code Playgroud)
宏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;
}
Run Code Online (Sandbox Code Playgroud)
它看起来很奇怪,因为函数一直check_is_bitmap返回1,而且,它的结果甚至不用于调用它的宏!编译器将最终在最终二进制文件中优化掉该调用.那可能会发生什么呢?
实际上,调用不是用于运行时,而是在编译时检查宏参数bitmap实际上是否为类型unsigned long *(因此只是那个函数的名称).如果bitmap类型错误,将发出警告,并且内核构建中的编译警告始终是一个严重问题.
从本质上讲,Linux内核人员将一个通常无类型的宏转换为一个类型化的宏,这通常是用C++和模板完成的.很简约.