「すいみんpreparation」解説

注意: この文章は教科書ではありませんし正確性も保証されていません。より詳細で正確な情報を知りたい場合には、ここで登場したキーワードをもとに自分で調べてみてください。

はじめに

眠れない夜にOSのことを考えていたら、ふとメロディを思いついてしまったのでそれを打ち込んだのが2/2のことでした。

そこから勢いで曲を打ち込んで歌詞をつくり(この段階で歌詞は出来上がっていたので伏線)

歌って動画を作った結果がこちらです。

なお歌はあまり上手くないので、声なしのバージョンがいいよという方はSoundCloudにありますのでそちらもどうぞ!

soundcloud.com

さて、この曲に対する人々の反応ですが、

はい、極めて正常な反応だと思います。

実のところ、この歌詞にはOSネタを大量に盛り込んでいたので、おそらくそのあたりの知識がないと完全には面白さが伝わらなかった可能性があります。

そこで本記事では、「すいみんpreparation」の歌詞を追いつつ、どのようにOSネタとの関連があるのか、どのような意図をこめた歌詞になっているのかについて解説したいと思います。

全体のテーマ

動画の概要欄に「やりたいことで手一杯なOSのみなさんのために」と書かれている通り、OSの果たす役割のうちの一つである「資源の管理」という側面について、その難しさを日常生活になぞらえて描写することが今回のテーマでした。

特に、タスクのスケジューリング問題に関しては、かなり強い仮定をおいてもなおNP完全な問題であることが "NP-complete scheduling problems" (J.D. Ullman, 1975) で示されています。ちなみにUllman氏は、コンパイラを実装したことのある皆様にはおなじみ、通称ドラゴンブックの作者の一人です。

www.sciencedirect.com

OSのみなさんだけでなく、人間のみなさんにとっても、日々遂行しなければならないタスクが山積していて、その処理に追われているという状況はよくみられることでしょう。

そのような困難な状況下にあっても、最優先で処理すべきタスクのひとつ、それが睡眠である、というのが本作品の主要な論点になります。

歌詞とその背景

では、最初から順番に歌詞を追ってゆきましょう。

いつも気づいたら 時計の針はとびまわり 
明日という日が 足音立てず向かってくるよ

作業に没頭していると、時間があっという間に過ぎてしまって、気づけば夜中になっていた!という経験はみなさんよくあるのではないでしょうか。

OSにおいては、並行処理における競合状態を回避するために「割り込み」を禁止することがあります。(このような競合状態の発生する可能性のある処理の範囲のことを、「クリティカルセクション」と呼びます。)

「割り込み」は、外部デバイスからの信号などを契機として、現在実行している処理に優先してなんらかの処理を行わせる仕組みのこと、またそれを引き起こす信号のことを指します。

割り込みを発生させる外部デバイスの状態変化の例としては、キーボードのキーを押下した時や、ネットワークを介してパケットが到着した時、外部記憶装置に対する書き込み要求が完了した時などが挙げられます。

他にも、重要かつ基本的な割り込みの一つとして、タイマー割り込みというものが存在します。これは、ある時間(たとえば10ms)が経過するごとに割り込みを発生させる、というものです。

OSは、このタイマー割り込みを活用することにより、時分割マルチタスクを実現しています。マルチタスクは、CPUの処理能力という限られた資源を有効活用するというOSの大事な役割のひとつです。 したがって、それを支えるタイマーや時刻の管理も、実はOSの重要な仕事のひとつなのです。(Linuxではtimekeepingサブシステムがこれを担当しています。)

さて、以上のことを念頭に一つ考えてみることにしましょう。もしも割り込みをずっと止めていたら、つまり割り込みを無視し続けたら、一体何が起こるでしょうか?

……そうです、時分割マルチタスクの仕組みが正しく動作せず、現在実行しているタスクが終わるまでずっとそのタスクを処理しつづけることになります。

これは、何かに没頭して時間の経過を忘れてしまうことと対応します。(人間においてこのような状況は「過集中」と呼ばれることもあります。)

その結果として、いつの間にか意図せず夜ふかしをしてしまうことを「明日という日が 足音立てず向かってくるよ」と表現しています。

また、タイマー割り込みによって定期的に行われていた時刻の管理も正しく行われなくなるため、時計が狂ってしまうこともあります。

それが「いつも気づいたら 時計の針はとびまわり」という部分の意味です。

今日も 下手したら 同じところを周ってばかりで
明日できるさ 帰納法によれば成り立たぬ

得てして長時間の割り込み禁止が発生してしまうのはバグの存在を意味します。というのも、先ほど説明した通り、長時間の割り込み禁止は様々な問題を引き起こす可能性があるため、そのような状況が発生しないように気をつけてコードを書く責任が開発者にはあるからです。

そもそも、アプリケーションレベルでは、割り込みを禁止することは一般に許可されていません。OSのみが割り込みに関連する制御を行うことができるよう、CPUの保護機構を活用して制限をかけている場合がほとんどです。(ユーザーモードカーネルモード・特権命令 等々で調べてみてください。)

したがって、この種のバグは、OS側で処理が無限ループするなどの要因で発生していることが考えられます。これを描いたのが「今日も 下手したら 同じところを周ってばかりで」という部分です。

これを日常生活における類推に落とし込むと「明日から本気出す」などの、問題を先送りにする場面が想像できます。(耳が痛いです…。)

残念ながら「明日になればできる」と考えているときは、永遠に来ない相対的な「明日」に対して問題の先送りを繰り返し、目標が達成されないか、達成されたとしても非常に長い時間がかかることが経験上知られています。

このように、いくつかの事実から一般的な規則を導く手法を枚挙的帰納法といいます。ただ、枚挙的帰納法数学的帰納法とは異なり、導かれた規則が正しいとは限りません。(ということで希望を失わずにがんばりましょう!)

やりたいことばかりで リソース不足まっしぐらなの
コンテキストスイッチだけで 手一杯だよ

これ…ほんとこれなんですよ。

時間は有限な資源です。それはOSにおいても同様です。 CPUのコア一つ一つは、単純な操作を繰り返しているに過ぎず、同時に複数の処理を行うことができません。 そのため、並行して処理を進めるためには、何らかの工夫をする必要があります。 複数のコアを利用して処理を進める並列計算はひとつの手段ですが、それでもコア数分しか並行に演算を行うことができません。 そこで、ほとんどのオペレーティングシステムでは、あるコアの上で実行されるアプリケーションを短い時間間隔(これをタイムスライスといいます)で切り替えることにより、大局的にみれば複数のアプリケーションが並行して動作しているようにみせる機能を有しています。これを、マルチタスキングといいます。

あるCPU上で実行されているアプリケーションを別のアプリケーションに切り替える際には、現在のCPUの内部状態の一部(CPUコンテキスト)を保存してあげる必要があります。この作業をコンテキストスイッチといいます。 より具体的には、CPUのレジスタに入っている値を、各プロセスごとに別々に用意されたメモリ領域にコピーして退避するなどの処理を行なっています。

コンテキストスイッチもCPU上で実行されるプログラムにすぎませんから、その処理には一定の時間がかかります。

ここまでくれば、もう察しの良い方は気づいているかもしれません…そうです、もしも非常に多くのアプリケーションを単一のマシン上で実行しようとすると、コンテキストスイッチにかかる時間が非常に大きくなり、さらには各アプリケーションが実行される頻度もどんどん下がってゆくことになります。つまり、アプリケーションの処理がほとんど進まなくなってしまう可能性があるわけです。これを飢餓状態(resource starvation)といいます。

…ですから、やりたいこと(アプリケーション)の数が多すぎるとリソース(資源、ここではCPU時間)不足に陥り、コンテキストスイッチだけでCPU時間を食い尽くす飢餓状態になる、ということです。

みなさんもやりたいことスタックの大きさには気をつけておきましょう。(耳が痛いです…。)

ぐーるぐるぐる回る run queue
このスケジューラーは壊れてる
panic 寸前のシステムを
救えるのは君だけさ ほらね

さて、前項で説明したコンテキストスイッチを司るのは、OSの中でもスケジューラーと呼ばれる部分になります。スケジューラーは、実行可能だが現在CPU上では実行されていないタスクの一覧をrun queueとよばれるデータ構造に保持しており、その中から次に実行すべきタスクを選択して、そのプロセスにコンテキストスイッチを行うことで、タスクを順々に実行していきます。(ちなみにここではアプリケーション・プロセス・・タスクという用語が入り乱れていますが、これらの厳密な定義はOSの実装に依存しますので、まあとにかく「処理の管理単位」なんだなあ、と思っていただければ助かります。)

実を言うと近年のほとんどのOSでは、タスクごとの優先度やリアルタイムタスクの実行などを考慮するために、単純なqueueを用いてスケジューラーが実装されているわけではないのですが、シンプルなモデルとしてここではqueueで考えることにします。

queueの中には実行を待っているタスクが入っており、OSはそれらを順番に取り出してCPUに割り当ててゆくことでマルチタスクを実現するので、まるでタスクがキューの中をぐるぐる回っているかのような感じになります。

もしもスケジューラーにバグがあったら、あるプログラムが永遠に実行されなかったり、色々困ったことが起きるかもしれません。下手をすると、システムの動作に必要なプロセスまでもがいつまでも実行されず、OSが固まってしまう、ということもあるかもしれません。そういった、OSにとって重大な問題が発生して、回復できる見込みがない場合は、OSはエラーメッセージを出力して再起動します。これをpanicと呼ぶことがあります。(Windowsでいえばブルースクリーンのようなものです。)

panicが発生するようなギリギリの状態にOSがある際は、十中八九割り込みが禁止されています。このような状況下では、キーボード割り込みなどの通常の割り込みは受け付けられません。

でも、こんなときでもなんとかなる割り込みが実はあるのです。

すいみん睡眠preparation 最高優先度でよろしくね
すいみん睡眠preparation 問答無用で割り込むよ
すいみん睡眠preparation これは無視不可能なシグナル
すいみん睡眠preparation いますぐおふとんへIRET

x86には、NMIという割り込みがあります。これは、Non-Maskable Interruptの略で、CPUでたとえ割り込みを禁止していたとしても、この割り込みに関しては即座に処理が行われる、という特別な割り込みです。(実はAMDのハイパーバイザまわりの文脈ではNMIをマスクできるとかいうNMIのアイデンディティ崩壊級の面白い話があったりするのですがそこらへんは一旦忘れましょう。)当然、この割り込みは、あらゆる割り込みに優先して(他の割り込み処理を実行中でも関係なく)処理が行われます。つまり、無視できないシグナルなわけです。

みなさんが何かの作業に熱中し過ぎている時には、きっと割り込みは無効化されているのでしょう。たとえば、宅配便が届いてインターホンが鳴っても気づかなかったり、呼びかけられても気づけなかったりするのがその一例です。

しかし、時には何よりも優先して実行すべきことがあるケースがあります。それが睡眠です(要出典)。というかそうしたいですね。難しいですが。

言い換えれば、我々の生活の健全性を担保するためには「マスク不可能な、おふとんへ今すぐ行け割り込み」が必要なわけです。

で、IRETというのは、RETurn from Interrtuptという命令で、割り込み処理を終了し、割り込みが起こった際に実行されていたコードの実行を再開するという命令です。 この命令はコンテキストスイッチでも利用されているので、まあ「マスク不可能な、おふとんへ今すぐ行け割り込み」が発生したら、熱中していたタスクから一旦コンテキストスイッチして、睡眠プロセスへ切り替えるというのが重要ですよ、という話をここでしています。

たまにどうしても 無理なときは
仕方ないから 全部吐き出して
今度直せばいいのさ 
次はもっとうまくやれるはず

まあ、無事におふとんへ辿り着けたらいいわけですが、ときには睡眠失敗するケースもあります。そういうときはまあpanicするしかないわけですが、panicする際は、後でなぜpanicしたのかデバッグできるように、メモリの状態をダンプすることがあります。これをコアダンプと言います。また、コアダンプを「吐く」とか「吐かせる」という慣用表現があり(dumpなので)、つまるところどうしても眠れないなら、現在のメモリの状態(頭の中のもやもや)を全部紙かなんかに書き出して、次の修正の参考にするといいよ、ってことを言ってます。

プログラムに完璧はありません。常にバグがあります。なので、コアダンプは大事です。失敗しないことよりも、次の失敗を防ぐ方法をつくること、そしてそのデバッグを容易にする仕組みを用意することが重要です。

だから
すいみん睡眠preparation 入出力は一時停止で
すいみん睡眠preparation 机の上を片付けようね
すいみん睡眠preparation 頭の中身書き残したら
すいみん睡眠preparation おやすみ世界

こっちは正常系の話で、suspend to diskの処理を表現しています。 よく、コンピューターにおけるメモリは、作業をするための机として例えられます。机が広ければよりたくさんのことを同時に効率よくできる、というアナロジーから来ているのでしょう。 さて、コンピューターがsuspendする際には、電力消費を抑えるために、メインメモリの内容をディスクなどの不揮発性の記憶装置に書き出します。(メモリが不揮発性とは限らないよ!NVDIMMがあるよ!と叫んでいる私のことは無視してください。)というのも、通常のコンピューターのメインメモリとして利用されているDRAMは、電荷コンデンサにためるような感じでデータを保持しており、その電荷は時間経過で抜けてしまうため、定期的にデータを読み出して再度書き込むという操作(リフレッシュ)を行う必要があり、継続的に電力を消費するため、ここにデータを置いたままにしておくのは不都合だからです。

とはいえ、OSのほとんどの状態はメモリ上にありますから、OSが必要な処理、たとえば入出力装置とのやりとりなどがsuspend処理中に起こるとよろしくありません。というわけで、一般的にコンピューターをsuspendする場合には、アプリケーションの動作を停止し、さらに入出力装置を一旦止め、そしてメモリの内容をディスクに書き出し、電源を落とす、という処理になります。

みなさんが睡眠の準備をする際も、うっかりSNSなどを見て割り込み処理が発生してしまうと、睡眠失敗panic路線まっしぐらになる可能性が高いです。というわけで、この順序を守って、安全にシステムをsuspendするようにしましょう。

ということで、おやすみなさい!よい夢を!