仮想メモリ
CPU は演算時に状態を一時的に保存する先として、通常内部に レジスタ が存在している。これは非常に高速にアクセスが可能だけれど、記憶容量は小さい上に個数も多くない。そのため、一般的には OS 本体や各種アプリケーション、実行中のコードやその変数等は メモリ に記憶される。メモリ は大容量のものを用意することが可能だが、CPU 外部に存在する DRAM であるため、レジスタ へのアクセスと比較すると遅い。 メモリ は、その先頭から 1 バイトずつふられた連番を使って利用する。一番先頭が 0 であり、1 バイト毎に 1 ずつ増えていく。この連番を メモリアドレス もしくは アドレス と呼ぶ。 メモリの断片化
合計で 300 byte の メモリ が空いていても、連続で 300 byte のメモリを利用できるとは限らない プログラム側で、メモリ が連続しているか?離散しているか?を意識することはとても難しい 連続して n byte の空きがないから要素数 n の配列が作れない... とかを意識しなくてはいけなくなってしまう
別用途のメモリへのアクセス
あるプロセスが他のプロセスのメモリにアクセスして、それらを壊してしまう危険性がある
そこで、プロセスは直接物理アドレスにはアクセスさせない ようにし、代わりに プロセス 固有の仮想アドレス空間にアクセスできる ようにする。これを管理するための 仮想メモリ という仕組みが、大抵の CPU に搭載されている。 ある 実行可能ファイル から プロセス が生成されると、カーネル はその プロセス 固有の仮想アドレス空間から、実行可能ファイル の内容を展開するための仮想アドレスを割り当てる。このようにメモリアドレスを割り当てる際、直接仮想アドレスの割り当てと同時に物理メモリ上での物理アドレスの確保までやってしまうと、割り当てられたけど実際には一度も利用されなかった ような物理メモリアドレスが発生してしまう恐れがある。物理メモリはなるべく節約したいので、このような無駄は避けたい。これを避けるための カーネル の仕組みが デマンドページング である。 その後無事仮想アドレスと物理アドレスがどちらも確保され、そのマッピングも行われて、プロセスの処理は続行する。 動的なメモリ確保
プロセス が追加で メモリ を確保したい場合は、カーネル にそれを要求できる。mmap を利用すると、ページ単位で仮想アドレスの確保を行うことができる (デマンドページング により、物理アドレスが同時に確保されることはない)。mmap はページ単位でメモリを確保できるが、C 言語の malloc 関数はバイト単位でメモリを確保できる。実は、この malloc 関数で確保されるメモリは、事前に glibc が mmap 関数で確保していたメモリプール上から確保される。事前に大きめのメモリプールを確保し、その中から改めて malloc で確保することによって、バイト単位のメモリの確保を実現している。 例えば実行対象の プロセス をマルチプロセスで動作させる場合や、それが他の複数の プロセス から参照されるライブラリのような プロセス であったりした場合、プロセス が複数実行される。このとき、仮想メモリ の仕組みを利用して プロセス のメモリへの展開が高速化されている。 実行可能ファイル には通常、マシンコード本体やソースコード上の定数定義など、実行中に書き換える必要のない部分と、変数等の書き換えが必要な部分が区分として分かれて存在している。カーネル がこれらを 仮想メモリ 上に展開する際、前者は 読み込みのみ可能、後者は読み書き可能と言ったパーミッション付与できる (ここは Linux カーネルの動作仕様を知らないので、間違えているかも... 雰囲気で...)。 fork 等で元プロセスを親として子プロセスを生成した場合、生成時には両プロセス共、各々の仮想アドレスを通して同じ物理アドレスを共有する。こうすることで、ディスク上から再度 実行可能ファイル を読み取って物理メモリ空間上に展開するためのディスク I/O の発生を防ぐとこができる。 しかしその後、実行中に書き換え可能なページのに対して書き換えが発生する可能性がある。このとき、物理アドレス上のページを直接書き換えてしまうと、親子プロセス間が別々のプロセスであるにもかかわらず、互いに影響を及ぼし合ってしまう。これを防ぐために コピーオンライト (CoW) という仕組みがある (正確には、まず物理アドレスを共有するところから CoW というべきなのかもしれない)。 結果として、親子プロセスは、読み込み専用部分は物理アドレスを共有したまま、書き込み可能部分はプロセス固有の物理アドレスを参照する 状態となった。このように、書き込み時に物理メモリをコピーする という特徴から、コピーオンライト という命名になっている。 参考
武内覚 (2018-03-08). 試して理解Linuxのしくみ. 274p.