コンピュータの5大装置とCPU・メモリの役割

コンピュータの5大装置とCPU・メモリの役割

コンピュータの5大装置は中学校の技術や、高校の情報の学習内容に含まれるため、基本的には皆さん聞いたことがある内容だと思います。また、ITパスポートや基本情報技術者試験の試験範囲であるため、大人になってから再学習された方もいるかと思います。

しかし、CPUやメモリが具体的にどのようにデータを保持し、どうやってプログラムが動いているかについては知らない方も多いと思うので、本記事ではこのあたりについて深堀りしていきます。

背景知識がなくても理解できるようにわかりやすく書いたつもりなのでぜひ最後まで読んでみてください。わかりにくい所や間違っている所があったらコメントお願いします。

5大装置

5大装置とデータの流れを図で示すと次のようになります。

図の中の「入力装置」「記憶装置」「制御装置」「演算装置」「出力装置」が5大装置と呼ばれています。

一般的にCPUと呼ばれているものは制御装置と演算装置をまとめたものです。 今回はあえて演算装置の中に「レジスタ」というものを追加しています。 単にレジスタと書きましたが、正確には汎用レジスタと言います。汎用レジスタは演算装置の中に数個~十数個用意されています。一般的な5大要素の図の場合、汎用レジスタは表記されておらず、主記憶装置のデータは単に演算装置に渡されるように書かれています。CPUの種類によっては汎用レジスタでメモリから値を一度受け取り、その後演算装置内の他の装置に転送する形式のものがあります。本記事はこの形式を採用しているARMマイコンで組み込みプログラムを書くことを想定しているためレジスタを図に追加しました。汎用レジスタはぜひとも理解しておいて欲しい要素です。この記事内で詳細を説明するのでしっかり理解しておきましょう。

記憶装置には「主記憶装置」と「補助記憶装置」があります。主記憶装置は「メモリ」や「RAM」と言われているもので、一時的にデータを保存しておくための装置です。一時的というのはプログラム実行中のことだと考えてください。プログラムが終了したり、コンピューターが再起動されたりすると主記憶装置のデータは使えないものとなります。一方補助記憶装置はHDDやSSDなどの大容量ストレージのことで、データを長期間保存するために使用します。

入力装置はユーザーの操作を受けてデータを主記憶装置に保存します。例えば、キーボードのどのキーが押されたか、タッチパネルのタッチされた座標、カメラのセンサーで取得した画像などが入力装置から主記憶装置へ送るデータとなります。

出力装置は演算装置で演算した結果をユーザーに伝えるための装置です。最も一般的なのはディスプレイですが、プリンタ、スピーカーなども出力装置と言えます。

メモリ

それでは、メモリについて詳細に見ていきましょう。メモリは、「列が一列しかないエクセルシート」と考えると良いと思います。つまりメモリは「行数」と「データ」を紐づけて記憶しておく装置であると言えます。エクセルシートを使ってイメージしてみましょう。

エクセルで「行」と呼んでいるものはメモリの場合「アドレス」と言います。各アドレスに対応する格納領域(エクセルでいうとセルのこと)にデータを格納します。

メモリに格納されるデータは0や1を8桁並べた値となっています。0と1だけで示す値を2進数と言い、2進数の桁のことをbit(ビット)と言います。なのでメモリに格納される値は8bitであるということになります。

この8bitと言う大きさは非常に頻繁に出てくるため、8bitのことを1byte(バイト)と定義しています。byteはスマートフォンの通信容量の単位などで馴染み深いですね。一つのアドレスに1byteのデータを格納できるということを抑えておいてください。8bitで表せる最大の数は11111111b(数字の後ろに”b”が付くと2進数であることを意味します)であり、これは10進数で表すと255になります。つまり10進数でいうと、一つのアドレスに格納できるデータは0~255の数字ということになります。

データというと画像データや音声データを想像しやすいですが、メモリが記憶するデータはこれらだけではありません。プログラムも2進数で表されており、実行順序や処理内容がメモリ格納されています。メモリからプログラムを読み込んでソフトを動かす方式をノイマン型と言い、現在のコンピュータは基本的にこの方式を採用しています。

2進数は桁数が大きくなるので人間にとっては読みにくいです。そこで、メモリのアドレスや値を表すときは16進数を使用することも多いです。2進数の4桁が16進数では1桁になるので、32bitの場合8桁になりだいぶ読みやすくなります。エクセルでイメージを掴んでもらいましたが、16進数も使ってより一般的な形に書き換えます。すると次のようになります。数字の先頭に”0x”が付くと16進数であることを意味します。

ちなみに2進数と16進数の変換はwindowsの電卓アプリで簡単に行えます。アプリの左上の三本線を選択して「プログラマー」を選択すると、普段とは少し違う画面に変わります。

“BIN(2進数)”を選択して試しに10101010入力するとHEX(16進数)だとAA、DEC(10進数)だと179であることが簡単にわかります。もちろん2進数以外からの返還も可能です。

macの場合も標準の計算機アプリで似たようなことができます。メニューバーの「表示」の「プログラマ」を選択してください。

メモリは記憶装置と言うだけあって、各アドレスにデータを保管しているだけです。メモリにデータを書き込んだり、メモリのデータを読み込んだりするのは別の装置です。

CPU

そこで登場するのがCPUです。CPUはメモリにアクセスしてデータを読み込んだり、演算装置で計算した結果を書き込んだりします。

CPUのポイントはフェッチ→デコード→実行の順で処理を繰り返すと言うことです。言葉の説明だと理解が難しいので、具体的な例を用いて説明もします。前半は概念的な説明だけで退屈ですが頑張って耐えてください。

フェッチ

プログラムを起動するとCPUのなかの制御装置は、あるアドレスのデータを読み込みます。このアドレスはプログラムの最初の処理(命令)が記憶されている領域になっています。このアドレスに記載されている命令が完了すると、記憶装置は次のアドレスを読んで命令を実行します。それが終わるとまたその次。と処理を繰り返すことでプログラムが動作します。

この「制御装置が命令をメモリから読み込む動作」をフェッチと言います。

プログラムの命令は16bitや32bitだったりします。メモリの1つのアドレスでは8bitしか記憶できないので、複数のアドレスを使って一つの処理内容を記憶しています。なのでフェッチでは複数のアドレスの値をまとめて読み込みます。ここでは、アドレス2つ分で一つの処理内容を表すと想定し、フェッチ時にアドレス2つ分を一度に読み込むこととします。このへんの仕様はCPUアーキテクチャごとに変わりますが、今は特に気にしなくて大丈夫です。

メモリの表現も変えましょう。前項では縦1列でメモリの中身を書いていましたが、アドレス2つ分を一度に読み込むと言うことなので、2列で書いたほうがわかりやすそうです。そこで偶数アドレスは左、奇数アドレスは右にならべて書き直すと次のようになります。

制御装置はこの表を一行ずつフェッチします。2列にしたため表の左列のアドレスは一個飛ばしの値になっています。

デコード

フェッチしたデータ(命令)を制御装置が解析し、どんな演算をするかを決めます。これをデコードと言います。

命令は一般的にオペレーター+オペランドの形になっています。プログラミング言語で言うと、オペレーターが関数、オペランドが引数に対応すると思います。つまり、命令の先頭の何文字かでどんな処理をするかを定義し、残りの文字でどのデータを使うかを指定します。引数がない関数があるのと同じように、オペランドがないオペレーターもあります。

オペレーターやオペランドの種類、表記方法はCPUによって異なります。詳細は使用するCPUのマニュアルを確認してください。下記に具体例を示しますが、すべてのCPUで同じ処理を示すわけではないことに注意してください。

“00100001 00100000″という命令を考えてみます。この命令はオペレーターが “00100”で、「指定した汎用レジスタに値を保存する」ことを示しています。後に続く”001″が保存先のレジスタ番号で、さらにそのあとに続く”00100000″が保存する値です。つまりこの命令をデコードすると、「1番目の汎用レジスタに500を格納せよ」ということになります。

実行

デコード結果をもとに実際に処理を実行することを「実行(execute)」と呼びます。前項の例で言えば、 汎用レジスタの値を書き換えることが実行になります。

実行が完了したら次の命令がフェッチされ、デコード、実行と処理を繰り返します。CPUの処理効率を上げるために、デコード中や実行中に次の命令のフェッチを並行して行なうパイプライン処理というものを実装したシステムもあります。

一連の流れ

プログラミングを理解されている方向けになってしまいますが、下記の簡単なプログラムを想定して具体的な流れを説明します。Pythonはインタプリタ型なので本当は少し違います。大まかなイメージを掴むためのものだと思ってください。

# Python
a = 500
b = a + 30
// C言語
int main(void){
    int a = 500;
    int b;
    b = a + 30;
    return 0;
}

プログラムは変数aに500を代入して、変数bにa+30を代入するだけのシンプルものです。このプログラムはメモリ上ではどのように表現されるのか?CPUはどのように処理するのか?を見てみます。

ちょっとその前に汎用レジスタについて少しだけ説明しておきます。汎用レジスタはCPUの中にある記憶装置で役割はメモリと似ています。しかし、メモリとは異なり「数はせいぜい十数個」「16bitや32bitなどのサイズ(CPU依存)」「高速な読み書きが可能」などの特徴があります。これから説明する例では2つの汎用レジスタ(GR0~GR1)を使用し、サイズは32bitということにします。

また、制御装置の中にはプログラムカウンタというものがあり、次に実行する命令が格納されているアドレスが記憶されています。

さて、一連の流れを見ていきましょう。処理が長いので動画にしました。適宜停止しながら確認してください。フェッチでPCが指すアドレスの値をデコーダに渡し、デコーダがデコードし、実行で汎用レジスタやメモリの値を更新していることに着目してください。二進数で書かれている命令の文法はCPU依存なので特に理解する必要はありません。デコード後の処理内容を記載しているので、各命令で何をするのかを理解してください。プログラミング言語では数行で書かれる処理が、細分化されてCPUが計算できる粒度にされているのがわかると思います。ちなみにプログラミング言語をこの命令に変換する作業こそがコンパイルです。

今回は説明を簡単にするためにメモリの上から順番に処理をするプログラムを考えましたが、「条件によって次に読み込むアドレスを変える」のような場合もあります。このような場合はプログラムカウンタを書き換える処理が行われます。

まとめ

簡単にコンピューターの5大装置を説明し、具体例も挙げながらメモリとCPUの役割、動作について詳細に説明してきました。この説明だけ読んでも、難しく感じたりそもそも理解することの意義を感じないかもしれません。しかし、今後プログラミングを進めていくと壁にぶち当たり、メモリ、CPU、レジスタの知識が欲しくなる時がきっと来ます。その時に改めてここに戻ってきていただければ幸いです。