cgroup namespace (2)
前回は /proc/$PID/cgroup ファイルが Namespace を反映した形で記載されているのを見ました。
とりあえずここまで。これだけだとすでにマウントされている cgroupfs はそのままの元のディレクトリ階層で見えるので、/proc/$PID/cgroup だけ見え方が変わっても意味がないような気がしますが、改めて cgroupfs のマウントを試すとエラーになりますし、どうするものなのかちょっとまだ見えてないので、またわかれば続編を書く予定です。
と書きましたが、試したところちゃんと cgroupfs も Namespace ごとに見えるようになったので紹介しておきます。前回何をボケて失敗していたのかわかりませんが、以下のように普通に簡単な処理をやっただけです。
- まずは "test01" cgroup を作成します (memory だけ)
# mkdir -p /sys/fs/cgroup/memory/test01/
- 現在のシェルを "test01" の tasks に登録します
# echo $$ > /sys/fs/cgroup/memory/test01/tasks
- namespace内の cgroupfs をホストから見て別のところにマウントするために、LXC コンテナ用のツリーを借ります
# cd /var/lib/lxc/test01/rootfs/
- unshare で chroot を実行します
# unshare -C chroot $PWD
- namespace 内で proc をマウントし、/sys/fs/cgroup を tmpfs でマウントします。これは特に不要な気がしますが
# mount -n -t proc proc /proc
# mount -n -t tmpfs none /sys/fs/cgroup/ - memory サブシステムを /sys/fs/cgroup/memory にマウントします
# mkdir /sys/fs/cgroup/memory
# mount -n -t cgroup -o memory memory /sys/fs/cgroup/memory/
マウントできたので、確認してみます。
# ls /sys/fs/cgroup/memory/ cgroup.clone_children memory.memsw.failcnt cgroup.event_control memory.memsw.limit_in_bytes cgroup.procs memory.memsw.max_usage_in_bytes memory.failcnt memory.memsw.usage_in_bytes memory.force_empty memory.move_charge_at_immigrate memory.kmem.failcnt memory.numa_stat memory.kmem.limit_in_bytes memory.oom_control memory.kmem.max_usage_in_bytes memory.pressure_level memory.kmem.slabinfo memory.soft_limit_in_bytes memory.kmem.tcp.failcnt memory.stat memory.kmem.tcp.limit_in_bytes memory.swappiness memory.kmem.tcp.max_usage_in_bytes memory.usage_in_bytes memory.kmem.tcp.usage_in_bytes memory.use_hierarchy memory.kmem.usage_in_bytes notify_on_release memory.limit_in_bytes tasks memory.max_usage_in_bytes
普通にマウントできていて、"test01" cgroup は存在しませんね。つまりここが cgroupfs のルートなわけです。
では、ここに "test02" cgroup を作ってみましょう。
root@enterprise:~# mkdir /sys/fs/cgroup/memory/test02 root@enterprise:~# ls -d /sys/fs/cgroup/memory/test02 /sys/fs/cgroup/memory/test02/ root@enterprise:~# ls /sys/fs/cgroup/memory/test02 cgroup.clone_children memory.memsw.failcnt cgroup.event_control memory.memsw.limit_in_bytes cgroup.procs memory.memsw.max_usage_in_bytes :(略)
/sys/fs/cgroup/memory 直下に test02 というディレクトリができて、中も普通にファイルができています。
"test02" にプロセスを登録して /proc/$PID/cgroup を確認してみましょう。
# echo $$ > /sys/fs/cgroup/memory/test02/tasks # echo $$ 8574 # cat /proc/self/cgroup 14:debug:/ 13:pids:/ 12:hugetlb:/ 11:net_prio:/ 10:perf_event:/ 9:net_cls:/ 8:freezer:/ 7:devices:/ 6:memory:/test02 5:blkio:/ 4:cpuacct:/ 3:cpu:/ 2:cpuset:/ 1:name=systemd:/
ちゃんと Namespace 内の cgroup ツリーになってますね。
ここで、元のホスト上の Namespace から "test01" と "test02" を確認してみましょう。
$ ls -d /sys/fs/cgroup/memory/test01 /sys/fs/cgroup/memory/test01/ $ ls -d /sys/fs/cgroup/memory/test01/test02 /sys/fs/cgroup/memory/test01/test02/
作成した cgroup namespace の外では "test01" の下に "test02" がありますね。tasks ファイルも確認してみると、
$ cat /sys/fs/cgroup/memory/test01/test02/tasks 8574
シェルの PID が登録されていますね。
cgroup namespace (1)
以前、RHEL6 のころに ns cgroup ってサブシステムが cgroup にありましたが、それとは別のお話。
4.6 カーネルに入る cgroup namespace のお話です (Ubuntu 16.04 のカーネルには入るようです) 。namespace ごとに別の cgroup ツリーが見えるようにするものかな。
軽く試してみました。
準備
(2016-04-12 追記) 今だと 4.6-rc? で試せます
とりあえず cgroup namespace の機能が入ったカーネルは
の "2016-03-21/nsroot" というブランチのものを使いました。出来上がったカーネルはこんな
$ uname -r 4.5.0-rc1-plamo64-cgns-00009-g37bbd8c-dirty
(-plamo64 は make menuconfig で指定してる文字列)
cgroup namespace が作れる unshare を含む util-linux は
から。こちらも "2016-03-02/cgns" というブランチを元にパッチを作って util-linux-2.27.1 に当てました。
実行
とりあえず /proc/$PID/cgroup ファイルで見える cgroup のパスが Namespace ごとに独立して見えるみたい。この様子を見てみました。
まずは現在のシェルを cgroup に所属させます。memory サブシステムのみ "test01" に所属させてみました。
# echo $$ > /sys/fs/cgroup/memory/test01/tasks
この状態で同じシェル上で /proc/self/cgroup を確認してみましょう。
# cat /proc/self/cgroup
13:debug:/
12:pids:/
11:hugetlb:/
10:net_prio:/
9:perf_event:/
8:net_cls:/
7:freezer:/
6:devices:/
5:memory:/test01
4:blkio:/
3:cpuacct:/
2:cpu:/
1:cpuset:/
memory は /test01 になっていますね。
ここでパッチを当てた unshare コマンドで新しい cgroup namespace を作成します。-C が cgroup namespace を作るオプションです。
# unshare -C
# cat /proc/self/cgroup
13:debug:/
12:pids:/
11:hugetlb:/
10:net_prio:/
9:perf_event:/
8:net_cls:/
7:freezer:/
6:devices:/
5:memory:/
4:blkio:/
3:cpuacct:/
2:cpu:/
1:cpuset:/
# echo $$
22302
memory の部分が "/test01" でなく、"/" になっていますね。最後に現在のシェルの PID を確認しています。
この PID の cgroup 情報を別のシェル (元の cgroup namespace にいるシェル) から見てみます。
# cat /proc/22302/cgroup 13:debug:/ 12:pids:/ 11:hugetlb:/ 10:net_prio:/ 9:perf_event:/ 8:net_cls:/ 7:freezer:/ 6:devices:/ 5:memory:/test01 4:blkio:/ 3:cpuacct:/ 2:cpu:/ 1:cpuset:/
作成した namespace の外から見るとちゃんと "/test01" になっていますね。
/proc/$PID/ns/cgroup ファイルも確認しておきましょう。
# ls -l /proc/1/ns/cgroup lrwxrwxrwx 1 root root 0 4月 6日 06:59 /proc/1/ns/cgroup -> cgroup:[4026531835] # ls -l /proc/22273/ns/cgroup lrwxrwxrwx 1 root root 0 4月 6日 07:17 /proc/22302/ns/cgroup -> cgroup:[4026532404]
リンク先が異なりますので違う namespace にいることがわかります。これはこれまでの他の namespace と同じですね。
プロセスが cgroup 間を移動したとき
プロセスを cgroup 間で移動させると面白い見え方をします。まず "test01" と同じ階層に "test02" を作成し、先ほどのプロセスを "test01" から "test02" に移動させます。
# mkdir /sys/fs/cgroup/memory/test02 # echo 22302 > /sys/fs/cgroup/memory/test02/tasks
先ほど unshare して作った namespace 内のシェルから /proc/$PID/cgroup ファイルを見てみると、
# cat /proc/self/cgroup 13:debug:/ 12:pids:/ 11:hugetlb:/ 10:net_prio:/ 9:perf_event:/ 8:net_cls:/ 7:freezer:/ 6:devices:/ 5:memory:/../test02 4:blkio:/ 3:cpuacct:/ 2:cpu:/ 1:cpuset:/
相対パスのように見えます。
とりあえずここまで。これだけだとすでにマウントされている cgroupfs はそのままの元のディレクトリ階層で見えるので、/proc/$PID/cgroup だけ見え方が変わっても意味がないような気がしますが、改めて cgroupfs のマウントを試すとエラーになりますし、どうするものなのかちょっとまだ見えてないので、またわかれば続編を書く予定です。
(続くかも?)
[Linux][Kernel][Security] User Namespace と Overlayfs と CVE-2015-8660
単なるメモです。
aufs-users ML に流れた、Overlayfs と User Namespace(userns) を併せて使った時の脆弱性のお話:
脆弱性の内容は
これに関連する脆弱性として CVE-2015-8660 があるようで、これはすでに修正済みで、上記に書かれた脆弱性もこれで影響なくなるらしい。
aufs で userns は aufs を使った一般ユーザ権限で起動するコンテナ - TenForwardの日記 のあたりの話で、普段から allow_userns=1 で使ってるので、少し興味をそそられたので試してみました。
ただ、上記に書かれている脆弱性ですが、そもそも vanilla kernel だと userns 内の特権ユーザでも overlayfs のマウントはできないので、ここに書かれている userns 内で Overlayfs マウントして悪いことする、ってのは無理なような気がするので、パッチを当てて userns 内で Overlayfs マウントができるようにしている Ubuntu 特有の話に思えるけど、他にできるようにしてるディストリビューションあるのかな?
Plamo でも Ubuntu を真似して、userns 内で overlayfs マウント可能にしてます。こんなパッチ:
diff -uNr linux-4.2/fs/overlayfs/super.c linux-4.2-olfs/fs/overlayfs/super.c --- linux-4.2/fs/overlayfs/super.c 2015-08-31 03:34:09.000000000 +0900 +++ linux-4.2-olfs/fs/overlayfs/super.c 2015-09-02 16:09:24.339937219 +0900 @@ -1097,6 +1097,7 @@ .name = "overlay", .mount = ovl_mount, .kill_sb = kill_anon_super, + .fs_flags = FS_USERNS_MOUNT, }; MODULE_ALIAS_FS("overlay");
(参考:overlayfs と LXC 非特権コンテナの snapshot によるクローン - TenForwardの日記の「Plamo 5.2の非特権コンテナのclone」の項)
とりあえず aufs-users ML にあったパッチを当てて試してみました。aufs では成功しないけど…
試した環境は
- Kernel 4.2.3 に aufs パッチと前述の userns 内で overlayfs マウント可能にするパッチを当てた環境(脆弱性あり)
- Kernel 4.4 に aufs パッチと前述の userns 内で overlayfs マウント可能にするパッチを当てた環境(脆弱性なし)
Kernel 4.2.3 の Overlayfs
$ ./UserNamespaceOverlayfsSetuidWriteExec -- /bin/bash Setting uid map in /proc/3749/uid_map Setting gid map in /proc/3749/gid_map euid: 65534, egid: 65534 euid: 0, egid: 0 overlayfs Namespace helper waiting for modification completion Namespace part completed
成功してるらしいけど、これだけだと何がなんだか謎なので、gdb でステップ実行しながら、Overlayfs マウントして su を chmod したあたりで止めてチェックしてみました。
$ gdb ./UserNamespaceOverlayfsSetuidWriteExec (gdb) set follow-fork-mode child (gdb) b 63 Breakpoint 1 at 0x400fb5: file UserNamespaceOverlayfsSetuidWriteExec.c, line 63. (gdb) run -- /bin/bash Starting program: /home/karma/userns/UserNamespaceOverlayfsSetuidWriteExec -- /bin/bash warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000 [New process 3236] Setting uid map in /proc/3236/uid_map Setting gid map in /proc/3236/gid_map euid: 0, egid: 0 euid: 0, egid: 0 [Switching to process 3236] Breakpoint 1, childFunc (arg=0x7fffffffe668) at UserNamespaceOverlayfsSetuidWriteExec.c:63 63 result=chmod("su", 04777); (gdb) n 64 if(result) { (gdb) p result $1 = 0 (gdb)
ここで止めます。別シェルで
$ sudo nsenter -t 3908 -m -U plamo64:/# ls -l /tmp/x/bin/su -rwsrwxrwx 1 nobody nogroup 161 1月 15 18:38 /tmp/x/bin/su plamo64:/# ls -l /tmp/x/over/su -rwsrwxrwx 1 nobody nogroup 161 1月 15 18:38 /tmp/x/over/su
ここで su のパーミッションが変わっているのがマズいんだと思う。
Kernel 4.4 の Overlayfs
対策済みなカーネルでやるとこんな風に。
$ ./UserNamespaceOverlayfsSetuidWriteExec -- /bin/bash Setting uid map in /proc/4960/uid_map euid: 65534, egid: 65534 Setting gid map in /proc/4960/gid_map euid: 0, egid: 0 overlayfs Mode change failed Failed to open /proc/4960/cwd/su, error No such file or directory
同じように gdb から起動して chmod のあとで止めてみます。
$ gdb ./UserNamespaceOverlayfsSetuidWriteExec (gdb) set follow-fork-mode child (gdb) b 63 Breakpoint 1 at 0x400ff1: file UserNamespaceOverlayfsSetuidWriteExec.c, line 63. (gdb) set env TRY_AUFS=1 (gdb) run -- /bin/bash Starting program: /home/karma/userns/UserNamespaceOverlayfsSetuidWriteExec -- /bin/bash warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000 [New process 3215] Setting uid map in /proc/3215/uid_map Setting gid map in /proc/3215/gid_map euid: 0, egid: 0 euid: 0, egid: 0 aufs [Switching to process 3215] Breakpoint 1, childFunc (arg=0x7fffffffe668) at UserNamespaceOverlayfsSetuidWriteExec.c:63 63 result=chmod("su", 04777); (gdb) n Failed to open /proc/3215/cwd/su, error Permission denied 64 if(result) { (gdb) p result $1 = -1 (gdb)
上記で失敗しているのはわかるんだけど、ここで同じく別シェルを開いて一応確認。
$ sudo nsenter -t 3215 -m -U plamo64:/# ls -l /tmp/x/bin/su -rwsr-xr-x 1 nobody nogroup 37400 1月 22 2015 /tmp/x/bin/su plamo64:/# ls -l /tmp/x/over/su -rwsr-xr-x 1 nobody nogroup 37400 1月 22 2015 /tmp/x/over/su
Kernel 4.2.3 の aufs (allow_userns=1)
allow_userns=1 で実行。
$ TRY_AUFS=1 ./UserNamespaceOverlayfsSetuidWriteExec -- /bin/bash Setting uid map in /proc/3321/uid_map Setting gid map in /proc/3321/gid_map euid: 0, egid: 0 euid: 0, egid: 0 aufs Mode change failed Failed to open /proc/3321/cwd/su, error Permission denied
$ gdb ./UserNamespaceOverlayfsSetuidWriteExec (gdb) set follow-fork-mode child (gdb) set env TRY_AUFS=1 (gdb) b 63 Breakpoint 1 at 0x400ff1: file UserNamespaceOverlayfsSetuidWriteExec.c, line 63. (gdb) run -- /bin/bash Starting program: /home/karma/userns/UserNamespaceOverlayfsSetuidWriteExec -- /bin/bash [New process 3399] Setting uid map in /proc/3399/uid_map Setting gid map in /proc/3399/gid_map euid: 0, egid: 0 euid: 0, egid: 0 aufs [Switching to process 3399] Breakpoint 1, childFunc (arg=0x7fffffffe668) at UserNamespaceOverlayfsSetuidWriteExec.c:63 63 result=chmod("su", 04777); (gdb) n 64 if(result) { (gdb) p result $1 = -1 (gdb) n 65 fprintf(stderr, "Mode change failed\n");
Kernel 4.2.3 の aufs (allow_userns=0)
allow_userns=0 にすると、そもそも Userns 内のユーザはマウントできないので
$ TRY_AUFS=1 ./UserNamespaceOverlayfsSetuidWriteExec -- /bin/bash Setting uid map in /proc/3245/uid_map Setting gid map in /proc/3245/gid_map euid: 0, egid: 0 euid: 0, egid: 0 aufs Overlay mounting failed: 1 (Operation not permitted)
Kernel 4.4.0 の aufs
4.2.3 と同じような動き。
$ TRY_AUFS=1 ./UserNamespaceOverlayfsSetuidWriteExec -- /bin/bash Setting uid map in /proc/3202/uid_map Setting gid map in /proc/3202/gid_map euid: 0, egid: 65534 euid: 0, egid: 0 aufs Mode change failed Failed to open /proc/3202/cwd/su, error Permission denied
$ gdb ./UserNamespaceOverlayfsSetuidWriteExec (gdb) set follow-fork-mode child (gdb) b 63 Breakpoint 1 at 0x400ff1: file UserNamespaceOverlayfsSetuidWriteExec.c, line 63. (gdb) set env TRY_AUFS=1 (gdb) run -- /bin/bash Starting program: /home/karma/userns/UserNamespaceOverlayfsSetuidWriteExec -- /bin/bash warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000 [New process 3215] Setting uid map in /proc/3215/uid_map Setting gid map in /proc/3215/gid_map euid: 0, egid: 0 euid: 0, egid: 0 aufs [Switching to process 3215] Breakpoint 1, childFunc (arg=0x7fffffffe668) at UserNamespaceOverlayfsSetuidWriteExec.c:63 63 result=chmod("su", 04777); (gdb) n Failed to open /proc/3215/cwd/su, error Permission denied 64 if(result) { (gdb) p result $1 = -1 (gdb) n 65 fprintf(stderr, "Mode change failed\n"); (gdb)
(2016-01-20 追記) http://lwn.net/Articles/671641/ にこの件が載っていますね。
[DIY] 一日限りの復活(?) 家を建てる日記 番外編 - 年に一度の恒例のお手入れ
実は記事数は少ないものの、このブログの一番のアクセス数を稼いでいるDIYカテゴリ。久々に帰ってきました。
むかーし家を建ててる時の現場の写真を PukiWiki に簡易的なブログ的に記録してたことがありました。その Wiki はもう全く更新していないのですが、その続編みたいな。
最近、mizzy さんの フリーランスが家を建てる日記 が更新されるたびに楽しく拝見してます。人の家でもどんどん出来上がっていくの見るのは楽しいですね。
それに触発されて、年に一回、家の一階のワックスがけをやるのですが、先日それをやった様子を書いておきます。
まずはリビング、ダイニング、キッチンのものを全て別の部屋に移動します。今流行りのミニマリストの家みたいになりました。ウチってこんなに広かったんだ!!
これが我が家のワックスがけセット。
「床クリーナー」をぬるま湯で薄めて、床を水拭きするわけですね。このクリーナーはワックスを剥離できるので、ワックス表面についた汚れも取れますね。ひどいところは原液を垂らしてゴシゴシすると、表面のワックスが面白いように剥がれて汚れが取れますね。床に直接膝をついて作業してると痛くなってくるので、毛布に乗ってやるわけです。毛布はリビングのテレビとか家具を移動させるときにも使いますよ。
定規は床にこべりついたものを剥がすのに使います。剥がれたゴミはコロコロで取ります。定規をヘラのように使うと表面のワックスもはがせます。
この水拭きに数時間かかって、その後のワックスはほんの 30 分ほどで済んでしまいますね。ハイ、出来上がり。
Linux 4.3 の Process Number Controller (1)
連載書いたり、勉強会で発表したりしているとなかなかブログが更新できませんね。久々の更新です。
これを書いている時点では Linux カーネルの 4.3-rc1 がリリースされていますが、久々に cgroup に新しいコントローラが追加されそうですね。
説明が不要なほどシンプルで分かりやすいです。このブログエントリ読まなくても使えるはず。:-)
軽く使ってみました。大体は上記ドキュメントに沿って試してるだけです。
カーネル
カーネルの config では Cgroup 関連の設定以下に "PIDs cgroup subsystem" という項目が新設されているので、ここを有効にしてカーネルを作成するだけです。
General setup ---> Control Group support ---> [*] PIDs cgroup subsystem
起動すると /proc/cgroups に pids が現れます。
# cat /proc/cgroups #subsys_name hierarchy num_cgroups enabled cpuset 1 1 1 cpu 2 1 1 cpuacct 3 1 1 blkio 4 1 1 memory 5 1 1 devices 6 1 1 freezer 7 1 1 net_cls 8 1 1 perf_event 9 1 1 net_prio 10 1 1 hugetlb 11 1 1 pids 12 2 1 debug 13 1 1
マウント
まずはマウントしてみましょう。単一階層構造でなく、現在の仕様の cgroup でも使えるようです。
マウントは他のコントローラと同じです。例えば
# mkdir -p /sys/fs/cgroup/pids # mount -t cgroup -o pids none /sys/fs/cgroup/pids
なかにはどんなファイルがあるかな? ルートは以下のような感じでした。
# ls /sys/fs/cgroup/pids/ cgroup.clone_children cgroup.sane_behavior pids.current tasks cgroup.procs notify_on_release release_agent
"pids.current" が PIDs サブシステム独自のファイルのようですね。
cgroup の作成
グループを作成してみます。ディレクトリを作ることに変わりはありません。
# mkdir /sys/fs/cgroup/pids/test01 # ls /sys/fs/cgroup/pids/test01 cgroup.clone_children notify_on_release pids.max cgroup.procs pids.current tasks
"pids.current" と "pids.max" という 2 つのファイルがこのコントローラの独自のファイルのようですね。
制限の設定
ファイル名を見ただけで大体わかると思いますが、それぞれのファイルの役割は以下です。
- pids.current
- 現在のプロセス(タスク)数
- pids.max
- 許可するプロセス(タスク)数の最大値
先に作った "test01" の作成直後、つまり初期値は以下のようになっています。
# cat /sys/fs/cgroup/pids/test01/pids.current 0 # cat /sys/fs/cgroup/pids/test01/pids.max max
"tasks" ファイルに何も追加していないので、当然現在値は 0 です。制限値なしにするには "pids.max" に "max" と書くようで、これが初期値です。
制限値を 2 にしてみましょう。
# echo 2 > /sys/fs/cgroup/pids/test01/pids.max # cat /sys/fs/cgroup/pids/test01/pids.max 2
きちんと設定されました。
現在のシェルをグループに追加します。
# echo $$ | tee /sys/fs/cgroup/pids/test01/tasks 5600 # cat /sys/fs/cgroup/pids/test01/pids.current 2
作成後に現在値を見ると、追加したシェルと実行した cat コマンドで 2 となっていますね。では、ここで 2 つ同時にコマンドを実行してみましょう。
# ( /bin/echo "Here's some processes for you." | cat ) bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: Resource temporarily unavailable Terminated
シェルで 1 消費している状態で 2 つ実行しようとしたので、プロセスを生成できませんでした。
当たり前ですが、ちゃんと制限が効いていますね。
階層構造
上記の "test01" の子グループとして "test02" を作り、"test01" には制限をかけ、"test02" には制限をかけない状態にしてみます。
# mkdir /sys/fs/cgroup/pids/test01/test02 # echo 2 > /sys/fs/cgroup/pids/test01/pids.max # cat /sys/fs/cgroup/pids/test01/pids.max (test01の制限値は2) 2 # cat /sys/fs/cgroup/pids/test01/test02/pids.max (test02は無制限) max
以下のような設定です。
/sys/fs/cgroup/pids/ └── test01 --> (pids.max = 2) └── test02 --> (pids.max = max)
この状態で "test02" で "test01" の制限を超えるプロセスを起動してみます。
# echo $$ > /sys/fs/cgroup/pids/test01/test02/tasks (1 つ追加) # ( /bin/echo "Here's some processes for you." | cat ) bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: Resource temporarily unavailable Terminated
起動しませんね。つまり複数の階層がある場合は、上位の階層の制限にひとつでも引っかかればダメということです。
制限を設定した状態でグループのタスクを増やす
"pids.max" を設定した状態で、グループの "tasks" にたくさんプロセスを追加していったらどうなるでしょう?
# echo 2 > /sys/fs/cgroup/pids/test01/pids.max # cat /sys/fs/cgroup/pids/test01/pids.max 2
"test01" に 2 の制限を設定しました。別途起動してあるプロセスを追加していってみましょう。
# echo 5954 > /sys/fs/cgroup/pids/test01/tasks # echo 5955 > /sys/fs/cgroup/pids/test01/tasks # echo 5956 > /sys/fs/cgroup/pids/test01/tasks #
おや、特にエラーにならずに、追加できてしまっています。
# cat /sys/fs/cgroup/pids/test01/tasks (追加したタスクは全部存在する) 5954 5955 5956 # cat /sys/fs/cgroup/pids/test01/pids.max (制限値は2に設定されている) 2 # cat /sys/fs/cgroup/pids/test01/pids.current (現在値は3で、制限値より多い) 3
つまり fork() や clone() で新しいプロセスを起動しようとする時にだけ制限が効くということですね。
存在するプロセス数以下の制限値を設定する
では、プロセスが既にグループに存在する状態で、その数以下に "pids.max" を設定してみます。
# echo 2 > /sys/fs/cgroup/pids/test01/pids.max (制限値は2) # cat /sys/fs/cgroup/pids/test01/tasks 5968 5970 # cat /sys/fs/cgroup/pids/test01/pids.current (現在2つのタスクが存在) 2
制限値以下であるふたつのタスクが存在していますが、ここで制限値を 1 に減らしてみましょう。
# echo 1 > /sys/fs/cgroup/pids/test01/pids.max (制限値を1に下げる) # cat /sys/fs/cgroup/pids/test01/tasks 5968 5970 # cat /sys/fs/cgroup/pids/test01/pids.current (タスクは2つのまま) 2
タスクは 2 つのままですね。
まとめ
Linux 4.3-rc1 環境で PIDs コントローラを試してみました。
- 制限値を設定しておくと、グループ内で fork() や clone() で新たに起動するプロセスを制限できる
- グループの "tasks" にプロセスを追加したり、制限値を変更したりしても、制限にひっかからない。つまり "pids.max" < "pids.current" となることもある
- グループに属することのできるタスク数は、そのグループの上位(祖先)のグループの制限を受ける
つづくかも...