4

我正在浏览 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));
}

当我查看定义的宏时,

#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)

我的问题:

  1. 怎么for_each_possible_cpu展开?
  2. 为什么#defines叫另外两个在里面?
  3. 为什么per_cpu输出等于-1?
4

3 回答 3

9
  1. How does it expand:

Keep in mind anytime you ask something like that wrt the Linux kernel, the answer is never easy... so... here we go:

#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)

You can see that this macro is really just a for loop, called with an iterator as cpu the for_each_cpu is another macro which is the looping part defined as:

#define for_each_cpu(cpu, mask)                 \
     for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)

And the cpu_possible_mask is a pointer to a struct:

extern const struct cpumask *const cpu_possible_mask;

Which is seen here (consisting of another macro):

typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;

That contains another macro (DECLARE_BITMAP) and it has another #define for NR_CPUS, that is the number of CPUs in the system, it should be system dependent and set in the kconfig. The macro in there is really just an array and an accessor:

#define DECLARE_BITMAP(name,bits) \
      unsigned long name[BITS_TO_LONGS(bits)]

So you can see that's the array and the accessor which of course consists of another #define:

#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

...which consists of two more #defines:

#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE 8

Anyway... you can see that (A) this is a mess and (B) it ends up being a for loop that increments number of CPUs but also issues a second iterative action via the comma operator. How exactly the second operator words itself out is system dependent. (what's the sizeof a long on your system? what's the number of cpus on your system?)

2.Why are two others #defines are called inside?

That's kind of answered by #1. Since it expands to a for loop, you need that loop to do something.

3.Why is the per_cpu output equated to -1?

The per_cpu macro is giving a pointer to the CPU frequency policy of each CPU in the system, that is being initialized to -1. I'd have to do more research to be sure, but presumably they picked that because of the define:

#define CPUFREQ_ETERNAL                 (-1)

And the __init_rwsem is an architecture defined way of initializing the read/write semaphore used for each CPU's policy.

I don't know if that explanation helped much, but at least maybe it helps point you in a better direction. Good luck exploring the kernel.

于 2013-03-26T12:48:39.627 回答
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);

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_is_bitmap返回1,而且它的结果甚至没有在调用它的宏中使用!编译器将明确优化最终二进制文件中的调用。那么那里可能发生什么?

实际上,调用不是用于运行时,而是在编译时检查宏参数bitmap实际上是类型unsigned long *(因此函数的名称只是这样做)。如果bitmap是错误的类型,则会发出警告,并且内核构建中的编译警告始终是一个严重的问题。

本质上,Linux 内核人员将通常无类型的宏转换为有类型的宏,这通常在 C++ 中使用模板完成。挺整洁的。

于 2013-03-26T15:10:13.180 回答
0

for_each_cpu is defined in cpumask.h, and takes two arguments - an iterator, and a mask. The mask is a cpumask_t lvalue that defines the set of CPUs to iterate over. so for_each_possible_cpu() iterates over all CPUs that could possibly be present on this boot of the kernel.

Per-CPU variables are data - arrays containing one object for each processor on the system. per_cpu macro definition will create name, which will hold one object of the given type for each processor on the system. Variables defined in this way are actually an array of values. To get at a particular processor's value, the per_cpu() macro may be used; it works as an lvalue, so code like the following works : per_cpu(cpufreq_policy_cpu, cpu) = -1;

于 2013-03-26T12:48:14.393 回答