如何在cpufreq.c文件中扩展"for_each_possible_cpu"?

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)

我的问题:

  1. 如何for_each_possible_cpu扩展?
  2. 为什么还有两个人#defines在里面打电话?
  3. 为什么per_cpu输出等于-1?

Mik*_*ike 8

  1. 它是如何扩展的:

请记住,无论什么时候你问这样的Linux内核,答案都不容易......所以......我们走了:

#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
Run Code Online (Sandbox Code Playgroud)

你可以看到这个宏实际上只是一个for循环,用迭代器调用,因为cpufor_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策略的读/写信号量的方法.

我不知道这个解释是否有多大帮助,但至少可能有助于指出你的方向更好.好运,探索内核.


did*_*erc 6

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++和模板完成的.很简约.