コピーオンライト (Copy-On-Write) とは、コンピュータプログラミングにおける最適化戦略の一種である。COWと略記することもある。

コンピュータ内部で、ある程度大きなデータを複製する必要が生じたとき、愚直な設計では、直ちに新たな空き領域を探して割り当て、コピーを実行する。 ところが、もし複製したデータに対する書き換えがなければその複製は無駄だったことになる。

そこで、複製を要求されても、コピーをした振りをして、とりあえず原本をそのまま参照させるが、ただし、そのままで本当に書き換えてはまずい。原本またはコピーのどちらかを書き換えようとしたときに、それを検出し、その時点ではじめて新たな空き領域を探して割り当て、コピーを実行する。これが「書き換え時にコピーする」、すなわちコピーオンライト (Copy-On-Write) の基本的な形態である。

基盤となる考え方は、複数の(何らかの)要求者がリソースを要求するときに、少なくとも当初はそれらの要求を区別する必要がないときに同じリソースを与える、というものである。これは要求者がリソースを「更新」しようとするまで保持され、「更新」が他者に見えないようにリソースの個別のコピーを必要になった時点で作成する。要求者からはこの一連の動きは見えない。第一の利点は要求者が全く更新しなければ、個別のコピーを作成する必要が生じないという点である。

仮想記憶への応用

編集

コピーオンライトは主に仮想記憶方式のオペレーティングシステムで使用されている。 例えば、プロセスのコピーを作成 (fork) するとき、書き換えることのないメモリページは、両方のプロセス(元のプロセスと生成されたプロセス)で共用し、書き換える可能性のあるメモリページは、新たなメモリページを割り当ててコピーを作成する必要がある。 ここでコピーオンライトが使用される。一方のプロセスがメモリを更新すると、オペレーティングシステムのカーネルがその操作を横取りし、メモリのコピーを作成してメモリ内容の更新が他方のプロセスから見えないようにする。

他にも、calloc()関数で使われている。calloc()はメモリ領域をゼロで初期化して確保する(malloc参照)。このためにゼロで初期化されたページをシステムに1つ用意しておき、calloc()で確保した領域にはこのページをマッピングしてコピーオンライト機能を使用する。このようにするとcalloc()呼び出し直後はたとえ何ページのメモリが必要でも物理的には1ページしか使われておらず、その領域の内容を更新しようとしたときに初めて個別のコピーが作成される。このような最適化はページサイズ以上の大きなアロケーションでのみ使用されるのが一般的である。

コピーオンライトは、MMUに対してプロセスのアドレス空間のある領域がリードオンリーであると通知することで実現する。その領域にデータを書き込もうとしたとき、MMUは例外を発生し、それをカーネルが処理する。カーネルは新たな物理ページを確保して、書き込みが行われた領域のマッピングを変更して、その新たな物理ページに対応させる。

COWの主な利点は、メモリを空間的にまばらに利用する可能性にある。データを格納したときだけ物理メモリ使用量が増えるので、若干メモリ使用量が増えるが非常に効率的なハッシュテーブルを実装することができる。しかし、そのような使い方をすると仮想空間を使い切ってしまう危険性もある(ハッシュテーブルに使われる仮想アドレス空間の領域はマッピングされる物理ページはまばらだが、他の用途に空間を利用できない)。カーネルレベルでのCOWの主な問題点はその複雑さにあるが、そのような懸念はスワップ方式などのもっと基本的な部分も同じである。カーネル自身がコピーオンライト制御されているページに書き込みを行った場合、やはりコピーが必要となる(訳注:例えばシステムコールの結果をユーザー空間に書き込む場合など。実際には書きこみの前にコピーオンライトかどうかをチェックし、必要に応じてコピー作成後に書き込みを行う)。

COWはカーネル以外でも、ライブラリアプリケーションやシステムコードなどでも使われる。C++Standard Template Libraryが提供する string クラスはコピーオンライト型の実装が可能な設計になっている[1]。このような領域でのCOWはマルチスレッドコードで問題となる。コピー元のリソースを複数のスレッドで共有するにはロックが必要であり、COWの利点以上のオーバヘッドとなる可能性が高い。

ファイルシステムへの応用

編集

COWはファイルシステムにおけるスナップショット機能を実現するアルゴリズムとして利用される。特定時点のファイルシステムの状態を原本とし、その後ファイルないしはブロックの変更があった場合には新たにオリジナルのそれを複製し、変更をその複製にのみ反映することで実現する。スナップショット機能を備える論理ボリュームマネージャやファイルシステムの多くがその実現にCOWを用いている。

単純なファイルのコピーも、COWで効率化できる可能性がある。 但し、ファイルシステムでのサポートとOSがファイルコピー用のAPIを提供し、アプリケーションがそれを使用することが条件となる。 (Windows APIではCopyFile()関数を提供しているが、POSIXシステムコールには存在しない。)

ファイルシステムが重複排除機構を備えている場合は更に相性が良い。前述の通り、COWにてコピーされたファイルは元ファイルと同一のデータブロックを参照しているため、コピー終了時点で"重複排除済み"とみなすことが出来る。このためCOWコピーされたファイルに改めて重複排除処理を加える必要がなくなる(もしくは一瞬で完了する)。

また、BochsQEMUといった仮想マシンの仮想ディスク装置で使われている。同じディスクイメージを使って複数の仮想マシンを動作させれば、必要とされるディスク容量を劇的に減らすことができる。またディスクからの読み込みイメージがメモリにキャッシュされ、それが仮想マシン間で共有されるため、性能も向上する。

制限事項として、ファイル上の既存のデータを更新する場合、コピーオンライト処理のために一定の空き容量が必要となる。データ更新の処理中、一時的に更新前と後のデータの両方がファイルシステムに存在することに依る。コピーオンライト処理のために十分な空き領域がファイルシステムにない場合は、ファイルシステムに空き容量があるにもかかわらず容量不足のエラーが発生し、一見奇妙な問題となる。

脚注

編集
  1. ^ ISO/IEC 14882:2003 時点において。その後の改定である ISO/IEC 14882:2011 においては規格の要求に従う場合そのような実装を行えなくなった。

関連項目

編集