こんにちは。
最近、プログラムはなぜ動くのか 第2版を読んでいるので内容を忘れないように学んだことを要約したメモを載せます。
このエントリは完全な個人のメモです。
お勉強のためにこの本を読んでいるので、内容を覚えるためにSummarizing(サマライジング)を行います。
1章~4章の要約はこちら。
第5章 メモリとディスクの親密な関係
ストアド・プログラム方式
プログラムは一度ディスクからメモリにロードされてから実行される。
この方式をストアド・プログラム方式という。
CPUはプログラムカウンタを使ってメモリを読み取ってプログラムを実行する。
ディスク・キャッシュ
ディスクI/Oは遅いため、早くするための方法。
いったんディスクから読み取ったデータをメモリに残しておくこと。
メモリの中に仮想的なディスクを作るイメージ。
仮想記憶
ディスクキャッシュがメモリ上の仮想ディスクだとしたら、仮想メモリはディスクにある仮想的なメモリ。
仮想記憶にはページング方式とセグメント方式がある
・ページング方式とは
プログラムを一定の大きさに区切った「ページ」単位でメモリとディスク間のデータの置き換えを行う方式。
Windowsはページング方式を採用している。
・セグメント方式とは
プログラムをデータや処理など意味のある単位のセグメントに分割する方法。
メモリを節約するプログラミング手法
・DLLファイルでライブラリを共有する。
DLLとは、プログラムの実行時に動的にリンクされるライブラリのこと。
複数のプログラムが一つのライブラリを共有できるのでメモリの節約になる。これに対して、各実行ファイルの中にライブラリの関数を直接入れ込むのがスタティック・リンク。
・_stdcall呼び出しでプログラムのサイズを小さくする
スタックのクリーンアップ処理を関数の中で行うための宣言。
通常は呼び出しもとでスタックのクリーンアップ処理を行う。
これはC言語が可変長の引数に対応しているため。
言語が可変長の引数に対応している場合、スタックの仕様領域は呼び出し元でないとわからないので呼び出しもとでクリーンアップ処理をする必要がある。
ディスクの物理構成
セクタ方式とバリアブル方式がある。
・セクタ方式
ディスクを同心円状に区切った領域をトラック、トラックを固定長に区切ったものをセクタという。
また、いくつかのセクタをまとめたものをクラスタという。
ディスクの読み書きはクラスタ単位で行われる。
Windowsではセクタ方式を採用している。
・バリアブル方式
ディスクを可変長サイズに区切って使用する方式。
第6章 自分でデータを圧縮してみよう
ランレングス法
同じ文字がどれだけ続くかで圧縮する方法。
画像ファイル、EXEファイルなどには有効だが、テキストファイルとかだと同じ文字が続くことが少ないので容量が増えることが多い。
ファックスの画像圧縮などに使われる。
ハフマン法
モールス信号から着想を得た圧縮方法。
よく使う文字は小さいビット、あまり使われない文字は大きいビットで表現することによってファイル全体を圧縮する。
かなり高い圧縮率が得られる。
対象のファイルによってどの文字をどういうビット列で表現するかが変わるので、圧縮後のファイルは圧縮されたデータとハフマン符号を持つ。
ハフマン符号は木を用いて作られる。
ハフマンツリーはめっちゃ頭いいなと思った。
可逆圧縮と非可逆圧縮
可逆圧縮→完全に元のデータを復元できる。
不可逆圧縮→元のデータには戻せない。
第7章 プログラムはどんな環境で動くのか
プログラムの動作環境とは
動作環境=OS + ハードウェア
CPUも種類によって処理できるマシン語が異なる。
OSも、OSごとにAPIが異なる(=システムコールの仕方が異なる)。
OSが提供しているのはキー入力、マウス入力、ファイル入出力、ディスプレイ出力などの周辺装置と入出力を行う機能。
FreeBSDのPorts
FreeBSDで使えるPortsという仕組みもある。
ネイティブコードがOS間で使いまわせないならソフトウェアはソースコードで配布したらいいじゃない。
みたいなのがPorts。
FreeBSDというUNIX OSで使える。
アプリのソースコードがネットに公開されていて、そこからソースを落とすことができる仕組み。
PortsはCPUを含めたすべてのハードディスクの違いを吸収することができる。
エミュレータ
Windows上でMac OSを動かしたり、Ubuntuを動かしたりすること。
Androidのエミュレータとか。
Javaの仮想マシン
Javaは以下の流れでプログラムを変換・実行する。
ソースコード→(コンパイル)→バイトコード→(逐次変換)→ネイティブコード
バイトコードでの配布をしたらどのOSでも動く。
ただし、特定のOSでしか使えない機能などは当然ある。
実行速度が遅い。
ただし、早く実行するための仕組みもある。
例えば、2回目からは1回目の実行時に生成したネイティブコードを使う、処理を最適化をするなど。
BIOSとブートストラップ
BIOSはキーボード、マウス、グラボ、ディスクの基本制御プログラムとブートストラップローダーを提供。
電源を入れるとBIOSが起動し、ブートストラップローダーがOSをメモリにロードして実行する。
第8章 ソース・ファイルから実行可能ファイルができるまで
コンパイルとは
CPUが処理できるのはネイティブコードだけ。
マシン語のコード=ネイティブコード
高水準言語のコード=ソースコード
ソースコードをネイティブコードに変換することをコンパイルという。
変換してくれるツールがコンパイラ。
クロスコンパイル
Linux環境で動くプログラムをWindows環境でコンパイルしたり、Windows環境で動くプログラムをLinux環境でコンパイルしたりすること。
クロスコンパイルをするコンパイラをクロスコンパイラという。
リンクとは
実はコンパイルするだけでは実行ファイルは得られない。
コンパイルのあとにリンクという作業が必要。
コンパイルして得られたオブジェクトファイルと、ライブラリをリンクして実行ファイルが出来上がる。
そのほかにも、すべてのプログラムの先頭に共通する処理がかかれたオブジェクトファイル(スタートアップ)と結合する。
DLLとインポート・ライブラリ、スタティック・リンク。ライブラリ
DLLとはDynamic Link Libraryの略。
プログラムの実行時に結合される。
インポートライブラリは、使用したいAPIがDLLであることと、どのDLLに含まれているのかを記述したファイルのこと。
リンク時にインポート・ライブラリを使うとDLLの関数を使うのが楽。
スタティック・リンク・ライブラリは実行ファイルに直接結合するライブラリのこと。
スタックとヒープ
プログラムがメモリにスタック領域とヒープ領域が確保される。
スタックは一時的な変数や関数呼び出し時の引数を格納するための領域。
クリーンアップ処理はコンパイル時に自動で付与される。
ヒープはプログラム実行時(ロード時ではない)にmallocとかで任意のデータやオブジェクトを格納するための領域。
プログラムで明示的にメモリの確保と開放を行う必要がある。
開放し忘れるとメモリ・リークが起きる。
コンパイラとインタプリタの違い
コンパイラはプログラム実行前にソースコード全体を解釈してオブジェクトファイルを作る。
インタプリタはプログラム実行時に1行ずつソースコードを解釈して実行する。
分割コンパイル
プログラムを複数のファイルに分けて書き、それらを個別にコンパイルし、最後に1つの実行ファイルにリンクすること。
ビルド
コンパイルとリンクを行うこと。
DLLを使うメリット
DLLで提供される関数は複数のプログラム間で共有される。
それによってメモリの節約や、関数変更時の修正箇所が減るなどのメリットがある。
インポート・ライブラリ
LoadLibrary()やGetProcAddress()などのAPIを使えばDLLで提供される関数を使用できるが、インポート・ライブラリを使えばより簡単にDLLを使える。
オーバーレイ・リンク
同時に実行されることがない関数を、メモリの同じ場所に交代でロードしながらして実行すること。
オーバーレイ・リンカというリンカを使うことで実現可能。
メモリの量が少なかったMS-DOS時代に使われていた。
ガベージ・コレクション
処理が終わって使わなくなったヒープ領域のデータやオブジェクトを破棄して、メモリ領域を解放すること。
Javaでは実行環境が自動で行ってくれる。
CやC++ではプログラムで明示的に行う必要がある。
大体こんな感じです。
9章~12章の内容はこちら。
おわり。