たのしく学ぶLinuxカーネル開発(第二回): システムを一時的に停止させる

3 min読了の目安(約2000字TECH技術記事

はじめに

システムを一時的に停止させるstop-machineというカーネルモジュールを紹介します。ソースはここにあります。使いかたはREADMEを見てもらえればわかります。

Ubuntu 18.04上でlinuxカーネル4.18.0-18-genericを起動させた状態で動作検証をしました。

現在の実装ではカーネルモジュールをロードしてから5秒だけシステムを停止させます。ソースの中のSTOP_MSECS定数を変更すればミリ秒単位で一時停止させる時間を変更可能です[1]。ここでいう停止とは次のようなものです。

  • あらゆるプロセスが動作できない
  • あらゆるデバイスが動けない。正確にいうとデバイスから割り込みが上がってきても停止が解除するまで待たせられる。
  • 上記に伴いストレージI/O、ネットワークI/Oが動かないし、ユーザからの操作も一切付けつけない

考えられる用途

このモジュールは、システムが応答しなくなったような状態でソフトウェアがどう振舞うかというテストを簡単にできるので便利です。Chaos Engineeringの部品の一つとして、間欠的なノード障害を手軽に起こすという手段としても使えるかと思います。

本当に止まっているかを確認してみましょう。まずはモジュールをロードするのとは別の端末を開いて、1秒ごとに現在時刻を表示するスクリプトを動かします。

$ for ((;;)) ; do sleep 1 ; date -R ; done
Sat, 04 May 2019 09:40:27 +0900
Sat, 04 May 2019 09:40:28 +0900
Sat, 04 May 2019 09:40:29 +0900
Sat, 04 May 2019 09:40:30 +0900
Sat, 04 May 2019 09:40:31 +0900
...

この状態でもとの端末に戻ってモジュールをロードすると、約5秒後にshellのプロンプトが出てきます。

$ sudo insmod stop-machine.ko
$                 # insmodコマンドを発行してから約5秒後にこのプロンプトが出てくる 

一秒ごとに現在時刻を出力していた端末では5秒間時間が飛んだように見えていることがわかります(以下実行例の☆1から☆2の間)。

...
Sat, 04 May 2019 09:44:38 +0900
Sat, 04 May 2019 09:44:39 +0900
Sat, 04 May 2019 09:44:40 +0900
Sat, 04 May 2019 09:44:41 +0900
Sat, 04 May 2019 09:44:42 +0900
Sat, 04 May 2019 09:44:43 +0900
Sat, 04 May 2019 09:44:44 +0900 ☆1
Sat, 04 May 2019 09:44:49 +0900 ☆2
Sat, 04 May 2019 09:44:50 +0900
Sat, 04 May 2019 09:44:51 +0900
...

最後に現在時刻を出していた端末を閉じた上で、モジュールを以下のようにアンロードしておきましょう。

$ sudo rmmod stop-machine
$ 

実装のはなし

このモジュールは、linuxカーネル内のstop_machine()という関数を使っています。この関数は第一引数によってシステム停止中に実行するハンドラを、第二引数によってこのハンドラ実行時に渡される引数を、第三引数によって「どのCPUを停止させるか」を決めます(NULLにすれば全CPU)。stop_machine()関数はカーネル内で他の処理に一切割り込まれたくない処理において使われます。このモジュールにおいてはハンドラの中で1秒だけCPUを使うような無限ループを全CPU上で回しています。

stop_machine関数について興味のあるかたはソース[2]をごらんください。この記事を書いた後にakachochinさんが大変よい記事を書いてくださったのでまずはこちらを読むといいと思います。

脚注
  1. モジュールパラメタを使えばロード時にこの値を変更できるようになります。興味のあるかたはやってみてください。 ↩︎

  2. カーネルソース内のinclude/linux/stop_machine.hやkernel/stop_machine.cなど ↩︎