CPUの命令実行の仕組みを徹底解説|Fetch・Decode・Execute・レジスタ・ALUの役割

はじめに

CPUは、プログラムの命令を実行するコンピュータの中心的な装置です。しかし、「CPUはどのように命令を実行しているのか」を理解している人は意外と多くありません。

例えば、lsコマンドを実行するとディレクトリの一覧が表示され、WebサーバはHTTPリクエストを処理し、データベースはSQLを実行します。一見すると、それぞれ異なる処理を行っているように見えます。しかし、CPUが実際に行っているのは、加算や比較、データの読み書きといった単純な命令を高速に繰り返し実行することです。

CPUは、メモリから命令を取り出し、その内容を解析し、実行結果を書き戻すという一連の処理を繰り返しています。この仕組みは「命令実行サイクル」と呼ばれ、CPUの基本的な動作を理解するうえで最も重要な考え方の一つです。

命令実行サイクルを理解すると、レジスタやプログラムカウンタ、ALUといったCPU内部の各機能がどのような役割を担っているのかが見えてきます。また、LinuxにおけるプロセスやCPUスケジューラ、コンテキストスイッチなどの仕組みを理解するための土台にもなります。

本記事では、CPUが命令を実行する基本的な流れを中心に、命令実行サイクルやレジスタの役割について、図を交えながら分かりやすく解説します。

CPUが実行する「命令」とは

CPUはプログラムを実行する装置ですが、実際にはプログラムそのものを理解しているわけではありません。CPUが理解できるのは、「命令(Instruction)」と呼ばれる非常に単純な処理だけです。

例えば、CPUが実行する命令には、次のようなものがあります。

・データをメモリから読み込む
・データをメモリへ書き込む
・数値を加算・減算する
・2つの値を比較する
・条件に応じて実行する場所を変更する(分岐)

一つひとつの命令は非常に単純ですが、これらを組み合わせることで複雑なプログラムを実現しています。例えば、ユーザーがlsコマンドを実行すると、CPUは「ディレクトリ一覧を表示する」という処理を直接実行しているわけではありません。

実際には、ディレクトリ情報を取得するために必要な命令を一つずつ実行し、その結果としてディレクトリの一覧が標準出力へ表示されます。

同様に、ApacheがHTTPリクエストを処理する場合や、PostgreSQLがSQLを実行する場合でも、CPUが行っているのは加算や比較、メモリへの読み書き、条件分岐といった基本的な命令の実行です。

また、私たちが普段利用しているC言語やC++、Rustなどのプログラミング言語は、そのままではCPUは理解できません。これらのソースコードはコンパイラによってCPUが理解できる機械語へ変換されます。

例えば、次のようなC言語のコードを考えてみます。

int c = a + b;

この処理もCPUから見ると、「aを読み込む」「bを読み込む」「加算する」「結果を書き込む」といった複数の命令に分解されて実行されます。

このように、CPUは常に単純な命令を一つずつ実行しています。アプリケーションの種類や処理内容が異なっていても、その基本的な動作は変わりません。

次の章では、CPUがこれらの命令をどのような流れで実行しているのか、「命令実行サイクル」の仕組みについて詳しく見ていきます。

命令実行の基本サイクル

CPUは、プログラムに記述された命令を一度にまとめて実行しているわけではありません。

実際には、命令を1つずつ取得し、内容を解析し、実行するという一連の処理を高速に繰り返しています。この一連の流れを「命令実行サイクル」と呼びます。

命令実行サイクルは、大きく次のような流れで進みます。

  1. Fetch(命令の取得)
  2. Decode(命令の解析)
  3. Execute(命令の実行)
  4. Load / Store(必要に応じてメモリアクセス)
  5. 次の命令へ

※実際のCPUではパイプラインやアウト・オブ・オーダー実行などにより、複数の命令を効率よく並行処理しています。本記事では、CPUの基本動作を理解するために、命令を順番に処理する単純化したモデルで説明します。

まずは全体の流れを見てみましょう。

それぞれの処理について順番に見ていきます。

①Fetch(命令の取得)

最初にCPUは、メモリ上に配置されたプログラムから、次に実行する命令を取得します。

どの命令を取得するかは、プログラムカウンタ(Program Counter)が保持しているアドレスによって決まります。CPUはこのアドレスを参照し、命令を取り出して次の処理へ進みます。

②Decode(命令の解析)

取得した命令は、そのままでは実行できません。

CPU内部の命令デコーダが命令を解析し、「加算を行う」「データを読み込む」「条件分岐を行う」といった命令の種類を判別します。そして、どの回路を利用して処理を実行するかを決定します。

③Execute(命令の実行)

命令の内容が分かると、CPUは実際に処理を実行します。

例えば、加算や減算、大小比較、論理演算などはALU(算術論理演算装置)が担当します。また、条件分岐命令であれば、次に実行する命令のアドレスを決定します。

④Load / Store(メモリアクセス)

命令によっては、メモリ上のデータを利用したり、処理結果をメモリへ保存したりする必要があります。

Loadは、メモリ上のデータをCPU内部へ読み込む処理です。一方、Storeは、CPUで処理した結果をメモリへ書き込む処理です。

例えば、変数の値を読み込んで計算する場合はLoadが行われ、計算結果をメモリへ保存する場合はStoreが行われます。

なお、LoadやStoreはすべての命令で実行されるわけではなく、メモリアクセスが必要な命令でのみ実行されます。

※Write Backについて

CPUの資料によっては、「Write Back」という処理が命令実行サイクルに含まれることがあります。

Write Backとは、演算結果をレジスタへ書き戻す処理を指します。RISCプロセッサなどでは独立したステージとして扱われることが多い一方、CPU全体の動作を説明する際には、ExecuteやLoad / Storeの一部として説明されることもあります。

本記事では、Linuxシステムを理解するうえで重要となるメモリアクセスに着目するため、Load / Storeを中心に説明しています。

CPUはこのサイクルを繰り返している

CPUは、この命令実行サイクルを絶えず繰り返しています。

例えば、lsコマンドを実行した場合でも、CPUは「ディレクトリ一覧を表示する」という処理を一度に実行しているわけではありません。命令を取得し、解析し、実行し、必要に応じてメモリへアクセスするというサイクルを何度も繰り返すことで、最終的にディレクトリ一覧が表示されます。

ApacheやPostgreSQLなどの大規模なソフトウェアも同様です。どれほど複雑なプログラムであっても、CPUが行っている基本的な動作は変わりません。

CPUの高い処理性能は、この命令実行サイクルを非常に高速に繰り返すことで実現されているのです。

レジスタの役割

CPUが命令を実行する際、データを毎回メモリから読み込んでいては時間がかかってしまいます。そこで利用されるのが「レジスタ」です。

レジスタとは、CPU内部にある非常に高速な記憶領域です。CPUは、命令を実行する際に必要なデータやアドレスをレジスタへ格納し、それらを利用しながら処理を進めています。

例えば、「変数Aと変数Bを加算する」という命令を実行する場合、CPUはメモリ上のデータを直接計算するのではなく、一度レジスタへ読み込んでから演算を行います。そのため、レジスタはCPUの処理性能を支える重要な役割を担っています。

レジスタにはさまざまな種類がありますが、ここでは代表的なものを紹介します。

汎用レジスタ(General Purpose Register)

汎用レジスタは、計算に使用するデータやアドレスを一時的に保存するためのレジスタです。

例えば、加算命令では2つの値を汎用レジスタへ読み込み、ALUがその値を使って演算を行います。演算結果も汎用レジスタへ保存され、その後の命令で利用されます。

プログラムカウンタ(Program Counter)

プログラムカウンタは、次に実行する命令のアドレスを保持するレジスタです。

CPUはプログラムカウンタが示すアドレスから命令を取得し、実行が終わると次の命令を指すように値を更新します。また、条件分岐やループ処理では、プログラムカウンタの値を書き換えることで、別の場所にある命令の実行へ移ります。

スタックポインタ(Stack Pointer)

スタックポインタは、スタック領域の先頭位置を保持するレジスタです。

関数の呼び出しや復帰、ローカル変数の保存などではスタック領域が利用されます。CPUはスタックポインタを更新しながら、必要なデータの保存や復元を行っています。

フラグレジスタ(Flags Register)

フラグレジスタは、直前の演算結果を記録するレジスタです。

例えば、「計算結果が0だった」「比較した結果、一方が大きかった」といった情報が保存されます。

CPUはこの情報を利用して、if文やfor文などの条件分岐命令を実行します。

レジスタはCPUの処理を支える重要な存在

レジスタは容量こそ非常に小さいものの、CPU内部にあるためメモリよりも高速にアクセスできます。

CPUは命令を実行するたびにレジスタを利用しながら処理を進めており、プログラムカウンタやスタックポインタ、フラグレジスタなど、それぞれのレジスタが役割を分担することで、プログラムを効率よく実行しています。

次の章では、CPUが演算や比較を実際に行う「ALU(算術論理演算装置)」について見ていきます。

ALUと制御装置

ここまで説明したように、CPUは命令を取得し、解析し、実行するという一連の処理を繰り返しています。

この処理の中心となるのが、「制御装置(Control Unit)」と「ALU(Arithmetic Logic Unit:算術論理演算装置)」です。

制御装置の役割

制御装置は、CPU全体の動作を管理する役割を担っています。CPUが命令を取得すると、制御装置はその内容を解析し、「どのような処理を行うのか」を判断します。

例えば、加算命令であればALUへ演算を指示し、メモリからデータを読み込む命令であればメモリアクセスを制御します。また、条件分岐命令であれば、プログラムカウンタを更新して次に実行する命令を変更します。

このように、制御装置はCPU内部の各回路へ指示を出し、命令が正しい順序で実行されるよう管理しています。

ALUの役割

ALUは、CPUの中で演算や比較を行う回路です。例えば、次のような処理を担当しています。

・加算や減算
・大小比較
・AND、OR、XORなどの論理演算
・ビットシフト

CPUが「1 + 2を計算する」「2つの値を比較する」といった命令を実行する際は、ALUが実際の計算を行います。

制御装置とALUは連携して動作する

制御装置とALUは、それぞれ独立して動作するわけではありません。例えば、「AとBを加算する」という命令を実行する場合は、まず制御装置が命令を解析し、ALUへ加算処理を指示します。ALUが演算を行うと、その結果はレジスタへ保存され、次の命令で利用されます。

また、「AとBを比較する」という命令では、ALUが比較を行い、その結果をフラグレジスタへ記録します。制御装置はその結果を参照し、必要に応じて条件分岐を実行します。

このように、制御装置が処理全体を管理し、ALUが実際の演算を担当することで、CPUはさまざまな命令を実行しています。

CPU内部では役割分担によって高速な処理を実現している

CPUは一つの装置に見えますが、その内部では制御装置やALU、レジスタなどが役割を分担しながら連携して動作しています。

これらの回路が高速に協調動作することで、CPUは1秒間に膨大な数の命令を実行し、Linux上で動作するさまざまなプログラムを支えているのです。

まとめ

CPUは、プログラムを構成する命令を取得し、その内容を解析し、実行する処理を繰り返すことでプログラムを動かしています。

本記事では、CPUが命令を実行する基本的な流れとして、Fetch、Decode、Execute、Load / Storeの役割を紹介しました。

また、命令の実行には、CPU内部のさまざまな構成要素が関わっています。

  • レジスタは、演算に使用する値やアドレスなどを一時的に保持する
  • プログラムカウンタは、次に実行する命令の位置を示す
  • 制御装置は、命令を解析してCPU内部の各回路へ指示を出す
  • ALUは、加算や比較、論理演算などを実行する

このように、CPU内部では複数の機能が連携しながら、プログラムの命令を処理しています。

実際のCPUでは、パイプラインやキャッシュ、アウト・オブ・オーダー実行など、さらに複雑な仕組みを利用して処理を高速化しています。しかし、CPUの基本的な動作を理解するうえでは、まず「命令を取得し、解析し、実行する」という流れを押さえておくことが重要です。

LinuxにおけるCPUの仕組みについては、以下の書籍にまとめています。興味がありましたら、ぜひ読んでみてください。

Linux入門・図解

LinuxのCPUの仕組みを
図解で理解したい方へ

命令実行・プロセス・スケジューラ・割り込み・ キャッシュ・マルチコアなど、 LinuxにおけるCPUの仕組みを図解を中心にわかりやすく解説しています。

Kindleで書籍を見る →

コメント