コンピュータ・アーキテクチャ

CPU・メモリというコア装置が計算をしていて、そこに情報が入出力される

コンピュータの仕組みが良く分からない人は、CPUとメモリという「コア装置」があって、そのコア装置にハードディスクなどから「情報」が入出力として送受信され、その上でマウス、キーボード、ディスプレイが「イベント」を与えると理解すると良いでしょう。
ここで重要なのは、コア装置よりも入出力として与えられる「情報」です。この情報の中に、全てのプログラム、全てのデータ、全ての設定内容、そして全ての読み書きされる内容が保管されているからです。
コア装置と情報がやりとりしながらイベントを送り合う、そんな機械がパソコンです。情報の中にプログラムがあり、そのプログラム情報の中の中核部分(カーネル)によって情報の「形態」や「形式」が決められ、それらが個別のプログラムとなって「カーネルとプログラムが協調して動作する」と理解すると良いでしょう。

コンピュータは計算とメモリ

コンピュータの基本は、計算とメモリです。それも、CPU自体は足し算(あるいは四則演算)しかできません。三角関数のような基本的な数学の関数でさえ、プログラミングを用いて行います。
また、メモリといっても、処理速度の速さから言って、CPUのレジスタ、メモリ(キャッシュメモリと主メモリ)、ハードディスクなどの大容量記録装置があります。
このようなパソコンですが、プログラミングでやっていることはまさに、計算とメモリです。
「aに3を入れろ」「bに10を入れろ」「a+bを計算してそれをcに入れろ」という、まさに電卓をやっているだけなのが、ハードウェアとしてのコンピュータです。あとは、そのaやbやcがどこにあって、どこから読み出し、どこへと転送するか、ということをやっているだけが、ストレージやネットワーク、あるいはデータベースです。
プログラミングとは、計算を行うための手段であると同時に、計算を何のために行うかという目的です。繰り返したり条件分岐したり、といったことは、ハードウェアを上手く活用して、抽象化されたプログラムをいかに作るのか、ということの「応用の極め」にすぎません。
ですが、一度作ったプログラムは何度でも使えます。変更する必要なく再利用できることが理想であるため、バージョンアップという発想は、正しいプログラムでは必要のない考え方です。ですが、プログラムを作るのもプログラマという人間の行いであり、プログラムは間違えなくても人間が間違えてしまいます。人間の間違いを後になって修正する、それがバージョンアップです。

どこまでをプログラムの内部に書き、どこからを外部に出す(実行時に決める)か

また、プログラムとは永遠のデータ処理のようなもので、プログラムの内部で保持される定数や変数があると同時に、外部からそうしたデータを読み込むプログラムを書くこともできます。
このため、「どこまでをプログラムの内部に書き、どこまでを外部に出すか」ということが重要になってきます。
プログラムの作成時に想定できる範囲が全てなら、それで良いでしょう。ですが、プログラムが使われるうちにデータや設定を変更し、外部に書き出して実行時に読み込むことで、あらかじめ決められているファイル形式で保存したデータを、読み込んだり書き出したりすることができます。
また、プログラムの外部として動的に処理するのはデータや設定ファイルだけではなく、ユーザーのフォームの入力や、どんな行動をしたかというログや、あるいは、プログラムの通信対象(たとえばサーバーに存在するWebサイトのデータなど)なども、プログラムの外部に出すべきデータとなります。
よって、単純に人工知能を学習するだけではなく、Webサイトの更新をチェックする「アンテナ」の更新日時や、ツイッターに自動的に文章を投稿する「ボット」などの定型文章(テンプレート)なども、プログラムのある意味でのデータと言えます。こうしたデータは、大規模で再利用可能なプログラムでは必ず必要になります。プログラムが必要とすることで、プログラムの中に書かれるものは、少ししかありません。ほとんどが、外部データを操作して読み書きするだけです。

コンパイラを上手く使うこと

そういうわけで、プログラム自身である「情報」をどのように組み立てるか、ということが、コンピュータを使う上で重要になってきます。
この時、機械語で直接プログラムを書くと、そのコンピュータのCPUでしか、プログラムが動かなくなります。この際にC言語などの「高級言語」を使うことで、UNIXがそうだったように、さまざまなアーキテクチャにプログラム(OS)を移植することができます。
高級言語は、そのままではCPUでは動きません。高級言語(C)をCPUの機械語Intel x86)に翻訳するプログラム、すなわち「コンパイラ」が必要となります。
昔は、コンパイラは「限りあるコンピュータ資源の無駄」であると言われましたが、今ではほとんどのOSがコンパイラを使って作られています。また、OSの上でJavaのようなミドルウェアを実行することで、さらに高水準の高級言語を使うことも増えています。今では、CのプログラマよりもJavaプログラマの方がはるかに多いです。

OSとカーネル

また、コンピュータのハードウェアを支配しているのは、OSと呼ばれるプログラムです。OSの中で、中核に当たる部分をカーネルと言います。
カーネルのやっていることは、ハードウェアから見れば、常にカーネルマルチタスクでプログラムを実行し、そのカーネルの内部で「さまざまな内部情報」を構築して、その中にデバイスの情報やそれを処理するデバイスドライバ、あるいはFATやNTFSのハードディスクを操作するために必要なファイルシステムモジュールを読み込み、その中で「上手くハードウェアのイベントに応じてやり取りすること」で、カーネルは成り立っています。
プログラムの実行が要求されると、カーネルはプログラムのインスタンスである「プロセス」を生成します。プロセスの論理アドレス空間は物理アドレス空間に変換され、それぞれのプログラムが別々のアドレス空間を持つようにします。また、別のプロセスの実行が要求されると、カーネルレジスタの内容をレジスタ外に退避して、そのプロセスを停止させ、別のプロセスを同時に実行し、スケジューラのクォンタム(プロセスを切り替える時間)と優先順位に基づいて次々にプログラムを実行させます。また、ユーザーモードカーネルモードはカーネルの中で明確に区別され、ユーザーランドのプログラムがカーネルのメモリ空間にアクセスすることはできません。

コンピュータは二進数の計算以外、何も出来ない

コンピュータは、内部の数値データを全て二進数で保持している。言ってしまえば、人間がどのような計算をさせても、コンピュータの方はただ二進数を論理回路で計算しているだけである。
コンピュータは、二進数を計算して、CPUで用意されたレジスタやメモリへ入出力を行う以外、何も出来ない。それ以上のことは、人間が「プログラミング」しなければならない。これがコンピュータの肝である。コンピュータは、そもそもプログラミングしなければ何も出来ない。最初から、二進数以外のことはプログラミングすることを意図して、コンピュータという機械は作られているのである。
CPUは順に命令を実行するほか、実行の順番を制御する「ジャンプ命令」がある。これはC言語のgotoに相当する。現代的なOS(カーネル)やコンパイラは、こうした順番の制御と二進数の計算とハードウェア的なやりとりによって作られている。モニターに映る内容はウィンドウシステムが描画している。
オープンソースの真価とは、こうしたプログラマブルな環境をユーザーに自由に解放することにある、と言っても良いだろう。確かにWindowsの方が会社が作っていて信頼できるかもしれないが、Windowsソースコードは非公開である。Linuxは今のところWindowsほどに優れたGUIは提供していないが、ソースコード全てがオープンなライセンスで公開されている。それは、「自分で研究し、改造し、コピーし、再配布する自由」があることを意味する。フリーソフトウェアと呼ばれるこれらの自由は、「CPUの上にある、全てのプログラム可能な環境を与える」という意味で、コンピュータ本来の自由を取り戻すということを意味しているのである。
Windowsのようなクローズドなソフトウェアを独占的(プロプライエタリ―)なソフトウェアというのは、著作権を利用してみんなのものを独占しているからである。言ってしまえば、「ドラえもんの絵しか描けない絵の具」に近い。それしか使ったことのない人間は、自分でもっと高度なOSをハッキングする楽しみを知らないのである。金儲けを取るか、自由を取るか、それがGNUの正義なのである。
ただ、そんなにGNUがかっこいいかというと、そうでもない。GNUは、自分の所有するコンピュータの自由という概念を持ち出して、「Microsoftが全てのコンピュータを勝手に独占している」と言うが、私有権をもっとも否定するのはGNUである。そういうところがおかしいから、Red Hatのようにみんなを騙して儲ける企業が出てくる。そもそもが革命は悪である。それは、言っておかなければならないだろう。

コンパイル時の設定と設定ファイルによる動的な実行内容の変化

僕は、OSとしての設計には、「コンパイル」と「アーキテクチャ」があると思う。
まず、アプリケーション・プログラムは、コンパイルした時に動作が変わる部分と、実行時に動作が変わる部分がある。
たとえば、コンパイルオプションと設定ファイル。
コンパイルオプションで指定した内容は、再コンパイルした時にしか変わらない。アプリケーションの動作はコンパイルした時に静的に決定される。
この場合、設定内容を変えるためには、再コンパイルする必要がある。
それと対照的に、実行オプションや設定ファイルで設定する方法なら、実行時に動的に決めることが出来る。
これにより、多様なプログラムの動作が期待出来る。
これと同じことが、テキストファイルを入出力するプログラムや、モジュールを動的に読み込むプログラムについても言える。
また、スクリプト言語のようなプログラムでは、スクリプトがそのまま動的に読み込むテキストファイルとして扱われる。
簡単に言ってしまえば、コンパイルする時に設定を決定するプログラムと、動的に設定を実行時に読み込むプログラムがある。
後日注記:つまり、コードを変えなくても、設定ファイルやオプションを変えることで、プログラムの動作を変えることができる。さらに言えば、これをシェルスクリプトのようなインタープリタのプログラムにすることで、単なる設定ファイルではなく、プログラムの実行によって設定を行うことができる。すなわち、「プログラムによってプログラムの動作を変えられる」。UNIXにはそうした設計思想がある。
後日注記:設定ファイルには、UNIX由来のプログラムではシェルから設定できる環境変数や空白やタブによって区切られたプレーンテキストの設定ファイルを用いるほか、JavaPHPではXMLやINIによる設定ファイルも見られる。もちろん、GNOMEKDEでは設定はGUIで行うことができる。もともとコマンドラインのプログラムであっても、GUIによる設定ユーティリティが用意されている場合もある。プログラムによって設定の種類は千差万別である。

CPUアーキテクチャ

次に、CPUアーキテクチャへの対応がある。
Windowsなどでは、x86/x86_64にしかOSは対応しないが、UNIXC言語で書かれたポータブルなOSであるため、移植性が高いと言われる。
これは、プログラム自体をC言語と言う、さまざまなバイナリフォーマットに対応出来るいくらか高水準な言語で書くことによって、移植性を高める、と言う発想だ。
カーネルの低レベルな部分で、最小限アーキテクチャ別のCとアセンブリ言語でのプログラムを書いて、高レベルな部分は全て共通のC言語で書く。
GCCと言うさまざまなアーキテクチャのバイナリが吐けるコンパイラを使うことで、さまざまなプログラムを違うCPUのLinuxで使うことが出来る。

パッケージ管理

また、パッケージ管理と言う意味では、「依存性の解決」と「自動更新」を行うことが出来る。
低レベルな部分では、パッケージ管理はOSにインストールされている全てのパッケージの依存関係の解決を行うことが出来る。
これにより、OSが壊れることを防ぎながら、簡単にUNIX上のユーザーランド・アプリケーションの無数のパッケージを管理することが出来る。
Linuxが管理しやすいのは、パッケージ管理のおかげでもある。
パッケージ管理があるおかげで、不要なシステムの破壊を防ぎ、簡単にディストリビューションと言う「OS全体」を作って配布することが出来る。
先に言う、コンパイルと設定ファイルの考え方のようなものが、パッケージ管理システムにおいても良く似た形態で見ることが出来る。
また、apt-getやyum/dnfに見られるように、パッケージは自動更新され、常に最新のパッケージリストをダウンロードし、最新のパッケージに更新することが出来る。
僕は、ここでもGentoo Linuxに注目したい。Gentoo Linuxでは、Portageと言う独自のパッケージ管理を行っている。
使うコマンドはemerge、パッケージの記法はebuildである。常に最新のebuildをダウンロードしておき、emergeを使ってパッケージをインストールする。
だが、Gentoo Linuxの特徴は、「全てのパッケージがソースベース」であることだ。emergeは、ほとんど全てのパッケージをソースコードからコンパイルする。
ソースコードからコンパイルすることで、先に言ったようなコンパイルにおける設定をmake.confで変えることが出来る。
また、USEフラグを使うことで、たとえばGTK+サポートをONにするかOFFにするかのような、パッケージのコンパイル依存関係を変えることが出来る。
そして、基本的にパッケージは設定ファイルを手動で設定する。rc-updateのようなランレベルとデーモンの設定のようなものはある。
ここで、DebianRed Hatのようなシステムに戻ると、DebianRed Hatでも、ソースパッケージとバイナリパッケージと言うものがある。
そして、それはGentoo Linuxと同じであり、そこまで考えると、ソースコードUNIXシステムの関係が分かりやすく、全貌を掴むことが出来る。

カーネルの手動コンパイル

最後に、Linuxカーネルである。Linuxカーネルは、手動でコンパイルすることで、さまざまなオプション機能を有効にしたり、無効にしたりすることが出来る。

入出力とパイプ

入出力とパイプによって、コマンドを関係で扱うことが出来る。

$ find / | less

後日注記:UNIXには、「機能は単一のプログラム単体からではなく、複数のプログラムの関係から生まれる」という哲学がある。上の例では、findで検索したファイル一覧をlessというページャに送って、lessを使ってスクロールしてfindの内容を見ている。たとえば、findではなくgrepの結果であっても、コマンドの入力を変えるだけで、lessを使ってあらゆるコマンドの出力結果を簡単に閲覧することができる。

シェルとX11

シェルは、新しいプログラムを実行するためのプログラム。
コマンド入力を受け付けて、PATHからバイナリを探し、子プロセスを起動出来る。
シェルの初期化ファイルを使うことで、シェルの起動時にシェルの初期化をすることが出来る。
X11でも、初期化ファイルを使うことで、Xサーバーの設定をしたり、最初に起動しておくアプリケーションの起動が出来る。
コマンドプログラムとX11GUIプログラムは大きく異なっているが、XクライアントはXサーバーに対する指令・通信を行う、Xプロトコルを使ったアプリケーションである、と理解すると、理解しやすい。
また、CUIとは異なり、プログラムは階層化され、ツールキット・ライブラリであるGTK+やQtからXlibを操作する形になる。
これは、命令である、と言うよりは、どのような仕組みを用いてX11を操作するか、と言う、GUIにおけるRailsのようなフレームワークである、と理解すれば良い。

パーミッションとユーザー

パーミッションは、システムを守る仕組みで、Windowsのような管理者権限を標準で使うOSとは違い、Linuxにはファイルやディレクトリごとにユーザーの権限が決まっている。
ユーザーに見せられない機密データの閲覧や、実行してはならない実行ファイルの実行に、制限をかけることが出来る。
rootユーザーだけが、全ファイルの全データにアクセス出来る。
これは、Linuxのセキュリティに大きく貢献しているが、その反面、サーバーなどでのファイル管理において、ややこしい仕組みとなっている。
だが、分かってしまえば簡単だ。

SELinux/AppArmor

もっとセキュリティを高めたい場合は、SELinuxのようなセキュリティ強化の仕組みを使うことも出来る。HTTP、FTPなどのプロセスごとにアクセス制限をかけ、rootユーザーを含む全ユーザーへのアクセス制限をかけ、rootユーザーへの権限の集中をもっと分散させることが出来る。
AppArmorと言うSUSEなどの方式もある。
SELinuxFedoraRHELなどでは標準で有効になっている。Fedoraは、そういう、Red Hatのやりたいように作っている、と言う側面がある。

関数

最後に、関数のレベルでの抽象化がある。
関数は自動の実行処理であり、引数に何かの値を設定することで、実行内容が変わる。
僕は、関数の本質とは、再利用性にあると思う。ライブラリのように、既に書かれている関数を再利用して、その利用を書く。
だから、正しいプログラミング言語とは、Rubyのように、継承やブロックによって、既に書かれているコードの内容を上書き出来るようでなければならない。
一度書かれたコードを無駄にせず、再利用と共有を行うために、関数は設計されなければならない。
ある意味、オブジェクト指向も、そのように考えれば、「クラスの再利用」であると理解することが出来る。

Linuxカーネルは常に動く

パソコンはOSが無ければただの箱と言うが、言ってしまえば、Linuxをインストールしたパソコンでは、常にLinuxカーネルが動いている。
常に動きながら、たくさんのプログラムを「自身の上で実行する」、それがLinuxカーネルだ。
GNOMEX11を使い、X11カーネルを使い、カーネルはハードウェアを使う。そして、最終的にはCPUとメモリが動いている。
それを階層構造で示した時に、メインとなるのは、Linuxカーネルだ。
Androidスマホを実行する場合でも、Linuxのサーバーを利用する場合でも、その環境で常に動いているのは、Linuxカーネルだ。
そう考えると、このLinuxと言うカーネルオープンソースで技術的に公開されていて、自由に参加出来て、改良やコピー・再配布も自由である、と言うことは、この上なく良いことのように感じられる。それをやりたかったのが、GNU Projectのストールマンだと思う。

カーネルとは何か

カーネルとは、ハードウェアとソフトウェアの橋渡しをするものであり、システムの中で常に実行されながら、特定のプログラムを適切に実行し、それぞれのプログラムにリソース(利用出来るコンピュータ資源)を割り当てると同時に、ソフトウェアからの要求に応じてハードウェアへのアクセス手段を与え、サーバーやアプリケーションのようなプログラムがプログラムとして実行が可能になるための環境を与えるものだ。
OSの中で、カーネルとはある意味下層部分と上層部分を繋げるノリのようなものであると同時に、ソフトウェアの仕掛け全体の中核に位置しながら、全てをきちんと成り立たせる、オーケストラで言えばコンダクター(指揮者)のようなものである。だが、主役は基本的にアプリケーションであり、それらが楽器である。
OSの中でカーネルは一部分にすぎず、コマンドを入力するシェルや、それぞれの仕事を、UNIXにおいてはそれぞれが単機能に行う、各種のコマンドプログラム、システムに常駐するデーモン、X11のようなハードウェアをグラフィカルな操作インターフェースに変える特殊なアプリケーション、そしてコンパイラやパッケージ管理システムがなければならない。GNU/LinuxにおいてはカーネルLinuxだが、OSとしてはGNUのさまざまなツールを使うため、GNU/Linuxと言う呼び名が適切であると(少なくともFSFからは)言われている。
だが、技術的に見ると、デバイスドライバやメモリマネージメントやCPUの並列処理といったハードウェアの操作と、C言語ライブラリとの関係性、そしてプロセスの作成とリソースの割り当てやタイムスケジューリング、OSの下層システムとして必要なファイルシステムの実現やネットワーク接続(パケットやソケットなどの実現)が多い。ハードウェアをソフトウェアから適切に使うことが主目的であり、ネットワークやグラフィックスで専門的な用途を行うためには、カーネルを用いてハードウェアに接続する「ユーザーランド」が必要となる。これは、Apache HTTPDX11 Serverのようなシステムに必須の専門プログラムから、専門的アプリケーションに至る広汎なソフトウェア集団となる。
カーネルの特殊な用途はネットワークであり、カーネル以外での特殊の用途はグラフィックスであると言える。両者はカーネルとユーザーランドのどちらにも区分されにくい領域であり、WindowsのようなOSではグラフィックスはOSに統合されている。特に、X11はとても巨大であり、X11からKDE/GNOMEに至るGUIデスクトップ環境の領域は、「GUI時代の第二のカーネル(サブカーネル)」と呼んでも良いかもしれない。(サブカーネルは、僕の作った造語にすぎません。)

低水準レイヤーと高水準レイヤー

Linuxの大きな特徴として、低水準レイヤーと高水準レイヤーがある。
UNIXでは、カーネルの上でユーザーランドのプログラムが動く、という比較的単純なレイヤー分けがなされているが、それが大きく様々なプログラムが発達したLinuxでは、もっと複雑になる。
たとえば、X11では、X Window Systemがまず最下部にあり、その上でGTKやQtなどのツールキットがあって、その上にGNOMEKDEのデスクトップ環境がある。
また、プログラミング言語では、Java仮想マシンPerlPythonRubyインタープリタがあって、その上にDjangoRailsなどのフレームワークがある。
ほかにも、ApacheなどのWebサーバや、TomcatJBossなどのミドルウェアMySQLPostgreSQLなどのデータベース管理システムが、低水準レイヤーとして存在する。
こうした「高水準・低水準」という考え方がLinuxには多く見られる。自分でプログラムを開発する場合には、自分のプログラムよりも低水準のレイヤーがどういう技術仕様で実装されているかやAPIなどに習熟しておく必要がある。

CPUとは

CPUとは

CPUは、コンピュータの中枢であり、全ての計算と実行を行うハードウェア装置。
加減乗除やシフト演算などの基本的な演算・計算ができる。
また、レジスタと呼ばれる高速の記憶領域を何個か持っていて、計算の時に使う。レジスタにはAX, BX, CX, DXのように名前がついている。
記憶を保管するためのメモリや、バスなどを通じて周辺機器ともつながっていて、メモリの中の記憶にアクセスしたり、メモリの中に記憶を保存したり、周辺機器に対するI/O操作を行うことができる。
また、実行中のプログラムに関しては、現在の命令の位置を任意の位置にジャンプする「ジャンプ命令」を使うことができる。
命令は機械が解釈できる機械語の命令で行うが、このそれぞれの命令を人間が理解しやすいように言葉(ニーモニック)に置き換えたアセンブリ言語というものがあり、機械語のプログラムを作る時はこうしたアセンブリ言語を用いる。

現在のコンピュータではCPUを意識しない

現在のコンピュータでは、CPUやメモリのような低レベルなハードウェアを直接触ることは少ない。OSが仮想CPUや仮想メモリの機構を用意しているため、プログラムを動かす時は原則プログラムはOSの管理下に置かれる。プログラムはマルチタスクを実現するコンテキストスイッチ(プログラムを並列で動かすためのOSの機構)やスケジューリング、あるいはプログラムが利用する変数などの記憶が物理アドレスの中のどの場所に保管されるのか、などといったことを意識することなく、独自の論理アドレス空間を用いて、並列でプログラムを動かすことができる。
だが、そうしたOSの低レベルなカーネル処理は、全て現実のCPUとメモリの上に実現されている。OSのコードを実行する場合でも、実際に動いているのはCPUであり、プログラムを動かす時も、実際に計算しているのはCPUである。CPUによっては「カーネルモード」と「ユーザーモード」が分かれていることがあり、カーネルはそうしたCPUの機能を用いて作られている。
また、機械語アセンブリ言語でプログラムを書くこと自体、現在のコンピュータでは稀であり、多くの場合C言語のようなプログラミング言語を用いて、それを機械語に翻訳するコンパイラを用いて機械語を生成する。手動でハンドアセンブルするよりは遅く非効率的になるかもしれないが、機械語アセンブリ言語で書くよりもはるかに人間に分かりやすく、書きやすく、読みやすく、また移植性も高い。多くのコンパイラで「最適化」が行われており、ハンドアセンブル並みに高速かつ効率的な機械語コンパイラが生成することも少なくない。
もっとも高速なのはFORTRANC/C++だが、遅くても生産性が高い他の言語を使うことで、さらに書きやすく、読みやすく、作りやすく、また移植性が高くなる。また、オブジェクト指向言語のような高水準の機能のある言語を用いて、より大規模かつ簡単な開発が可能となる。
ただし、現在のコンピュータではCPUを意識しないからといって、CPUが無関係になるわけではない。実際のところコンピュータの中枢として動いているのはCPUであり、そのため、CPUのクロックやコア数が上がるとコンピュータのスピードが速くなる。
また、OS開発者にとっては、CPUの処理は知っておかなければならない。近代のOSでは、CPUを有効に使うために、特にマルチプロセッサ環境やクラスタシステムなどにおいて、複数のプロセッサ(マルチプロセッサ)をいかに効率よく利用するか、などといった課題がある。
後日注記:本当のことを言うと、機械語アセンブラでの開発をしていると、CPUの命令体系にじかに触れる機会は結構多い。特に、x86アセンブラをやろうとすると、Intel CPUの演算命令(ニーモニック)を覚える必要があるし、セグメント、レジスタ、アドレスなどの概念を知っておかなければならない。C/C++言語を用いて開発をする上でも、メモリアドレスを格納する変数であるポインタに触る機会はわりと多い。また、2進数や16進数の考え方は、デジタル処理を行う上で多用される。CPUの仕組みを知っておいて、損になることはなく、ためになる知識がそのままスキルアップになる。

CPUと人間の頭脳の比較

CPUはコンピュータの「頭脳」だが、人間の頭脳との違いは、プログラムを書かなければ動かない、ということ。機械語の命令に変換(コンパイル)することのできるプログラム言語を用いて、プログラムを書かなければ動かない。自らプログラムを考える、ということをCPUはしない。
その代わり、いったんプログラムが与えられてしまえば、それを計算するのは高速であり、人間よりはるかに速く、人間のするのと同じ計算をすることができる。また、CPUは柔軟で、どんなプログラムであっても、休まず、何度でも、ひたすらその通り計算することができる。
その代わり、プログラムに書いたことしか理解できないため、プログラムに書くことのできない曖昧で厳密性を欠いたことについては理解することができない。必ず、厳密な命令が必要であり、そしてどんなに細かすぎるようであっても、機械が理解することができるように、極めて細かく、厳密に、間違いなく丁寧に書かなければならない。
プログラムという方式の利点としては、「抽象性」が挙げられる。どんなに関数やサブルーチンを繰り返しても、その通りきちんと実行する。変数を用いることで、同じ言葉を繰り返さなくても、ひとつのサブルーチンでどんな場合にでも対応できる。人間であれば学習してひとつひとつ覚えなければならないことを、その計算式に基づいて簡単に計算できる。そのため、人間が頭で考えるには不可能な膨大な情報を瞬時かつ自動的に計算してくれる。そのため、形式的・手続き的な処理を行うのであれば、マウスやキーボードで行うより、プログラムを書いて行った方が楽である。OSのソースコードなどはとても膨大であるが、CPUは文句を言わずOSのコードを簡単に計算する。

CPUの基本原理

CPUは、基本的に二進数の0と1の組み合わせと桁上がりで足し算と引き算を行います。
技術の進歩とともに、計算出来る量が増えてきました。ですが、これは計算する人数が増えたのであって、決して計算する頭が賢くなったわけではありません。だから、基本的に昔のプロセッサと原理は変わらないのです。

命令のステージと実行手順

プロセッサは命令を実行するが、一言で「命令を実行する」といっても、命令(プログラム)自体は記憶装置にあるし、計算命令などを実行する時は、その前にデータを用意する必要がある。
このように、一つの命令の実行であってもやるべきことはいくつもあり、それぞれをステージと呼ぶ。
代表的なものは次の5つのステージ。

  • 命令の取り出し
  • 命令の解読
    • 制御装置で命令を解読して、何を行うかを知る。
    • たとえば、「ADD A, B」という命令なら、「AとBを加算してその結果をAに格納する」ということを理解する。
  • データの取り出し
    • 記憶装置(またはキャッシュメモリ)から、命令の実行に使うデータを演算装置に取り出す。
    • 前述の「ADD A, B」なら、AとBに対応するデータを取得する。
  • 命令の実行
    • 演算装置で命令を実行する。実際に計算などを行うステージ。
  • 結果の格納

ひとつの命令を実行するのに、このように複数のステージが必要。また、プロセッサの進化とともに、この処理を並列で動かすことで高速化を実現している。

パイプライン

CPUのIF(命令読み出し) - ID(命令解読) - AC(アドレス計算) - AT(アドレス変換) - OF(オペランド読み出し) - EX(演算実行) - MW(メモリ書き込み)の処理を逐次実行するのではなく、クロックごとに独立して動作できるようにした上で、次々に命令を実行させる方式をパイプライン処理という。
パイプラインがない状態では、CPUの命令実行は以下のようになる。

時間 1 2 3 4 5 6 7 8 9 10 11 12 13 14
命令1 IF ID AC AT OF EX MW
命令2 IF ID AC AT OF EX MW

パイプラインがある状態では、CPUの命令実行は以下のようになる。

時間 1 2 3 4 5 6 7 8 9 10 11 12 13 14
命令1 IF ID AC AT OF EX MW
命令2 IF ID AC AT OF EX MW
命令3 IF ID AC AT OF EX MW
命令4 IF ID AC AT OF EX MW
命令5 IF ID AC AT OF EX MW
命令6 IF ID AC AT OF EX MW
命令7 IF ID AC AT OF EX MW
命令8 IF ID AC AT OF EX MW

CISCRISC

CISCは、複雑な命令セットをもつ処理装置。処理装置内に記憶されたマイクロプログラムによって命令を実行する。
RISCは、命令数を減らし単純な命令セットをもつ処理装置。
RISCでは、単に命令数を減らすだけでなく、各命令をできるだけ単純なものに限定し、マイクロプログラムを用いることなくワイヤードロジックだけを用いて命令を実行する。ほとんどの命令の実行時間が等しくなり、パイプライン処理の実行効率が著しく高くなる。

ソフトウェアやOSで仮想化しても動いているのはCPU

CPUについて言えることとして、「ソフトウェアやOSでいくら仮想化しても、動いているのはCPUである」ということ。
マルチタスクや並列性のような技術は確かにソフトウェアでCPUを仮想化して実現しますが、それでも、パソコンで中央処理をしているのは、現実にあるCPU自身に他なりません。
Intel 4004以降のマイクロプロセッサでは、CPUやハードウェアに機械的回路を作って計算するのではなく、ソフトウェアで「プログラミング」を行って計算を実現します。(ちなみに、Intel 4004インテルとともに開発したのは、嶋正利という日本人エンジニアです。)
そのため、プログラミングは必ず必要ですが、それでも、動いているのはCPUであり、それは機械語の命令を読み出し、解読し、データを取得し、計算し、実行結果を保管する、一連のステージを実行できるCPUプロセッサなのです。

半導体メモリ(主記憶装置)

メモリとは

メモリは、コンピュータにおいて情報の記憶を行う装置。
大きく分けて、読み書きが自由なRAMと、読み出し専用のROMがある。
一般的なRAMには、「電源の供給がなくなると内容が消えてしまう」という特徴がある。
また、RAMには、一定時間経つとデータが消失するDRAM(Dynamic RAM)と、電源を切らない限り内容を保持するSRAM(Static RAM)がある。
DRAMは低速だが安価で大容量なため、主記憶装置に使う。SRAMは高速だが高価で小容量なため、キャッシュメモリに使う。

メモリの特徴

メモリの特徴は、
・メモリの中の記憶情報の格納場所(番地・住所)を指すメモリアドレスを指定して読み書きする。x86アセンブリ言語ではmov [XX]を使う。
・CPUの計算に使われるレジスタより低速。
・電源を消しても内容を保持できるハードディスクなどの磁気ディスクより高速。

メモリの種類

RAMとROM

RAMはデータの読み書きが出来るメモリ。反対に読み込みしかできないメモリをROMという。

DRAM(主メモリ)

主記憶装置に用いられる。安価で容量が大きい一方、SRAMと比較すると低速。

SRAMキャッシュメモリ

キャッシュメモリに用いられる。主メモリよりもさらに高速。
キャッシュメモリは低容量で高価格だがスピードが高速なため、一度アクセスしたデータに何度も繰り返し頻繁にアクセスするための「キャッシュ」として用いられる。

キャッシュメモリのデータ更新方式

プロセッサがキャッシュメモリのデータを更新した場合、その内容をメモリに反映する必要がある。
しかし、メモリにアクセスするには時間がかかり、毎回アクセスしていると効率が悪くなる。

  • ライトスルー方式
    • プロセッサがキャッシュメモリに書込みを行った時、その内容を同時にメモリにも転送する。
    • 単位時間の処理量であるスループレットは悪くなるが、コヒーレンシ(データの一貫性)は保たれる。
  • ライトバック方式
    • プロセッサがキャッシュメモリに書き込んでも、すぐにはメモリに転送しない。
    • キャッシュメモリのデータがメモリに追い出されるなど、条件を満たした場合にのみメモリに書き込まれる。
    • スループレットは良くなるが、コヒーレンシは保たれないことがある。