I/O
さて、メモリと CPU が最低限のコンピュータのセットだと説明しました。
そのあとに説明した部品(デバイス)というのは、ではどのようにして CPU やメモリとやりとりをするのでしょうか?
CPU は基本的にメモリとアクセスすることしかできません。グラフィックの場合は VRAM というメモリが映像を表現することを説明しました。
ではハードディスクはどうだったのでしょうか?
ハードディスクに書き込むためのメモリがどこかにあるのでしょうか?
でもそれだと、320 ギガバイトのハードディスクには 320 ギガバイトのメモリが必要になってしまいます。
その秘密を解くのが I/O というものです。
I/O とは Input Output の略で、これ自体が何か特別な意味を持っているわけではありません。
ただコンピュータ(パソコンとかなんでも)を設計するとき、どこどこに書き込むとメモリ以外のデバイスとやりとりできるというメモリ空間を作るのです。つまり CPU からはメモリに見えるのですが、実際はそのメモリはデバイスと接続されており、そのメモリに CPU がアクセスするとそのデバイスと情報をやりとりすることができるのです。
また、CPU の中にはもう一つのアクセスできる空間を持っているものもあります。
それが I/O ポートです。in と out という特別な命令を使って CPU はメモリではなく、デバイスとつながっている線とアクセスすることができるのです*1。
前者を、Memory Mapped IO、後者の方を I/O Port と言います。
パソコンでよく聞く、AGP、PCI、PCI-Express なんかはすべてこの I/O なのです。
もちろんハードディスクや光学ドライブをつなぐ IDE、S-ATA なんていうのも全て I/O です。つまり、CPU とメモリ以外はこの I/O というものによってつながっているのです。
じゃぁ 320 ギガバイトのハードディスクにアクセスするために、320 ギガバイト分の I/O があるのかというとそう言うわけではありません。この I/O に CPU はどういう事をして欲しいかというのをリクエストして、それにつながっているデバイスが答えるのです。
たとえばハードディスクに今あるメモリの内容を書き込みたいとき、CPU はハードディスクのつながっている I/O へとアクセスします。
ちなみにどの I/O がハードディスクなのかは、コンピュータを設計する人が決めます。
皆さんが使っている Windows 機の場合はそのアクセスする場所が規格によって決まっています。だからどのメーカーのパソコンを買っても、同じように動くのです。CPU が同じでも、この I/O の場所が違っていたら、実は動かないのです。
さて、CPU はハードディスクがつながっている I/O に「○○バイト書き込みたいいんだけど」という指令を出します。その時にハードディスクのどこに書き込むか、どこが空いているかなどを問い合わせます。
ハードディスクが CPU が問い合わせた内容に合致すれば OK を出し、書き込み準備を整えます。空き容量が足りないなどの問題が発生したら、ハードディスクは書き込めない旨を CPU に連絡します。
書き込む準備ができたら、CPU は自分のメモリから、ハードディスクの I/O に対して書き込みたいデータをどんどん送り出していきます。このときに CPU の速度で一気に送ってはいけません。あくまでもハードディスクから「書き終わったよ、次のデータをおくれ」という返事がくるまで CPU は待たなければなりません。
これを繰り返して書き込みたいデータをすべてハードディスクに送ると、CPU は「これでおわりだよ」という命令を送って、書き込みが終了するわけです。
読み出しも手順は似たようなものです。
実際はもっと細かい作業が発生するのですが、まぁ大まかにはこんな感じです。
これはハードディスクに限らず、およそほとんどのデバイスがこのような仕組みになっています。たとえばプリンタを例にとれば、印刷したいことを CPU はプリンタがつながっている I/O に送るわけです。するとプリンタは紙がセットされているか、どの大きさの紙がセットされているかなどの情報を CPU に返します。そして印刷が可能であれば準備できたことを CPU に知らせます。
すると CPU はプリンタに印刷したい内容を送り始めるわけです。
でもここでプリンタの処理を超えてデータを送ってはいけません。プリンタが受け取ったデータを処理しきるまで CPU は待機し、プリンタが次の処理ができるようになったらまたデータを送り始めます。
こうやってみると、なんだか CPU は待ってばかりに見えませんか?
そうなのです、I/O にアクセスするとき、CPU は極端に遅くなります(実はメモリでも遅くなりますが、それはまた別の項で説明します)。
CPU にとって I/O につながっているデバイスはとてつもなく遅いのです。
たとえばインクジェット・プリンターが紙を往復している間に、CPU はどれくらいの処理ができるでしょうか。
それを考えると、CPU にとってこの待ち時間は著しく無駄な時間になってしまいます。
ハードディスクにアクセスに言っているときも、Windows がすごく遅いですよね。これもずばり待ち時間が関係してくるのです。
これを解決するには様々な方法があります。有名なのが割り込み、DMA 転送、キャッシュです。
まず基本的に I/O デバイスというのは割り込みという機能を持っています。
これはどのようなものかというと、デバイスに何か起きたときに CPU に知らせる機能です。
人間でたとえるなら、電話や玄関のブザーのようなものでしょうか。
CPU はデバイスにデータを渡したあと、次のデータを渡すために待つ必要があるわけですが、その間に別の処理をするようにします。そして次のデータを受け取る準備がデバイス側にできたとき、デバイスは割り込みを発生させます。
すると CPU は送り途中だった所に戻り、また続きのデータを I/O へ送り、そしてまた別の処理に戻るのです。
こうすれば、I/O へデータを転送したいときだけ CPU は拘束され、それ以外は他の作業ができます。
次に I/O に渡したいデータがすでにそろっている場合、I/O に渡す処理を CPU がやらずに別の転送専用のデバイスを使う方法があります。これを DMA(Direct Memory Access)転送と言います。これはすでに転送したいデータがメモリにそろっている場合に有効です。CPU がリアルタイムで計算しながらその計算結果を I/O に送らなければならない場合には使えません。
CPU は DMA に「メモリのここからここまでを、ここに転送しておいてね」と頼んでおくとあとはかってに DMA がデータを転送してくれます。そのため CPU は他の処理が実行できるのです。
デバイスの速度が極端に遅い場合、もしくはリアルタイムに変化する必要がない場合、キャッシュという手も有効です。これはデバイス内部にメモリを持っておく方法です。CPU はデバイスの処理を待たずにどんどん I/O にデータを送り続けます。デバイスはキャッシュにどんどんデータをため込み、キャッシュに入っているデータから順に取り出して処理をしていきます。
CPU はキャッシュがいっぱいになるまでは、とにかくデバイスの速度に関係なくデータを送り続けることができるというわけです。
プリンタなんかが、1 ライン印刷するたびに CPU が待たなくてすむのは、このキャッシュのおかげです。キャッシュに印刷したいデータを送ってしまえば CPU はもうプリンタのことは気にする必要もないのです。
でもこれも、リアルタイムで逐一結果が変わっていくような処理ではなかなか使えません。またキャッシュの容量よりも巨大なデータの場合は、キャッシュがいっぱいになってしまうと CPU は待たなければならなくなってしまいます。
しかしながらこのような努力をしても、やっぱり遅延は発生してしまいます。それはメモリが同時にいくつものデバイスからアクセスできないからです。
たとえば DMA 転送したい、CPU もメモリからデータを取り出したいと言う具合に、メモリの取り合いが起きてしまうのです。
メモリは一度に一つの要求しか満たせません。CPU が CPU 内部で計算だけしているときは DMA はメモリにアクセスしたい放題ですが、CPU がメモリにデータをとりにいくと、CPU にデータを渡すために DMA からのアクセスはいったん止めなければなりません(このような処理をスケジューリングといいます。本ページの一番最後で説明します)。
もちろんメモリ空間が全く違う部分の場合は同時にアクセスできたり、デュアルポートといって複数の場所から一度にアクセスできるようにメモリも改善されてきていますが、パソコンの場合多くのデバイスがつながっているので、全部が全部一度にアクセスできるようにはなっていないのです。
というわけで CPU とメモリ以外を結ぶ I/O 、これによってコンピュータは様々なデバイス(装置、部品)を制御することができます。ですがその反面、速度がかなり犠牲になるのです。