コンピュータシステムはハードウェアとシステムソフトウェアから構成され、両者が連携してアプリケーションプログラムを実行します。
#include<stdio.h>int main(){ printf("hello world! C"); return 0;}プログラムhello.cのライフサイクル—>作成、実行、出力、終了を追跡します。
1 情報=ビット+コンテキスト
hello.cはソースプログラム、すなわちhelloプログラムの出発点であり、プログラマーが作成したテキストファイルです。これはバイト(8ビットを1組としたもの)で構成されています。
その大部分ではASCII規格が使用されます。プログラムはバイト列としてファイルに保存され、各バイトの整数値が1つの文字に対応します。ASCII文字だけで構成されたファイルをテキストファイル、それ以外のファイルをバイナリファイルと呼びます。
基本的な考え方:システム内のあらゆる情報は、一連のビットによって表現されます。異なるデータオブジェクトは、データのコンテキストによって区別されます。数値の機械表現は実際の値とは異なり、真の値に対する有限精度の近似値です。
2 プログラムは別のプログラムによって異なる形式へ変換される
高水準C言語—変換—>低水準の機械語命令—パッケージ化—>実行可能オブジェクトプログラム
gcc -o hello hello.c./hello
コンパイルシステム
コンパイルシステム:プリプロセッサ、コンパイラ、アセンブラ、リンカ
-
プリプロセス
プリプロセッサ(cpp)は、#で始まる指令に基づいて元のプログラムを変更し、ヘッダーファイルの内容をプログラムのテキストへ直接挿入します。
hello.c—cpp—>hello.i
-
コンパイル
コンパイラ(ccl)は、前の手順で生成された.iファイルを、アセンブリ言語プログラムを含む.sファイルへ変換します。
hello.i—ccl—>hello.s
-
アセンブル
アセンブラ(as)は.sファイルを機械語命令へ変換し、再配置可能オブジェクトプログラムとしてパッケージ化して、.o形式のバイナリファイルに保存します。
hello.s—as—>hello.o
-
リンク
リンカ(ld)は、現在の.oファイルと、呼び出されるライブラリ関数のコンパイル済みオブジェクトファイルを結合し、システムが実行できる実行可能オブジェクトファイルを生成します。
hello.o+printf.o—ld—>hello
3 コンパイルシステムを理解する利点
- プログラムの性能を最適化できる
- リンク時に発生するエラーを理解できる
- セキュリティ上の脆弱性を回避できる
4 プロセッサは命令を読み取り、解釈する
4.1 システムを構成するハードウェア

システムを構成するハードウェア
-
バス
システム全体を貫く電子的な経路の集合であり、情報を表すバイトを伝送します。一定長のバイトブロックであるword(ワード)を転送します。1ワードを構成するバイト数はシステムによって異なり、4バイト(32ビット)や8バイト(64ビット)など、システムの基本的なパラメータとなります。
システムバス、メモリバス、I/Oバス
-
I/Oデバイス
システムと外部との接続を担います。入力用のキーボードやマウス、出力用のディスプレイ、長期保存用のディスクなどが該当します。
I/Oデバイスは、コントローラまたはアダプタを介してI/Oバスに接続されます。
-
コントローラとアダプタの違い
実装形態が異なります。
コントローラ:I/Oデバイス自体、またはシステムのマザーボード上にあるチップセット。
アダプタ:マザーボードのスロットに挿入されるカード。
-
-
主記憶
プログラムと、プログラムが処理するデータを格納する一時記憶装置です。
物理的にはダイナミックランダムアクセスメモリ(DRAM)チップで構成されます。論理的には線形のバイト配列であり、各バイトには0から始まる一意のアドレスが割り当てられます。
-
プロセッサ
中央処理装置(CPU)は、主記憶に格納された命令を解釈するエンジンです。
その中心には、1ワード分の大きさを持つプログラムカウンタ(PC)という記憶装置があり、主記憶内の特定の機械語命令(のアドレス)を指します。電源が投入されると、プロセッサはPCが指している命令を繰り返し実行し、PCを更新して次の命令を実行します。
プロセッサは、命令セットアーキテクチャによって定められた命令操作モデルに従って動作します。このモデルでは命令が厳密な順序で実行されます。1つの命令を実行する際、CPUはPCが指すメモリ位置から命令を読み取り、命令内のビットを解釈し、命令が指示する単純な操作を実行した後、PCを更新して次の命令を指すようにします。
このような単純な操作の種類は多くありません。これらの操作は、主記憶、レジスタファイル[複数の1ワード長のレジスタで構成され、それぞれに一意の名前を持つ小さな記憶装置]、算術論理演算装置(ALU)[新しいデータ値やアドレス値を計算する装置]を中心に行われます。
- 単純な操作
-
ロード
主記憶から1バイトまたは1ワードをレジスタへコピーし、レジスタ内の元の内容を上書きします。
-
ストア
レジスタから1バイトまたは1ワードを主記憶内の特定の位置へコピーし、その位置にあった元の内容を上書きします。
-
演算
2つのレジスタの内容をALUへコピーし、ALUで2つのワードに対する算術演算を行い、その結果を特定のレジスタに格納して元の内容を上書きします。
-
ジャンプ
命令自体から1ワードを取り出してプログラムカウンタ(PC)へコピーし、PC内の元の値を上書きします。
-
形式上、プロセッサは命令セットアーキテクチャを単純に実装したものですが、実際にはプログラムの実行を高速化するために非常に複雑な仕組みが使用されています。そのため、プロセッサの命令セットアーキテクチャとマイクロアーキテクチャを区別する必要があります。命令セットアーキテクチャは各機械語命令がもたらす効果を記述し、マイクロアーキテクチャはプロセッサが実際にどのように実装されているかを記述します。
- 単純な操作
4.2 プログラムの実行
処理の流れ:
shellプログラムは命令を実行し、コマンドが入力されるのを待ちます。./helloを入力すると、shellプログラムは文字を1つずつレジスタへ読み込み、続いてメモリへ格納します。Enterキーを押すとコマンドが実行され、helloファイルがロードされ、そのコードとデータがディスクから主記憶へコピーされます。

helloコマンドの読み取り
ダイレクトメモリアクセス(DMA)を使用すると、データはプロセッサを経由せず、ディスクから主記憶へ直接転送できます。

ディスクから主記憶への実行可能ファイルのロード
続いてhelloプログラムのmainに含まれる機械語命令の実行が始まり、「hello world! C」という文字列の各バイトが主記憶からレジスタへ、さらにレジスタから表示装置へコピーされ、最終的に画面へ表示されます。

画面への文字列の出力
5 キャッシュの重要性
以上から分かるように、システムは情報の移動に多くの時間を費やしており、このようなコピー処理によってプログラムの動作はある程度遅くなります。
プロセッサと主記憶の速度差に対処するため、近いうちに使用される可能性がある情報を格納するキャッシュメモリ(cache)が使用されます。L1キャッシュ、L2キャッシュなどは、スタティックランダムアクセスメモリ(SRAM)技術によって実装されます。キャッシュでは局所性の原理、すなわちプログラムには局所的な領域にあるデータやコードへアクセスする傾向があるという性質を利用します。
キャッシュを利用することで、プログラムの性能を1桁向上させることができます。

キャッシュメモリ cache
6 記憶装置の階層構造
メモリ階層では、プロセッサと、より大容量で低速な装置との間に、より小容量で高速な記憶装置を配置します。
基本的な考え方は、1つ上の階層のメモリを、1つ下の階層のメモリに対するキャッシュとして使用することです。

メモリ階層
7 オペレーティングシステムによるハードウェアの管理
プログラムは、オペレーティングシステムが提供するサービスを通じてハードウェアへアクセスします。アプリケーションプログラムによるハードウェア操作は、すべてオペレーティングシステムを経由しなければなりません。

コンピュータシステムの階層構造
オペレーティングシステムは、制御不能なアプリケーションプログラムによるハードウェアの不正使用を防ぐとともに、複雑で多様なハードウェアデバイスを制御するための、単純で一貫性のある仕組みをアプリケーションプログラムへ提供します。これは、いくつかの抽象概念(プロセス、仮想メモリ、ファイル)によって実現されます。
ファイルはI/Oデバイスを抽象化したもの、仮想メモリは主記憶とI/Oデバイスを抽象化したもの、プロセスはプロセッサ、主記憶、I/Oデバイスを抽象化したものです。

オペレーティングシステムが提供する抽象表現
7.1 プロセス
プログラムの実行時、オペレーティングシステムは、現在のプログラムがプロセッサ、主記憶、I/Oデバイスを独占しているかのような状態を提供します。このような見かけ上の状態は、プロセスによって実現されます。
プロセスは、実行中のプログラムに対してオペレーティングシステムが提供する抽象概念です。1つのシステムでは複数のプロセスを同時に実行でき、それぞれのプロセスはハードウェアを独占しているかのように動作します。
並行実行とは、あるプロセスの命令と別のプロセスの命令が交互に実行されることを指します。これは、プロセッサがプロセス間を切り替えることで実現され、この仕組みをコンテキストスイッチと呼びます。
コンテキストとは、オペレーティングシステムが追跡する、プロセスの実行に必要なすべての状態情報です。これにはPC、レジスタの現在値、主記憶の内容などが含まれます。単一プロセッサシステムでは一度に1つのプロセスのコードしか実行できないため、別のプロセスを実行する際にはコンテキストスイッチ、すなわち現在のプロセスのコンテキストを保存し、新しいプロセスのコンテキストを復元する処理が必要です。

プロセスのコンテキストスイッチ
図に示すように、プロセスの切り替えはオペレーティングシステムのカーネル(kernel)によって管理されます。カーネルはオペレーティングシステムのコードのうち主記憶に常駐する部分であり、オペレーティングシステムがすべてのプロセスを管理するために使用するコードとデータ構造の集合です。アプリケーションプログラムがオペレーティングシステムの機能を必要とすると、特殊なシステムコール(system call)命令が実行され、制御がカーネルへ渡されます。カーネルは要求された処理を実行した後、アプリケーションプログラムへ制御を戻します。
7.2 スレッド
1つのプロセスは、複数の実行単位(スレッド)で構成できます。各スレッドはプロセスのコンテキスト内で動作し、同じコードとグローバルデータを共有します。マルチスレッドは複数のプロセスよりもデータを共有しやすいため、一般にスレッドのほうがプロセスよりも効率的です。
7.3 仮想メモリ
仮想メモリは、各プロセスが主記憶を独占して使用しているかのような状態を提供します。各プロセスから見える一貫したメモリを仮想アドレス空間と呼びます。Linuxでは、アドレス空間の最上部の領域にオペレーティングシステムのコードとデータが配置され、下部にはユーザープロセスが定義したコードとデータが格納されます。

仮想アドレス空間
-
プログラムのコードとデータ
すべてのプロセスにおいて、コードは同じ固定アドレスから始まり、その後にCのグローバル変数に対応するデータ領域が続きます。
-
ヒープ
実行時のヒープは、
mallocやfreeなどを呼び出すことで、実行時に動的に拡張または縮小できます。 -
共有ライブラリ
アドレス空間の中間部分には、C標準ライブラリや数学ライブラリなどの共有ライブラリのコードとデータが格納されます。
-
スタック
ユーザースタックはユーザー仮想アドレス空間の上部に位置し、コンパイラが関数呼び出しを実現するために使用します。スタックも実行時に動的に拡張または縮小できます。関数を呼び出すとスタックが拡張され、関数から戻るとスタックが縮小されます。
-
カーネル仮想メモリ
アドレス空間の最上部に位置します。アプリケーションプログラムは、この領域の内容を読み書きしたり、カーネルコードで定義された関数を直接呼び出したりすることはできず、必ずカーネルを介して呼び出す必要があります。
基本的な考え方は、プロセスの仮想メモリの内容をディスク上に保存し、主記憶をディスクのキャッシュとして使用することです。
7.4 ファイル
ファイルとはバイト列であり、すべてのI/Oデバイスをファイルとして扱うことができます。Linuxシステムの入出力は、Unix I/Oと呼ばれる少数のシステム関数を使用してファイルを読み書きすることで実現されます。
ファイルは、さまざまなI/Oデバイスを扱うための統一されたビューをアプリケーションプログラムに提供します。
8 システム間のネットワーク通信
単一のシステムから見ると、ネットワークは1つのI/Oデバイスとみなすことができます。システムは別のマシンから送信されたデータを読み取り、自身の主記憶へコピーできます。

ネットワークデバイスI/O
helloプログラムはリモートサーバー上で実行することもでき、ネットワークを介してサーバーと通信し、返された結果を取得できます。

telnetを利用してネットワーク経由でhelloをリモート実行する
9 重要なテーマ
9.1 アムダールの法則
システムの一部を高速化した場合、システム全体の高速化率は、その部分の重要性と高速化の程度によって決まります。
$\alpha$はその部分の実行時間が全体の実行時間に占める割合、$k$は性能の向上率です。
$$
T_{new}=(1-\alpha)T_{old}+(\alpha T_{old})/k = T_{old}[(1-\alpha)+\alpha/k]
$$
高速化率$S=T_{old}/T_{new}$を計算すると、
$$
S=\frac{1}{(1-\alpha)+\alpha/k}
$$
kが無限大に近づくと、
$$
S_{\infty}=\frac{1}{(1-\alpha)}
$$
9.2 並行性と並列性
並行性:複数の処理が同時に進行しているシステムを指します。並列性:並行性を利用してシステムをより高速に動作させることを指します。
-
スレッドレベルの並行性
プロセスを使用すると複数のプログラムを同時に実行でき、それによって並行性が生じます。この並行性は、1台のコンピュータが実行中のプロセス間を高速に切り替えることで疑似的に実現されます。これにより、複数のユーザーが同時にシステムを操作したり、1人のユーザーが複数のタスクを同時に実行したりできます。
マルチコアプロセッサは、複数のCPUを1つの集積回路チップ上に統合したものです。

マルチコアプロセッサ
ハイパースレッディングは、同時マルチスレッディングとも呼ばれ、1つのCPUが複数の制御フローを実行できるようにする技術です。ハイパースレッディング対応プロセッサは、サイクル単位でどのスレッドを実行するかを決定できるため、CPUの処理資源をより効率的に活用できます。
マルチプロセッサによるシステム性能の向上:1. 複数のタスクによる疑似的な並行性の必要性を減らせる。2. アプリケーションプログラムをより高速に実行できる(プログラムをマルチスレッド形式で作成する必要がある)。
-
命令レベルの並列性
プロセッサが複数の命令を同時に実行できることを指します。プロセッサはパイプラインを使用して命令の実行速度を高めます。命令の実行を複数の異なる手順に分割し、プロセッサのハードウェアを一連のステージとして構成して、各ステージで1つの手順を実行します。各ステージは並列に動作し、異なる命令の異なる手順を処理します。
スーパースカラプロセッサ—1サイクルにつき1命令を上回る実行速度を持つプロセッサです。
-
単一命令・複数データ並列性
プロセッサの特殊なハードウェアにより、1つの命令から並列実行可能な複数の処理を生成できます。これを単一命令・複数データ、すなわちSIMD並列性と呼びます。主に画像、音声、映像データを処理するアプリケーションの実行速度を向上させる目的で使用されます。コンパイラがサポートする特殊なベクトルデータ型を使用してプログラムを記述できます。
9.3 抽象化の重要性
抽象化は、コンピュータサイエンスにおける最も重要な概念の1つです。
プロセッサでは、命令セットアーキテクチャが実際のプロセッサハードウェアを抽象化します。

コンピュータシステムの抽象化
仮想マシンは、オペレーティングシステム、プロセッサ、プログラムを含むコンピュータ全体を抽象化したものです。
この記事が役に立ったときは、ぜひ他の人に共有してください!
一部の情報は古い可能性があります





