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. なぜ他の2人#definesが内部で呼ばれるのですか?
  3. per_cpu出力が-1に等しいのはなぜですか?
4

3 に答える 3

9
  1. どのように拡張しますか:

Linuxカーネルでそのようなことを尋ねるときはいつでも、答えは決して簡単ではないことを覚えておいてください...だから...ここに行きます:

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

このマクロは実際には単なるforループであり、次のように定義されたループ部分である別のマクロであるcpuため、イテレータで呼び出されます。for_each_cpu

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

そして、cpu_possible_maskは構造体へのポインターです。

extern const struct cpumask *const cpu_possible_mask;

これはここに表示されます(別のマクロで構成されています):

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

これには別のマクロ(DECLARE_BITMAP)が含まれ、別の#defineforNR_CPUSがあります。つまり、システム内の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))

...これはさらに2つで構成されます#define

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

とにかく...(A)これは混乱であり、(B)forCPUの数を増やすだけでなく、コンマ演算子を介して2番目の反復アクションを発行するループになることがわかります。2番目の演算子がそれ自体をどの程度正確に表現するかはシステムに依存します。(システムのlongのサイズはいくつですか?システムのCPUの数はいくつですか?)

2.他の2つの#defineが内部で呼び出されるのはなぜですか?

それは#1によって答えられたようなものです。ループに拡張されるため、何かを行うforにはそのループが必要です。

3.per_cpuの出力が-1に等しいのはなぜですか?

per_cpuマクロは、に初期化されているシステム内の各CPUのCPU周波数ポリシーへのポインターを提供します-1。確実にするためにもっと調査をしなければならないでしょうが、おそらく彼らは定義のためにそれを選んだでしょう:

#define CPUFREQ_ETERNAL                 (-1)

また、これ__init_rwsemは、各CPUのポリシーに使用される読み取り/書き込みセマフォを初期化するアーキテクチャ定義の方法です。

その説明が大いに役立ったかどうかはわかりませんが、少なくともそれはあなたをより良い方向に向けるのに役立つかもしれません。カーネルを探索して頑張ってください。

于 2013-03-26T12:48:39.627 に答える
6

Mike Answerは、1つの小さな興味深いビット、つまり変数に使用されるマクロ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はcpumask.hで定義されており、イテレータとマスクの2つの引数を取ります。マスクは、反復するCPUのセットを定義するcpumask_t左辺値です。したがって、for_each_possible_cpu()は、カーネルのこのブートに存在する可能性のあるすべてのCPUを反復処理します。

CPUごとの変数はデータであり、システム上のプロセッサごとに1つのオブジェクトを含む配列です。per_cpuマクロ定義は、システム上のプロセッサごとに指定されたタイプの1つのオブジェクトを保持する名前を作成します。このように定義された変数は、実際には値の配列です。特定のプロセッサの値を取得するには、per_cpu()マクロを使用できます。左辺値として機能するため、次のようなコードが機能します。per_cpu(cpufreq_policy_cpu、cpu)= -1;

于 2013-03-26T12:48:14.393 に答える