情報処理 において共有メモリ (きょうゆうメモリ)とは、複数のプログラムが同時並行的にアクセスするメモリ である。
概要
3つのプロセッサによる共有メモリシステムの図。
複数のプログラム間の通信手段として使う場合と、単に複製を用意する冗長さを防ぐ目的の場合などがある。共有メモリはプログラム間でデータをやりとりする効率的手段である。文脈によって、それらプログラムが単一のプロセッサ上で動作する場合と複数の異なるプロセッサ群上で動作する場合がある。単一のプログラムの内部でメモリを使って通信する場合もあり、例えばマルチスレッド が典型的だが、仮想空間をもともと共有している場合は「共有メモリ」とは呼ばない。
ハードウェアによる共有メモリ
コンピュータのハードウェア による共有メモリ は、マルチプロセッサシステム における複数のCPU がアクセスできるRAM の(通常)大きなブロックを意味する。
共有メモリシステムでは、全プロセッサがデータを共有しているためプログラミングが比較的容易で、同じメモリ位置へのアクセスによって高速なプロセッサ間通信が可能である。問題は、CPUはなるべく高速なメモリアクセスを必要とするため、それぞれにキャッシュメモリ を持っていることが多い点である。そのため、以下の2つの問題が生じる。
CPU-メモリ間がボトルネック になりやすい。共有メモリ型コンピュータはあまりプロセッサ数を増やせない(CPUを増やしてもCPU数に比例して性能が強化されなくなる)。多くの場合、10個かそれ以下のプロセッサ数である。
キャッシュコヒーレンシ 問題。あるキャッシュ上であるメモリ位置の情報が更新され、それを他のプロセッサが必要とする場合、その更新を他のプロセッサにも反映させなければならない。さもないとそれぞれのプロセッサが一貫していないデータを使って動作することになる。そのためのプロトコルをコヒーレンシプロトコルと呼び、それがうまく機能すれば複数のプロセッサが高速に共有メモリ(上の情報)にアクセスできるようになる。しかし一方で、コヒーレンシプロトコルがオーバーヘッドとなり、性能のボトルネックになることもある。
ボトルネック問題を和らげる技術として、クロスバースイッチ 、オメガネットワーク (英語版 ) 、HyperTransport 、CPUバス の分離(フロントサイドバス とバックサイドバス、等)などがある。
共有メモリ以外の方式として分散メモリ (英語版 ) や分散共有メモリ があるが、どちらにも似たような問題がある。また、NUMA も参照。
GPU内の共有メモリ
GPGPU に対応したモダンなGPU は、GPUのスレッドブロック(ワークグループ、スレッドグループ)内でのみアクセス可能な共有メモリ(ローカルメモリ、グループ共有メモリ)を有している。この共有メモリは、VRAM 上に確保されるグローバルメモリと比べると小容量だが高速であり、アプリケーションコードから操作可能なキャッシュメモリの役割を果たす[ 1] 。CUDA 、OpenCL 、DirectCompute のようなAPIは、それぞれ名称は異なるものの、このGPU共有メモリを利用する機能を持ち、グローバルメモリから読み出したデータをGPUのスレッドブロック内で共有したり、計算結果を交換したりする用途に活用することで、高速化を図ることができる。
ソフトウェアによる共有メモリ
ソフトウェアにおける共有メモリ は、以下のいずれかを意味する。
プロセス間通信 (IPC) の技法の一つ。同時に動作しているプログラム間でデータを交換する方法である。1つのプロセス がメモリ上に他のプロセスからもアクセスできる領域を作成する。
通常、アクセスする主体ごとにコピーを用意するようなデータがあるとき、仮想記憶 機構や何らかの明示的プログラム機構を使ってそれらが同じ実体(物理メモリ)をアクセスするようマッピングすること。共有ライブラリ やXIP (Execute in Place) でよく使われる。
スレッド 実装の一方式
プロセス群は共有メモリ領域に通常のメモリ領域と同じようにアクセスできるので、他のプロセス間通信(名前付きパイプ 、ソケット 、CORBA など)と比較して通信手段としては非常に高速である。しかし、プロセス群が同じマシン上で動作しなければならないという制約があり(他のIPC手段はネットワーク上でも機能する)、プロセスが別々のCPU上で動作する場合はハードウェアによる共有メモリを使っていることになり、キャッシュコヒーレンシ などに注意が必要となる。プロセス間の通信がFIFO なストリーム 型の場合は、名前付きパイプ も通信手段として検討すべきである。一般に共有メモリ自体は保護機能をもたないので動作は高速である。しかし共有されるメモリは不定のタイミングで複数のプロセスからアクセスされる可能性がある。競合を避ける為にはセマフォ やロックなどで競合を回避しなければならない。
共有メモリによるIPCは、例えばUNIX 上のXサーバ とアプリケーションの間で画像を転送する場合や、Windows のCOM ライブラリで CoMarshalInterThreadInterfaceInStream()
関数が返す IStream
オブジェクトの内部で使われている。一般的に共有メモリが使われるアプリケーションとしてOracle などのデータベースがある。Unix版OracleではSGAと呼ばれる共有メモリ空間にデータベースバッファキャッシュがおかれて複数のプロセスからアクセスさせて性能の向上を図っている。
動的ライブラリ は一度メモリ上に置かれると、それが複数のプロセスにマッピングされ、プロセスごとにカスタマイズされるページ群(シンボル解決に違いが生じる部分)だけが複製され、通常コピーオンライト という機構で、そのページに書き込もうとしたときにコピーが行われる。
UNIXでのサポート
POSIX には共有メモリの標準化APIとして POSIX Shared Memory がある。これは、sys/mman.h にある shm_open
という関数を使う[ 2] 。POSIXのプロセス間通信(POSIX:XSI拡張の一部)には共有メモリ関数として shmat
、shmctl
、shmdt
が含まれている[ 3] [ 4] 。
shm_open
で生成された共有メモリは永続的であり、プロセスが明示的に削除しない限りシステム内に存在し続ける。ただしこれには欠点もあり、共有メモリを削除すべきプロセスがその前に異常終了したとき、その共有メモリがシステムのシャットダウンまで残存し続けることになる。そのような問題を避けるには、mmap を使って共有メモリを作成すればよい[ 5] 。2つの通信しあうプロセスが同じ名前の一時ファイルをオープンし、それに対してmmapすることでファイルをメモリにマッピングする。結果として、メモリマップされたファイル(メモリマップトファイル )への変更はもう一方のプロセスからも同時に観測できる。この技法の利点は、両方のプロセスが終了したとき、OSが自動的にファイルをクローズし、共有メモリを削除する点である。
Linuxカーネル 2.6 では、RAMディスク 形式の共有メモリとして /dev/shm が導入された。より正確に言えば、誰でも書き込めるメモリ内のディレクトリであり、その容量の上限は /etc/default/tmpfs で指定できる。/dev/shm 機能サポートはカーネルの設定ファイル で指定でき、デフォルトでは無効となっている。なお、RedHat や Debian ベースのディストリビューションではデフォルトで有効になっている。
Androidでのサポート
Android では Linux カーネルを使用しているが、IPC 関係が一部無効になっており、独自に開発した(現在はLinuxカーネルに入っている)ashmem (anonymous shared memory) を使用している。メモリが不足したときにカーネルが解放する仕組みがあり、解放されないようにするには、ashmem_pin_region()
を使い指定する。
Windowsでのサポート
Microsoft Windows では、Win32 API のCreateFileMapping()
関数を使って共有メモリ(メモリマップトファイル )を作成することができる[ 6] 。クライアント側プロセスはOpenFileMapping()
関数を使って、ホスト側プロセスにて作成済みの共有メモリのハンドルを取得することができる。共有メモリを各プロセスのアドレス空間にマッピングするにはMapViewOfFile()
関数を使う。
なおWindows APIには、CreateSharedMemory()
[ 7] [ 8] など “-SharedMemory” の名前を持つ関数があるが、これはセキュリティ関連のAPIであり、メモリ共有のためのAPIではない。これをメモリ共有のために使用すれば、リソースを大量に消費しシステムリソースを使い果たす可能性がある。
プログラミング言語ごとのサポート
一部のC++ ライブラリは、共有メモリ機能への移植性の高いオブジェクト指向的なアクセスを提供している。例えば、Boost C++ライブラリ には Boost.Interprocess があり[ 9] 、POCO C++ Libraries には Poco::SharedMemory、Qt には QSharedMemory クラスがある[ 10] 。
PHP では POSIX で定義している関数群とよく似た共有メモリ用APIが存在する[ 11] 。
.NET Framework はバージョン4でSystem.IO.MemoryMappedFiles.MemoryMappedFile
クラス[ 12] を標準化した。.NET Core あるいはXamarin (Mono ) を通じて、Windows以外の他のプラットフォームでも利用できる。
脚注
関連項目
学習参考書
外部リンク