読者です 読者をやめる 読者になる 読者になる

TenForward

技術ブログ。はてなダイアリーから移転しました

cgroup の SUBSYS マクロ

Linux カーネルの cgroup 関連のコードのお話。

cgroup_subsys.h というヘッダがあって、cpuset の部分だけ抜き出すと

#if IS_ENABLED(CONFIG_CPUSETS)
SUBSYS(cpuset)
#endif

という風に SUBSYS マクロの中に cpuset のようなサブシステム名を与えているだけのヘッダファイルがあります。

この SUBSYS マクロは cgroup.h と cgroup.c で計 4 回ほど定義されています。

まず最初に、cgroup.h で

#define SUBSYS(_x) _x ## _cgrp_id,

と定義されています。サブシステム名と _cgrp_id という文字列を連結します。

直後に enum の定義があり、

enum cgroup_subsys_id {
#include <linux/cgroup_subsys.h>
        CGROUP_SUBSYS_COUNT,
};

上記のように、そこで cgroup_subsys.h が include されているので、さきほどの SUBSYS(cpuset)cpuset_cgrp_id という風に展開されます。

つまり、

enum cgroup_subsys_id {
        cpuset_cgrp_id,

となります。cgroup_subsys.h には、他に存在するサブシステムが (kernel の config で有効になっている場合に) SUBSYS(サブシステム名) という風に定義されているので、(サブシステム名)_cgrp_id という値をサブシステム分持つ enum が定義されます。そして、最後が CGROUP_SUBSYS_COUNT となるわけですね。つまり、

enum cgroup_subsys_id {
        cpuset_cgrp_id,
        cpu_cgrp_id,
        cpuacct_cgrp_id,
        blkio_cgrp_id,
        memory_cgrp_id,
        devices_cgrp_id,
        freezer_cgrp_id,
        net_cls_cgrp_id,
        perf_event_cgrp_id,
        net_prio_cgrp_id,
        hugetlb_cgrp_id,
        debug_cgrp_id,
        CGROUP_SUBSYS_COUNT,
};

という enum が完成します。直後に

#undef SUBSYS

と SUBSYS は undef されているので、ここで上記の enum を作って役割は終わりです。この CGROUP_SUBSYS_COUNT はサブシステム分ループする場合に使えますね。例えばこんなふうに

#define for_each_subsys(ss, ssid)                   \
   for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT &&      \
        (((ss) = cgroup_subsys[ssid]) || true); (ssid)++)

そして、サブシステムの配列などで、それぞれのサブシステムの ID を表す (サブシステム名)_cgrp_id という値が利用できます。あとは配列の領域確保のときに使われています。


もう 1 か所、cgroup.h に SUBSYS の定義があります。

#define SUBSYS(_x) extern struct cgroup_subsys _x ## _cgrp_subsys;
#include <linux/cgroup_subsys.h>
#undef SUBSYS

サブシステム名を _cgrp_subsys と連結して、extern struct cgroup_subsys の後に書くので、例えば cpuset なら

extern struct cgroup_subsys cpuset_cgrp_subsys;

となります。cpuset_cgrp_subsys 変数は kernel/cpuset.c に定義がありますので、それの extern 宣言になりますね。同様に、有効になっている各サブシステムの cgroup_subsys 構造体ごとに宣言されます。


kernel/cgroup.c にも 2 ヶ所ほどあります。

/* generate an array of cgroup subsystem pointers */
#define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys,
static struct cgroup_subsys *cgroup_subsys[] = {
#include <linux/cgroup_subsys.h>
};
#undef SUBSYS

最初に紹介した、サブシステム名と _cgrp_id を連結した enum の id が使われていますね。これも同様に

[cpuset_cgrp_id] = &cpuset_cgrp_subsys,

という行が生成されますね。つまり cgroup_subsys という配列の (サブシステム名)_cgrp_id 番目は (サブシステム名)_cgrp_subsys へのポインタということです。

これは先に紹介した for_each_subsys マクロで使われていましたね。


さいごに kernel/cgroup.c にあるのが、サブシステム名を命名するマクロです。

/* array of cgroup subsystem names */
#define SUBSYS(_x) [_x ## _cgrp_id] = #_x,
static const char *cgroup_subsys_name[] = {
#include <linux/cgroup_subsys.h>
};
#undef SUBSYS

これもさきほどと同様の処理でサブシステム名の配列を作っています。配列の (サブシステム名)_cgrp_id 番目は (サブシステム名)、つまり SUBSYS マクロに指定した文字列となります。

[cpuset_cgrp_id] = cpuset,

つまり、カーネルが使うサブシステムに関わる構造体の配列とか ID とか名前の配列なんかは、カーネルの config 時 (make config とか make menuconfig とか) の時点で定義され、コンパイル時点で作成されるってことですね。

いやー、さすがカーネル開発するような人は賢いなあ (←言ってる人アホっぽい)