Cgroup
Cgroup (コントローラグループ) とは、任意のプロセスをグループ化して管理するための Linux カーネルの機能となり、I/O やメモリの割り当て制御といった具体的なリソース管理機能 (Cgroup サブシステム) が実装されている。
Cgroup サブシステムには、メモリを制御する Memory コントローラやプロセススケジューリングを制御する CPU コントローラ等が存在し、実行中のカーネルで利用可能な Cgroup サブシステムは /proc/cgroups で確認可能。
$ cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 0 1 1
cpu 0 1 1
cpuacct 0 1 1
blkio 0 1 1
memory 0 1 1
devices 0 1 1
freezer 0 1 1
net_cls 0 1 1
perf_event 0 1 1
net_prio 0 1 1
hugetlb 0 1 1
pids 0 1 1
cgroup ファイルシステム
Cgroup はグループ管理やサブシステムの設定を行うためのユーザーインターフェイスとして cgroup ファイルシステムを提供しており、Cgroup を利用するには cgroup ファイルシステムをマウントする必要がある。また、マウントの際はオプションでどのサブシステムを使用するかを指定する。
ここでは cpu サブシステムを指定してマウントしてみる。
$ sudo mount -t cgroup -o cpu cgroup /cgroup
実際に /cgroup 配下を確認すると Cgroup が見せているいくつかの特殊ファイルが存在することがわかる。
$ ls -l /cgroup/
total 0
-rw-r--r-- 1 root root 0 Jan 8 07:51 cgroup.clone_children
-rw-r--r-- 1 root root 0 Jan 8 07:51 cgroup.procs
-r--r--r-- 1 root root 0 Jan 8 07:51 cgroup.sane_behavior
-rw-r--r-- 1 root root 0 Jan 8 07:51 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Jan 8 07:51 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Jan 8 07:51 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Jan 8 07:51 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Jan 8 07:51 cpu.shares
-r--r--r-- 1 root root 0 Jan 8 07:51 cpu.stat
-rw-r--r-- 1 root root 0 Jan 8 07:51 notify_on_release
-rw-r--r-- 1 root root 0 Jan 8 07:51 release_agent
-rw-r--r-- 1 root root 0 Jan 8 07:51 tasks
上記のうちプレフィクスが cgroups のものとプレフィクスが無いものは Cgroup のインフラが提供する特殊ファイルとなり、cpu というプレフィクスが付いているものは cpu サブシステムが提供する特殊ファイルとなる。
特殊ファイルには読み取り専用のものと読み書き可能な 2 種類が存在するが、前者はユーザーに情報を提供するためのものであり、後者は値を書き込むことで設定を行うためのものとなる。
ここで、tasks という特殊ファイルが存在するが、この内容はグループに所属するスレッドのスレッド ID (TID) となるが、この時点ではシステム上で実行されている全てのスレッドの TID が含まれ、これは全スレッドがこのグループに所属していることになる。
$ cat /cgroup/tasks | head
1
2
3
4
6
7
8
9
10
11
なお、「グループ」とは cgroup ファイルシステムのディレクトリとして表現されるスレッドの集合体であり、/cgroup もディレクトリとなるため、1 つのグループを表すことになる。今回で言う /cgroup のようなマウントポイントの最上位に位置するディレクトリは自動的に作成されるグループとなり、ルートグループと呼ばれる。この段階では /cgroup 、つまりルートグループのみがシステム上に存在する唯一のグループとなる。
新たにグループを作るには /cgroup の下に新たにディレクトリを作成すれば良い。
$ sudo mkdir /cgroup/test
$ ls -l /cgroup/test/
total 0
-rw-r--r-- 1 root root 0 Jan 8 08:02 cgroup.clone_children
-rw-r--r-- 1 root root 0 Jan 8 08:02 cgroup.procs
-rw-r--r-- 1 root root 0 Jan 8 08:02 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Jan 8 08:02 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Jan 8 08:02 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Jan 8 08:02 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Jan 8 08:02 cpu.shares
-r--r--r-- 1 root root 0 Jan 8 08:02 cpu.stat
-rw-r--r-- 1 root root 0 Jan 8 08:02 notify_on_release
-rw-r--r-- 1 root root 0 Jan 8 08:02 tasks
tasks の中身を確認すると空であることがわかるが、これはまだこのグループにスレッドが所属していないためである。
$ cat /cgroup/test/tasks
スレッドをグループに所属させるためには、tasks にそのスレッドの TID を書き込めば良い。今回はシェル自身を所属させる。
$ echo $$
26681
$ echo $$ > /cgroup/test/tasks
$ cat /cgroup/test/tasks
26681
26694
$ cat /cgroup/test/tasks
26681
26695
ここで、シェル (26681) を所属させた後に /cgroup/test/tasks を確認すると 26694 や 26695 という別のスレッドの TID が存在することがわかる。
これは、グループに所属しているプロセスが子プロセスを生成すると自動的にそのグループに所属するという動作によるものであり、26694 及び 26695 はシェルから生成された子プロセスである cat となる。
なお、tasks は TID を表示するが、cgroup.procs は所属するプロセスの PID を表示する。cgroups.proc はマルチスレッドプロセスのスレッドグループリーダー以外の TID を含まないという点で tasks と異なる。
$ cat /cgroup/test/cgroup.procs
26681
26696
$ cat /cgroup/test/tasks
26681
26697
Namespace
Namespace を使用することで、プロセスグループ毎に独立した PID や IPC 、またネットワーク空間を持たせることが可能となる。clone システムコールの第 3 引数 flags に名前空間を分割するフラグを設定して、clone システムコールを発行することで名前空間を分割できる。
例えば PID 名前空間を分割すると、新たに作成された PID 名前空間においてプロセスの PID は 1 から始まる。新たな PID が 1 のプロセスから fork() されたプロセスはこの PID 名前空間に閉じ込められ、他の PID 名前空間とは分断される。
同様に IPC 、ネットワーク、ファイルシステムのマウント空間、UTS (Universal Time sharing System) を対象にリソース分割ができる。