割り込みとポーリング
2006.6
現在のコンピュータシステムは「割り込み」なくしては実現できないといっても過言ではないんだ。特に、組込みでは「割り込み」を駆使したプログラミングが必須となる。
今回は「割り込み」とはどういうものか、対極にある「ポーリング」という手法とあわせて紹介していこうと思う。
「ポーリング」に関して
まずは、携帯電話を例に考えてみよう。例えば、こんな動作をするよね。
- キーを押すと、画面が切り替わったり、押した文字が表示されたりする。
- 電話の着信があると、音や振動、画面の表示などで知らせてくれる。
- 電波状況は、リアルタイムに画面のアイコンに反映される。
- しばらく操作しないでおくと、液晶画面が暗くなる。
これらに共通する点は「なにかイベントが起こったときに対応した処理をする」ということだ。それぞれのイベントは
- キーが押された
- 電話の着信があった
- 電波状況に変化があった
- 一定時間、操作されなかった
ということになるよね。
同様の動作は、携帯電話以外でもパソコン、テレビ、ビデオ、ゲーム機など、ほとんどのデジタル機器でよく見ることができる。これらのイベントには基本的に互いに関連がなく、システムからすれば「いつ起こるかわからない」ものばかり。このようなイベントを「非同期イベント」と呼ぶんだ。
コンピュータシステムは、このような「いつ起きるか分からない」たくさんの非同期イベントを監視しながら動いているんだ。でも、どうやっていると思う?まず思いつくのは、「イベントが発生しているかを定期的にチェックする」という方法。これを「ポーリング」という。
でも、チェックする周期が長いと、イベントの発生から検出までにタイムラグが生じてしまう。キーを押してから画面に文字がなかなか出なかったら、イライラするよね?このようなイベントはすぐに応答してもらわないと困るけど、だからといってチェック周期を短くすると、CPUがその処理ばかりやることになってしまって、他の処理に支障がでてしまうんだ。
このように、ポーリングで多くのイベントをさくさくと処理するのはちょっと無理があるというのがわかると思う。そこで、出てくるのが「割り込み」なんだ。
「割り込み」とは
割り込みは、CPUとOSでサポートされる機能で、その名のとおり「今やっている処理に割り込んで特別な処理を実行する」こと。「仕事中に電話がかかってきた状態」と考えるとわかりやすいと思うよ。
Aくんが集中して開発作業をやっているところに電話がかかってきました。
電話に出てみると、お客さんからのクレームです。
Aくんはときに謝りながら一生懸命に対応します。
やっと電話が終わり、元の作業に戻ろうとしてAくんは思いました。
「あれ、さっき何をやってたんだっけ?」
そういえば電話に出る直前にメモを残していました。
メモを見て思い出したAくんは開発作業を再開しました。
「電話に出る」=「割り込みを処理する」であることは分かったかな。実際の割り込み処理のシーケンス(動作の順序)も似たような動作になるよ。
- あるデバイスでイベントが発生すると、CPUに割り込み信号が通知される。
- CPUは割り込み信号を受けると、現在の処理を中断。このとき後で戻ってこられるように現在の状態を保存(退避)しておく。
- 対応する割り込み処理を行う。この処理を行う関数は、割り込みハンドラや割り込みサービスルーチン(ISR)と呼ばれ、あらかじめOSに登録しておく。
- 割り込み処理が終わると、状態を復帰させて、元の処理を継続する。
このように割り込みは、自動的に登録しておいた処理が実行される仕組みなんだ。つまり、ポーリングのようにソフトウエアで監視を行う必要がない。
各イベントが起こったときの処理を記述した割り込みハンドラをあらかじめ用意しておけば、いろいろなイベントがいろいろなタイミングで発生しても、それぞれ勝手に処理してもらえるということになる。これは便利だよね!
割り込みは一種のマルチタスク手法なんだ。かつて、MS-DOSなどの非マルチタスクOSの環境しかなかった頃は、1個のメイン (ループ)プログラムと複数の割り込み処理を駆使して、同時並列的な処理を実現していたんだ。現在でも、OSを使わないような小規模マイコンシステムでは同じような構造になっているはずだ。大変便利な割り込みだけど、やっぱりデメリットはある。
- ハードウエアに依存する。
各イベントに対応した割り込み信号がCPUに通知されるようにハードウエアを設計しておかなくてはならない。ソフトウエア側も、どのデバイスの割り込みにどの割り込み番号が割り当てられているかを理解する知識が要求されるんだよ。
- OSに依存する。
割り込みハンドラを登録する時に指定する番号(ベクタともいう)や割り込み制御関数の仕様は、OSによって異なっているんだ。
- 割り込みハンドラにはご法度がある。
長時間の処理はダメ、待ち状態になってはいけない、 呼んではいけない関数がある、などだ。
普通は、割り込みハンドラでは必要最小限の処理のみを行い、別のタスクに通知して、メインの処理はそっちでさせるように実装すべきなんだけどね。
ついでに、ポーリングのメリットも挙げておこう。割り込みとポーリングは状況に応じて、使い分けることが重要なんだ。
- ハードウエアに依存しない。ソフトウエアだけで実現できる。
- ループするだけなので、簡単に実装できる。
- 割り込みを発生できないイベントも監視できる。
割り込みは、使いこなすには少しコツがいるけど、組込み開発では避けて通ることはできないんだ。実際には、いろんな経験をつんでいくことが必要だけど、まずはイメージだけでもつかんで、考え方になじんでほしい。