メモリ管理

メモリ管理とは?
物理メモリの構造
仮想記憶システム
ページの構造
アドレス変換
ハードウェアキャッシュ
仮想メモリの抽象モデル
デマンドページング
スワッピング
物理アドレスモードと仮想アドレスモード
メモリが少ないとどうなるか?
プロセスのメモリ配置 テキスト・セグメント データ・セグメント スタック・セグメント
mainの引数と環境変数

共有ライブラリ 静的リンク・ライブラリ 動的リンク共有ライブラリ 共有ライブラリの作成とリンク
共有メモリ 使用方法 注意事項
/proc について
プロセスのメモリマップ
システムのメモリ使用量を知る:vmstat
プログラム例 例1:メモリ・マップを調べる 例2:共有ライブラリの作成 例3:共有メモリの作成と使用法
例4:セマフォによるロックメカニズム



◆ メモリ管理とは?

UNIX では、どうやって複数のプログラムを実行しているのでしょうか?

具体的には、以下の3つの目標を実現する必要があります。

  1. 複数のプログラムを同時に実行すること。
  2. 物理メモリを効率良く管理すること。
  3. あるプログラムの暴走や異常終了が他のプログラムの実行へ影響しないこと。
そのためには、メモリ管理機構にどのような機能が必要でしょうか?

UNIXはこれらの問題を克服するために、 仮想記憶という概念を採用しています。

さらに、カーネルは物理メモリよりも大きなサイズのプログラムを実行させたり、 複数のプロセスを効率よく実行するために、 ページングスワッピングという手段を使います。




◆ 物理メモリの構造



図1:物理メモリの配置例

図1:物理メモリの配置例




◆ 仮想記憶システム

仮想記憶システムでは、アプリケーションに対して、 物理メモリのサイズに関係なく、 あたかも大きなメモリを利用しているような幻想を与え、 仮想アドレスから物理アドレスへ変換できる必要があります。

あるアプリケーションがアクセスするアドレスは、 直接物理メモリのアドレスへ変換されずに、アドレス変換表を利用して、 間接的に物理メモリにアクセスします。 この仕組みを仮想記憶と呼び、 これにより上記の機能が実現できます。

もちろん、仮想記憶も良いことばかりではなくて、

といったオーバヘッドが発生します。

仮想記憶を実現するためには、上記の仮想アドレス空間の管理、 アドレス変換テーブルの管理、物理メモリの管理を実現する必要があります。 加えて、複数のアプリケーションを実行する場合の、 各プロセスの保護や共有の仕組みも実現する必要があります。

メモリ管理機構の性能はシステム全体に影響を与えるため、 その設計は性能に注意を払わなければなりません。 このため、アドレス変換の変換テーブルや変換テーブルのキャッシュは、 ハードウェアで実現されることが多いです。

図2:仮想記憶

図2:仮想記憶

仮想記憶を実現する方法には、ページングがあります。

情報は必要なときにのみディスクから読み込まれます。 プログラム内部で使用される仮想アドレスは、 システムにより自動的に物理アドレスにマップされます。 プロセスが、 現在メモリ上にない実行可能ファイルのテキスト・セグメントにアクセスすると、 カーネルはページングを行います。 通常これは、プロセスがすでに必要としないページを置き換える形で行われます。 プログラムのテキスト・セグメントの不要部分のメモリは解放され、 ほかの用途に回すことができます。

プログラムが巨大なデータ領域を使用する場合でも、 これらすべてを同時にアクセスしないのであれば、 すべてが同時にメモリ上に存在する必要はありません。

しかし、プログラムを実行するには、 コードとデータ両方が物理メモリ上にあり、 実アドレスを持っていなければなりません。 したがって、 実行時に仮想メモリのアドレスから物理メモリのアドレスに変換する機構が必要です。

仮想メモリは、多層[階層記憶]の手法を用いて実現されています。 すなわち、メモリに収まらないプログラム部分は、 ローカルなディスク装置、 またはネットワーク上のリモート・ディスクに置かれます。 プログラム実行中に使用しないコードやデータ部は、 ディスクにおきます。

図3:デバイスの種類

図3:デバイスの種類





◆ ページの構造

プロセスは、走行時物理アドレスに変換される仮想アドレスを使用して、 コードやデータを参照し、 マッピング関数を使って、 仮想アドレスを物理メモリのアドレスにマップします。 つまり、仮想アドレスを受けとり、 アドレス・マップを適用して物理アドレスを生成します。

仮想アドレスは、 仮想ページ番号とそのページ内のオフセットから構成されます。 ソフトウェアとハードウェアの技術を結集して、 仮想アドレスをページ・テーブル中から探し出し、 物理ページを生成し、 これとオフセットを組み合わせて物理メモリ上の実アドレスを生成します。

しかし、物理ページが常にすべての仮想アドレス参照に利用できるわけではありません。 ページ・テーブルは、 そのページが現在メモリにあるかを示す、 有効フラグを各ページごとに持っています。 メモリ管理システムは、ページがメモリになければ、それはアクティブでなく、 そのページのディスク上における位置の情報を保持しています。

カーネルは、メモリにない、 つまりアクティブでないページをプロセスが参照すると、 物理メモリにそのページをロードするまで、そのプロセスの実行を一時停止します。

図4:仮想アドレスの構成要素

図4:仮想アドレスの構成要素




◆ アドレス変換

ページ管理は、仮想アドレス空間と物理メモリを固定サイズのメモリ領域に分割し、 これを領域単位で管理します。 分割された仮想アドレス空間をページ、 分割された物理メモリをページフレームといいます。 ページとページフレームを対応づける変換テーブルを、 ページテーブルといいます。

仮想アドレスから物理アドレスへの変換は、以下の順序で行なわれます。

  1. ページの仮想アドレスを、ページ番号とページオフセットに分割する。
  2. ページ番号とページフレームのアドレスを対応づけるページテーブルを利用して、 仮想アドレスの指すページフレームを探す。
  3. ページフレームのアドレスとページオフセットから物理アドレスを計算する。

図5:仮想アドレスから物理アドレスへの変換

図5:仮想アドレスから物理アドレスへの変換

実際のプロセッサではどうなっているでしょうか?

x86 では2段階のページテーブルを採用しています。
SuperSPARC では3段階のページテーブルを採用しています。

このアドレス変換テーブルは、多くの CPU でハードウェアとして実装されています。 変換テーブルの変換方法には、 ページテーブルの他にハッシュを利用したものもあります。 このハードウェアを MMU(Memory Management Unit) といいます。

プログラムの実行の度にページテーブルを利用して変換すると性能が低下するので、 これを防ぐために、CPU には変換テーブルのキャッシュがあります。 このキャッシュを TLB(Translation Lookaside Buffer) といいます。




◆ ハードウェアキャッシュ

キャッシュの実装として一般的なのが、 プロセッサ内のハードウェアキャッシュである、 ページテーブルエントリのキャッシュです。 この場合、プロセッサはいつも直接ページテーブルを読み出すのではなく、 変換が必要なとき、そのページ変換をキャッシュしておきます。

プロセッサ内には、アドレス変換バッファ (TLB) があり、そこにはシステム上の複数のプロセスに関する、 ページテーブルエントリのコピーがキャッシュされています。

仮想アドレスへの参照が行われるとき、プロセッサは合致する TLB エントリを探し、見つかれば、仮想アドレスを直接物理アドレスに変換して、 データに対して適切な処理を実行します。 プロセッサが合致する TLB エントリを見つけられない場合は、 カーネルに対して、TLB 失敗が起こったことをシグナルで伝えます。

システム固有のメカニズムを使用して、 その例外をカーネルの例外処理コードに渡します。 カーネルは、そのシグナルを受けて、 アドレスマッピングのための新しい TLB エントリを生成します。 例外がクリアされると、プロセッサは再度仮想アドレスの変換を試みます。 今度は TLB 内に必要なアドレスに関する有効なエントリがあるので、 問題なく変換されます。




◆ 仮想メモリの抽象モデル

図6:仮想アドレスから物理アドレスへのマッピングに関する抽象モデル

図6:仮想アドレスから物理アドレスへのマッピングに関する抽象モデル

仮想記憶システムでは、使用されるアドレスはすべて仮想アドレスで、 物理アドレスではありません。 仮想アドレスはプロセッサによって物理アドレスに変換されます。 その変換は、 カーネルが管理する変換テーブルに保持された情報を基に行われます。

仮想メモリと物理メモリは、変換を容易にするため、 ページと呼ばれる単位に分割されていて、 サイズはすべて同じです。 ページは、Solaris sprac では 8K バイト、 Intel x86 システム、AIX では、4K バイトです。 ページサイズは pagesize(1) コマンドがあればそれで知ることができます。 ページそれぞれには、他と重複しないページフレーム番号 (Page Frame Number, PFN)が割り振られています。

このようにページ分割されたシステムの場合、仮想アドレスは、 仮想ページフレーム番号(PFN)とオフセットから構成されています。

ページサイズが 4K バイトならば、 仮想アドレスの bit 11 から 0 がオフセットで、 bit 12 以降が仮想ページフレーム番号です。

プロセッサは仮想アドレスに出会うたびに、 仮想ページフレーム番号とオフセット番号を抽出し、 仮想ページフレーム番号を物理ページフレーム番号に変換し、 該当する物理ページの正しいオフセット位置にアクセスします。 プロセッサは、この変換のためにページテーブル(page table)を使用します。

図6は、プロセス X とプロセス Y の2つのプロセスの仮想アドレス空間を示すもので、 どちらもプロセス自身のページテーブルを持っています。 それらのページテーブルは、 プロセスの仮想ページをメモリの物理ページにマップするものです。

この図では、プロセス X の仮想ページフレーム番号 0 が物理ページフレーム番号 1 にマップされ、 プロセス Y の仮想ページフレーム番号 1 が物理ページフレーム番号 4 にマップされています。 ページテーブル内には、次の情報があります。

有効フラグ(valid flag)

ページテーブルが有効かどうかを示すフラグ。

物理ページフレーム番号(physical page frame number)

対応する物理ページフレーム番号。

アクセス制御情報(access control information)

ページの利用方法。 書き込み可能か、実行コードを含むか等に関係する情報。

ページテーブルにアクセスする場合、 仮想ページフレーム番号がページテーブルへのインデックスとして使用されます。 仮想ページフレーム番号 5 は、ページテーブルの 6 番目の要素に該当します
(0 が最初の要素に該当するため)。

仮想アドレスを物理アドレスに変換する場合、 プロセッサはまず仮想ページフレーム番号と、 その仮想ページ内のオフセット値を探します。 ページサイズを 2 の n 乗とすることで、マスクとシフト操作で簡単に得られます。

もう一度図6を見てほしい。 ページサイズが 0x2000 バイト(十進数なら 8192)で、 プロセス Y の仮想アドレス空間でのアドレスが 0x2194 だとすると、 アドレスを変換した結果は、仮想ページ番号 1 のオフセット値 0x194 です。

プロセッサは、仮想ページフレーム番号をプロセスのページテーブルへのインデックスとして使用することで、 そのページテーブルのエントリがわかります。 もしそのページテーブルのエントリが有効(valid)なら、 プロセッサはそのエントリから物理ページフレーム番号を取得します。 エントリが無効なら、 プロセスは物理メモリ上に存在しない仮想メモリ領域にアクセスしたことになります。 その場合、プロセッサはアドレスを解決できないので、 カーネルに制御を渡し、問題の解決を依頼します。

プロセスが有効な変換先のない仮想アドレスにアクセスしたことを、 プロセッサがカーネルに伝える方法は、その種類によって異なりますが、 伝達方法がどのようなものであれ、 その現象はページフォルト(page fault)と呼ばれ、 カーネルには、 フォルトとなった仮想アドレスとフォルトが生じた原因が通知されます。

プロセッサは、ページテーブルのエントリが有効な場合、 物理ページフレーム番号を取得し、それにページサイズを掛け、 物理メモリ内のページのベースアドレス(base address)を取得します。 最後に、プロセッサはそのベースアドレスを起点に、 取得すべき命令やデータまでのオフセットを加えることで、 完全な物理アドレスを得ます。

上記の例を再び取り上げると、プロセス Y の仮想ページフレーム番号 1 は、 物理ページフレーム番号 4 にマップされます。 その物理ページフレームは、 アドレス 0x8000(4 * 0x2000)を起点(ベースアドレス)としています。 それに 0x194 バイトオフセット値を加えることで、 0x8194 という物理アドレスが得られます。

仮想アドレスから物理アドレスへのマッピングにこうした方法を用いることで、 仮想ページフレーム番号と物理ページフレーム番号の関係は自由になります。

図6において、 プロセス X の仮想ページフレーム番号 0 は、 物理ページフレーム番号 1 にマップされますが、 仮想ページフレーム番号 7 は、 仮想ページフレーム 0 より大きな番号であるにも関わらず、 物理ページフレーム 0 にマップされます。 すなわち、仮想メモリのページは、物理メモリ内で特定の順番通りに並んでいる 必要がないということです。




◆ デマンドページング

仮想メモリに比べて物理メモリは非常に量が少ないので、 カーネルは物理メモリを効率的に使わなければなりません。 そのひとつの方法は、 実行中のプログラムが現在使用している仮想ページだけをロードすることです。

たとえば、データベースプログラムがデータベースに問い合わせたとします。 その場合、データベース全体をメモリにロードする必要はなく、 調べる必要のあるデータレコードだけをロードすればよいでしょう。 またデータベースへの問い合わせが検索の場合、 新規レコードを追加処理するプログラムをロードしても無駄です。 アクセスされた仮想ページだけをメモリにロードするテクニックは、 デマンドページング(demand paging) と呼ばれます。

プロセスが物理メモリ上にない仮想アドレスにアクセスしようとすると、 プロセッサは、参照された仮想ページのページテーブルエントリを見つけられません。

たとえば、 図6 では、プロセス X のページテーブルには、 仮想フレーム番号 2 のエントリがないので、 プロセス X が仮想ページフレーム番号 2 の中にあるアドレスを読もうとしても、 プロセッサは、そのアドレスを物理メモリ上のアドレスに変換できません。 この時点で、プロセッサはページフォルトが発生したことを、 カーネルに通知します。

フォルトとなった仮想アドレスが無効である場合、 それはプロセスが存在しない仮想アドレスにアクセスしたことを意味します。 おそらく、アプリケーションに不具合が起こって、 たとえばメモリ内のランダムなアドレスへ書き込みしようとしたのかもしれません。 この場合、カーネルはそのプロセスを終了させて、 暴走したプロセスからシステム内の他のプロセスを保護します。 この場合、通常プロセスの core が作成されます。

しかし、フォルトとなった仮想アドレスが有効なものでかつ、 参照されたページがそのときメモリ内になかった場合、 カーネルは、ディスク上のイメージから適切なページをメモリ内に持ってこなければなりません 。

ディスクアクセスは、相対的に遅い処理なので、 プロセスは、ページがロードされるまで待つ必要があります。 その際カーネルは、他に実行可能なプロセスがあれば、 それを選んで実行します。 そして、ロードしたページを空いた物理ページフレームに書き込んで、 仮想ページフレーム番号用のエントリをプロセスのページテーブルに追加します。 これによって、プロセスはメモリフォルトが発生した命令から再び実行されます。

今度は、仮想メモリへのアクセスに成功し、 プロセッサも仮想アドレスから物理アドレスへの変換ができるので、 プロセスの実行は継続されます。




◆ スワッピング

プロセスを実行する場合、仮想ページを物理メモリに持ってこなければなりませんが、 利用可能な物理メモリの空きがない場合、カーネルは、 別のページを物理メモリから取り除くことで、そのページのための空間を確保します。

物理メモリから取り除くべきページが、 テキスト・セグメントやデータファイルのコピーであり、 上書きされていない場合、そのページは保存する必要はありません。 プロセスが再度そのページを必要とするときに、 該当するテキスト・セグメントやデータファイルからメモリ内に戻せばよいのです。

しかし、ページが変更されている場合(データ・セグメントなど)、 カーネルはそのページを保存して、 後でそのページにアクセスできるようにしなければなりません。 この種のページは、 ダーティページ(dirty page)と呼ばれ、 メモリから削除するときは、スワップファイルに保存します。

スワップファイルへのアクセスは、 プロセッサとメモリ間のスピードに比べて非常に遅いので、 カーネルは、ページをディスクに書き込む必要がある場合でも、 再使用に備えて出来るだけそれをメモリに保持しようとします。

破棄もしくはスワップするページを決めるためのアルゴリズム (スワップアルゴリズム)の効率が悪い場合、 スラッシング(thrashing)と呼ばれる状態に陥ります。 その場合、ページは間断なくディスクに書き込まれたり読み出されたりするので、 カーネルはそれに時間を取られて本来の仕事が出来ません。

たとえば、もし物理ページフレーム番号 1 ( 図6) が定期的にアクセスされるなら、 そのページはハードディスクにスワップすべきではありません。 プロセスが現在使用しているページセットは、 ワーキングセット(working set)と呼ばれ、 効率の良いスワップスキームとは、 すべてのプロセスが自分のワーキングセットを物理メモリに持つよう手配するスキームです。




◆ 物理アドレスモードと仮想アドレスモード

カーネル自体が、 仮想メモリ内で実行されるというのはほとんどナンセンスです。 つまりカーネルが自分のページテーブルを管理しなければならないとしたら、 悪夢のような状況になるでしょう。

大部分の汎用プロセッサは、 仮想アドレスモードと同時に物理アドレスモードという概念をサポートしています。 物理アドレスモードでは、ページテーブルは不要で、 プロセッサはこのモードのときはアドレス変換しません。

物理メモリの構造にも書いてありますが、 オペレーティング・システムのカーネルは、物理アドレスモードで実行されます。




◆ メモリが少ないとどうなるか?

カーネルは、プロセスが新しいページを必要としかつ、 未使用ページ、あるいは再使用可能なページがない場合、 メモリ上の既存ページをページアウトし、 これらのメモリを動的に割り当てます。

つまり、ページをディスク上のページング領域に書き出します。 そして、そのページが再度必要になった時に、これを読み込ます。 そのときは他のページを強制的にページアウトします。 このような状況では、使用可能な CPU 時間のうち、 かなりの部分がページフォルト処理に費やされてしまい、 すべてのプロセスの実行効率が低下してしまいます。

この問題を解決する最良の方法は、メモリを増設することです。




◆ プロセスのメモリ配置

では、実際のプロセスのメモリ配置を見てみましょう。

プロセスのメモリは、 図7 のように、テキスト・セグメントデータ・セグメントスタック・セグメントに分類されます。

ここでセグメントとは、 連続したメモリ領域のことで、ページの集合体です。

データ保護

システム上のプロセスは、各々が独自の仮想アドレス空間を持っています。 これらの仮想アドレス空間は、お互いに完全に独立しているので、 あるアプリケーションを実行しているプロセスは、 他のプロセスの実行に影響を与えません。 さらに、メモリエリアはハードウェア上の仮想メモリ機構によって、 書き込みから保護されています。

実行中のプロセスが、自分のセグメント以外を参照しようとしたり、 書きこもうとすると、カーネルから SIGSEGV などのシグナルが送られます。 これを受信したプロセスは、カーネルから強制終了させられます。

図7:プロセスのアドレス空間

図7:プロセスのアドレス空間

◆ テキスト・セグメント

テキスト・セグメントは、 機械語命令を置く領域、すなわち実行コードがある場所です。

このセグメントは、普通読み出し専用で、 同じロードモジュールのプロセス間で共有されます。 例えばシェルは複数のユーザが同時に使いますが、 この場合メモリ上のテキスト・セグメントは1セットだけです。

◆ データ・セグメント

データ・セグメントは、 データを置く領域です。

C 言語の 静的変数 (static)大域変数 (extern)malloc() で割り当てたヒープ上の変数は、 このセグメントに割り付けられます。
( 自動変数(auto) は、次のスタック・セグメントに割り付けられます。)

データ・セグメントは、次の3つの部分で構成されます。

データ
初期値つき静的変数や大域変数が割り付けられます。
データの初期値は、実行可能ファイルに入っています。
         static int x=100 ;
         
実行可能ファイル内の x には 100 という整数値のビットパターンが入っています。

BSS

(Block
Started by
Symbol)

初期値を指定しない静的変数や大域変数が割り付けられます。
(初期値は 0)
         static int y ;
         
実行可能ファイルには、BSS の領域はありません。
y は実行時にカーネルが BSS に割り当て、値を 0 に初期化します。
ヒープ

heap

malloc() で確保する変数の領域本体が割り付けられます。
ヒープは実行時に大きさが決まる変数を確保するための領域です。

         char *p ;
         
         p = malloc( 100 );
         
100バイトの領域が、malloc() 実行後、ヒープ上に確保され、 そのアドレスが p に入ります。

ヒープ領域の大きさは、brk() システム・コールや sbrk() システム・コールで変えられます。
これらのシステム・コールは、 malloc() ライブラリ関数からも呼び出されます。

◆ スタック・セグメント

スタック・セグメントは、 スタックとして使うメモリ領域です。

C 言語の自動変数(auto variable) 、関数への引数、関数呼び出しの戻り番地は、ここに割りつけられます。

C 言語で以下のようにすると、 4バイトの領域がスタック・セグメントに割りつけられます。
auto というキーワードは、普通省略します。

main() {
    auto int z;
}

スタック・セグメントは、普通、アドレス 0xffffffff (32ビットのアドレス空間の場合) から小さい方へ伸びていきます。 この部分は、関数呼び出しが続くと伸びます。 カーネルは、割り当てられた領域を越えると自動的に拡張します。

◆ mainの引数と環境変数

プログラムを実行すると、指定した 引数(argument)環境変数(environment variable) がプログラムに渡されます。

C 言語では、次のように main()関数は3つの引数で呼び出されます。

main( int argc; char *argv[], *envp[] ) {

}
ここで、 argc は引数の数(argvの数)、 argv は引数文字列のベクタの先頭番地、 envp は環境変数のベクタの先頭番地です。

この引数と環境変数は、スタック・セグメントのスタックの底、 図7 では上にあります
(アドレスが大きいところ)。 また、extern char **environ で参照することも可能です。




◆ 共有ライブラリ

ライブラリは、静的にリンクするものと、動的にリンクするものとがあり、 後者を共有ライブラリと言います。

■ 静的リンク・ライブラリ

何百ものCのライブラリ関数がオペレーティング・システムにより提供されています。 これらのライブラリは、コンパイル、リンク時に実行可能ファイルに連結されます。

リンク・ローダは、参照しているオブジェクトがプログラム・コードにない場合、 ライブラリのリストを探し、 見つかれば実行可能ファイルに、それを連結します。 これをプログラムの全てのオブジェクトに対して行います。

しかし、この手法にはいくつか問題点があります。

  1. オブジェクトが実行可能ファイル毎に複製されるので、 ディスク空間を多量に浪費してしまう。
  2. 複数のプロセスが、 同一の関数を異なるテキスト・セグメントにロードして実行するので、 メモリを不必要に浪費してしまう。
  3. ライブラリ関数を変更した場合、 それを使うすべての実行可能ファイルは、再リンクしなければならない。

このライブラリは、ar コマンドで 複数のオブジェクトファイルをアーカイブして作り、 拡張子は .a です。

通常、システムの c ライブラリ関数は、/usr/lib/libc.a にあります。

■ 動的リンク共有ライブラリ

共有ライブラリは、プロセスのアドレス空間のどこにでもマップでき、 最小の再配置で実行できる位置独立コードで構成され、 複数のプロセスで実行時に共有されます。

リンカが、 オブジェクト・ファイルとライブラリから実行可能ファイル (a.out) を生成するとき、共有ライブラリから出力ファイルへはテキスト (コード) またはデータをコピーしません。 その代わりに、ダイナミック・ローダが、 実行時にそのライブラリをプロセスのアドレス空間にマップします。

リンカは、プログラムから共有ライブラリ・ルーチンおよびデータへの参照を、 リンケージ・テーブルのエントリにバインドするだけです。 一方、ダイナミック・ローダは、 ライブラリがマップされたときにリンケージ・テーブルのエントリを、 共有ライブラリの関数のアドレスで埋めます。 このリンケージ・テーブルは、関数呼び出しのジャンプ・テーブルとして働きます。

ダイナミック・ローダは、 Solaris では libdl.so、HP-UX では dld.sl で、 それ自身も、共有ライブラリです。

ロード

不完全な実行可能ファイルには、 リンク時に検索した共有ライブラリのパス名リストがあります。 実行時、ダイナミック・ローダは、 プロセスとプログラムにリンクされたすべての共有ライブラリをつなげます。 またダイナミック・ローダは、 リンク時に検出した共有ライブラリでロードされていないものを、ロードします。

共有ライブラリのテキスト・セグメントは、 それを使用するすべてのプロセス間で共有されます。 データ・セグメントおよび BSS セグメントは、ページ単位で共有されます。

動的リンク共有ライブラリを使用する場合、 ライブラリ全体が、プロセスの仮想アドレス空間にマッピングされます。 しかし、ライブラリのテキスト・セグメントは、 プロセス毎に異なった仮想アドレスにマップされるかもしれないが、 そのコピーはメモリに1つしかありません。

プログラムが最初に共有ライブラリのデータまたは BSS ページをアクセスした時点で (読み込みまたは書き込み)、 そのページのコピーが、プロセスのために作成されます。 これをコピー・オン・ライトといいます。

プロセスが共有オブジェクトのページに書き込みを行った場合、 そのページのほかの利用者に影響を与えずに、 その内容を自由に書きかえることができるように、 システムはそのプロセスにページのコピーを割り当てます。

共有ライブラリは、ヒープとスタック・セグメントの間にありますが、 どの番地に割り当てられるかは、システムに依存します。

この動的リンク共有ライブラリの利点としては、次のものが挙げられます。

  1. ライブラリはディスクにひとつだけで良く、 他のライブラリや実行可能ファイルには複製されません。
  2. メモリ中の共有ライブラリのテキストは1つだけです。
  3. テキストやデータのシンボルは、 実行時動的に割り当てられる仮想アドレスに配置されます。
    実行時のプロセスの絶対アドレスは、 ダイナミック・リンク・ローダによって計算されます。
  4. 共有オブジェクトの外部参照は、実行時に解決されるので、 それに依存している実行可能ファイルを、 再リンクする必要がありません。
  5. 共有ライブラリのテキストとデータのメモリ・ページは、 他のメモリ・ページがページングされるのと同様に、 ページ・インやページ・アウトできます。

UNIX では、プロセス間で仮想メモリを共有するシステム・コールである mmap() を使用し、共有ライブラリをマップします。

■ 共有ライブラリの作成とリンク

共有ライブラリは、 cc コマンドから実行される ld コマンドが作成します。
一般的に拡張子は、.so ですが、HP-UX では、.sl です。

cc あるいは ld コマンドのオプションとしては、Solaris/HP cc では -G で、 Linux などで使われる gcc では -G も互換性のため使える場合もありますが、 -shared オプションがそれにあたります。

共有ライブラリの作成


cc -G XXX.c -o 共有ライブラリ名                         (Solaris/HP cc)
cc -G -xarch=v9 -xcode=pic32 XXX.c -o 共有ライブラリ名  (Solaris 64bit module)
gcc -shared XXX.c -o 共有ライブラリ名                   (gcc)

共有ライブラリとのリンク

共有ライブラリとのリンクには、2つの方法があります。

  1. 共有ライブラリの絶対パスを指定する
  2. 共有ライブラリの検索パスを指定する


[Solaris cc]
> cc オブジェクト名リスト -ldl フルパス指定の共有ライブラリ名
> cc オブジェクト名リスト -ldl -L 共有ライブラリの検索パス -l共有ライブラリ
[HP-UX]
> cc オブジェクト名リスト -ldld フルパス指定の共有ライブラリ名
> cc オブジェクト名リスト -ldld -L 共有ライブラリの検索パス -l共有ライブラリ

後者のリンク方法を使った場合、 プログラムを実行するときは、 LD_LIBRARY_PATH に共有ライブラリ検索パスを設定します。




◆ 共有メモリ

共有メモリは、1つのデータ・セグメントを2つ以上のプロセス間で共有することです。 もし、あるプロセスが共有メモリを読んでいるなら、 他のプロセスは、それへの書きこみは終了するのを待つ、 機構を作る必要があります。

共有メモリは、 プロセス間通信として最もシンプルで高速なもので、 メッセージやフラグとして使用するの便利です。

UNIX では、共有メモリを含むプロセス間通信機能をまとめて IPC(Inter Process communication) として管理しており、 メッセージキュ、共有メモリ、セマフォが含まれます。

ipcs コマンドで、それらの使用状況をみれます。

■ 使用方法

共有メモリの使用方法は、

■ 注意事項

共有メモリは無限にあるわけではありません。 OSにもよりますが、共有メモリのサイズは OS起動時に固定されます。 カーネル・チューニング・パラメータで変更することは可能ですが、 メモリ内の一部を占有することになりますので、 あまり大きく確保すると、プロセス用のメモリが減ります。

Solaris の場合、チューニング・パラメータは /etc/system にあり、 これを変更した場合は、リブートする必要があります。




◆ /proc について

Linux と Solaris では /proc というディレクトリがあります。 これは現在動いているプロセスのための擬似ディレクトリです。

この直下にある数字から成るディレクトリは PID (Process IDentifier) に対応するもので、 カーネルにあるプロセス制御テーブルを抽象化して見せるインタフェースです。 従って、サイズはディスク上のファイルではないため、0です。 そして、これらの擬似ファイルはカーネルの管理下にありますので、 削除できません。

プロセスが終了すると、/proc 配下からもなくなります。

PID を指定してこれらの情報を操作するツールがいくつかあります。 それらは、/usr/proc/bin/* (Solaris 2.5 以上)にあります。

なお、 /proc の構造体は /usr/include/procfs.h に定義されています。(solaris)


nagano-58] df -k
Filesystem           Kバイト    used   avail capacity  Mounted on
/dev/dsk/c0t0d0s0     192799   18776  154744    11%    /
/dev/dsk/c0t0d0s6    1015679  615130  339609    65%    /usr
/proc                      0       0       0     0%    /proc
fd                         0       0       0     0%    /dev/fd
/dev/dsk/c0t0d0s1     145135   55799   74823    43%    /var
/dev/dsk/c0t0d0s7    1083751       9 1029555     1%    /home0




◆ プロセスのメモリマップ

solaris 8 では SGA は Shared に含まれるように変更されました。 solaris 7 までは Private に含まれていました。

oracle の全プロセスが占めるメモリの概算は以下のとおりです。

oracle プロセスメモリ = Private * (oracle プロセス数) + Shared (+ PGA)

92032dio-5 9:44am] pmap -x 21701
21701:  ora_cjq0_ora92032
 Address   Kbytes Resident Shared Private Permissions       Mapped File
00010000   49136   25304   25304       - read/exec         oracle        text segment
0301A000     488     360     312      48 read/write/exec   oracle        data segment
03094000     288     160     120      40 read/write/exec     [ heap ]
20000000  593920  593920  593920       - read/write/exec/shared  [ ism shmid=0x141e5 ]    SGA 
FEB00000       8       8       8       - read/write          [ anon ]
FEB10000       8       8       8       - read/write          [ anon ]
FEB50000       8       8       -       8 read/write          [ anon ]
FEB58000      48      48       8      40 read/write          [ anon ]
FEB68000      32      32       -      32 read/write          [ anon ]
FEBB0000      16      16      16       - read/exec         libmp.so.2
FEBC4000       8       8       8       - read/write/exec   libmp.so.2
FEBD0000      88      72      72       - read/exec         libm.so.1
FEBF4000       8       8       8       - read/write/exec   libm.so.1
FEC00000    4416    1216    1216       - read/exec         libjox9.so
FF05E000     192     168     168       - read/write/exec   libjox9.so
FF0A0000       8       8       8       - read/exec         libkstat.so.1
FF0B2000       8       8       8       - read/write/exec   libkstat.so.1
FF0C0000      24      24      24       - read/exec         librt.so.1
FF0D6000       8       8       8       - read/write/exec   librt.so.1
FF0E0000      32      24      24       - read/exec         libaio.so.1
FF0F8000       8       8       8       - read/write/exec   libaio.so.1
FF100000     688     632     632       - read/exec         libc.so.1
FF1BC000      32      32      16      16 read/write/exec   libc.so.1
FF1D0000       8       8       8       - read/write/exec     [ anon ]
FF1E0000       8       8       8       - read/exec         libsched.so.1
FF1F2000       8       8       8       - read/write/exec   libsched.so.1
FF200000     568     392     392       - read/exec         libnsl.so.1
FF29E000      40      40      40       - read/write/exec   libnsl.so.1
FF2A8000      24      16      16       - read/write/exec   libnsl.so.1
FF2C0000      16      16      16       - read/exec         libc_psr.so.1
FF2D0000       8       8       8       - read/write/exec/shared   [ anon ]
FF2E0000       8       8       -       8 read/write/exec     [ anon ]
FF2F0000      24      24      24       - read/exec         libgen.so.1
FF306000       8       8       8       - read/write/exec   libgen.so.1
FF310000      40      40      40       - read/exec         libsocket.so.1
FF32A000       8       8       8       - read/write/exec   libsocket.so.1
FF330000       8       8       8       - read/exec         libskgxn9.so
FF340000       8       8       8       - read/write/exec   libskgxn9.so
FF350000       8       8       8       - read/exec         libskgxp9.so
FF360000       8       8       8       - read/write/exec   libskgxp9.so
FF370000       8       8       8       - read/exec         libodmd9.so
FF380000       8       8       8       - read/write/exec   libodmd9.so
FF390000       8       8       8       - read/exec         libdl.so.1
FF3A0000       8       8       -       8 read/write/exec     [ anon ]
FF3B0000     160     160     160       - read/exec         ld.so.1
FF3E8000       8       8       -       8 read/write/exec   ld.so.1
FFBE8000      32      32      16      16 read/write/exec     [ stack ]     stack segment
--------  ------  ------  ------  ------
total Kb  650504  622928  622704     224

上記の出力項目の説明は以下のとおりです。単位は KB です。

項目名説          明
Address 仮想メモリ上のアドレス (*)
Kbytes 全サイズ
Resident 常駐部分のサイズ
Shared 共有部分のサイズ
Private 個別部分のサイズ
Permissions メモリ属性
read/exec テキストセグメント(命令部分)
read/write/exec データセグメントまたはスタックセグメント(データ領域)
read/write/exec/shared 共用メモリ
Mapped File マップしているファイル名

(*) 仮想メモリ上のアドレスとは、実行形式を全メモリに展開した場合のアドレスです。 UNIX のメモリ管理は、仮想記憶という概念を利用していますので、 仮想という表現しますが、これらは実際には存在しません。




◆ システムのメモリ使用量を知る:vmstat

プロセスがスムーズに動作しているかを知るには、 定時的に vmstat の情報を分析する必要があります。 vmstat は、プロセス、仮想メモリ、トラップ、CPUアクティビティの 正確な統計情報をレポートします。 特に注目すべき項目は赤で示します。

vmstat - 仮想メモリ統計情報のレポート

Solaris の場合の各カラムの見出しおよびその意味は、以下のとおりです。

procs 各状態にあるプロセス数に関する情報

r 実行待ち行列にあるプロセス数

b 入出力待ちでブロックされたプロセス数 (I/O、ページングなどのリソース)
プロセス数が多いことは IOwait のプロセスが多いことです

w スワップアウトされたプロセス数    パフォーマンスが低下しています
memory 仮想メモリおよび実メモリの使用状況に関する情報。 仮想ページは、実行中であるか、または最後の 20 秒間動作しているプロセスに属する場合、アクティブであるとされる (単位 KB)

swap 使用可能な未予約スワップ空間サイズ。    数 MB になると、プロセス起動不可

free 空き記憶領域
page ページフォルトとページングに関する情報(単位秒)    メモリが不足すると po と sr が多くなる

re フリーリストから利用されたページ数

mf 軽度のページフォルト数(ページはメモリ内にあるが、マップされていない)

pi ファイルシステムまたはスワップからページ・インされたページ(キロバイト)
ページ・インが多いとパフォーマンスが低下します

po ファイルシステムまたはスワップへページ・アウトされたページ(キロバイト)

fr 削除または解放されたページ(キロバイト)

de 書きこみ後に解放されたページ数        0 以外だとメモリ消費速度が速い

sr ページ・デーモンがスキャンしたページ数    長時間0以外であると、メモリ不足です
disk ディスク動作に関する情報(単位秒)

f0 ディスク入出力数

s1 ディスク入出力数

s2 ディスク入出力数

s3 ディスク入出力数
faults トラップ/割込み率(単位秒)

in デバイス割込み回数

sy システム・コール回数

cs cpu コンテクスト切り替え回数
cpu CPU の使用割合

us ユーザ

sy システム

id アイドル状態

下の例は、vmstat の出力例です。


ozzy-19] vmstat 5 5
 procs     memory            page            disk          faults      cpu
 r b w   swap  free  re  mf pi po fr de sr f0 s1 s1 s1   in   sy   cs us sy id
 0 0 0  14008 21664   0   8  2  7 10  0  0  0  0  1  0  441 4008  611  1  2 98
 0 0 0 2109624 52208  0   1  0  0  0  0  0  0  0  1  1  427 4367  729  0  1 98
 0 0 0 2109624 52208  0   0  0  0  0  0  0  0  0  1  9  482 4349  730  0  1 98
 0 0 0 2109624 52200  0   0  1  0  0  0  0  0  5  1  5  497 4389  735  0  4 96
 0 0 0 2109592 52168  0   0  0  0  0  0  0  0  1  1 39  657 4393  728  0  3 97




プログラム例

    ■ 例1:メモリ・マップを調べる

    mem.c は、初期値つき静的変数や大域変数、初期値を指定しない静的変数や大域変数、 malloc() した領域などのマップ・アドレスを表示するプログラムです。

    
    /* 
       Function : print each addresses
       FileName : mem.c
    */
    #include <stdlib.h>
    #include <dlfcn.h>
    #include <unistd.h>
    #define SZ_LOOP    4
    #define SZ_ALLOCA  512
    
    void prthline(char),prtOSinfo(),recursive(int);
    extern _etext,_edata,_end;
    
    /* libc shared_library_name        */
    #ifdef HPUX
    char *libc = "/usr/lib/libc.sl";
    #define OS_NAME  "HP-UX"
    #else
    #ifdef LINUX
    char *libc = "/lib/libc.so.6";
    #define OS_NAME  "Linux"
    #else
    #ifdef SUN
    char *libc = "/usr/lib/libc.so";
    #define OS_NAME  "SunOS"
    #else
    char *libc = NULL;
    #define OS_NAME  "Unknown OS.  cc -DSUN/HPUX/LINUX"
    #endif       /* SUN */
    #endif       /* Linux */
    #endif       /* HP */
    
    int x1=1 ;
    int pg ;
    char *prgname;
    
    int main(int argc,char *argv[],char *envp[] ) {
       int  i,x3 ;
       char *x4p ;
       void *hdl,*vp=0;
    
       prgname = argv[0];
       if (libc == NULL)    {
          printf("%s\n",OS_NAME);
          exit(12);
       }
    /* get address of malloc() in libc shared_library     */
       hdl = dlopen(libc,RTLD_LAZY);
       if (! hdl)  printf(" dlopen : %s\n",dlerror());
       else {
         vp  = dlsym(hdl,"malloc");
         if (! vp)  printf(" dlsym : %s\n",dlerror());
       }
       prtOSinfo();
       prthline('=');
       printf("         0  |                      \n" );
       printf("            | &main   = 0x%08x\t(text) \n",&main );
       printf("     Etext  |           0x%08x\t(text_end)\n",&_etext );
       prthline('=');
       printf("     Data   | &x1     = 0x%08x\t(data)\n",&x1 );
       if (&pg < &_edata) {
          printf("     BSS    | &pg     = 0x%08x\t(bss)\n",&pg );
          printf("     Edata  |           0x%08x \n",&_edata );
       }
       else {
          printf("     Edata  |           0x%08x \n",&_edata );
          printf("     BSS    | &pg     = 0x%08x\t(bss)\n",&pg );
       }
       printf("     End    |           0x%08x \n",&_end );
       prthline('=');
       printf("     Heap   | \n" );
    
       for (i=0; i<SZ_LOOP; i++) {
          x4p = malloc(pg);     /* allocate memory from the heap     */
          printf("            | x4p_%d   = 0x%08x\t(heap)\n",i,x4p );
       }
       prthline('=');
       printf("     ShrL   | &malloc = 0x%08x\t(shr_library)\n",vp);
       prthline('=');
       recursive( 3 );
       printf("            |          :\n" );
       printf("            | &x3     = 0x%08x\t(auto)\n",&x3 );
       printf("            | &argc   = 0x%08x\t(auto)\n",&argc );
       printf("            | &argv   = 0x%08x\t(auto)\n",&argv[0] );
       printf("            | &envp   = 0x%08x\t(auto)\n",&envp[0] );
       printf("     Stack  | \n");
       prthline('=');
       exit(0);
    }
    
    void recursive(int n) { 
       int x5 ;
       void *vp;
    
       vp  = alloca(SZ_ALLOCA);      /* allocate memory from the stack   */
       printf("            | &n      = 0x%08x\t(auto,%d)\n",&n,n );
       printf("            | &x5     = 0x%08x\t(auto,%d)\n",&x5,n );
       printf("            | vp      = 0x%08x\t(auto,%d)\n",vp ,n );
       prthline('-');
       if  ( n<=0 ) return;
       recursive( n-1 );
    }
    
    #define SZ_CNT   45
    void prthline(char ch) {
       int  i;
    
       printf("            *");
       for (i=0; i<SZ_CNT; i++)   printf("%c",ch);
       printf("\n");
    }
    
    void prtOSinfo() {
    
    /* get page_size           */
       pg = (int) sysconf(_SC_PAGESIZE);
       if (pg >=0 ) {
          printf("\tOS name : %s\t\tPage_size : %ld(0x%04x) Bytes\n\n",
          OS_NAME,pg,pg);
       } else  perror(prgname);
    }
    
    > gcc -c -g -DSUN mem.c > gcc -o mem mem.o -ldl > mem OS name : SunOS Page_size : 8192(0x2000) Bytes *============================================= 0 | | &main = 0x00010a90 (text) Etext | 0x000113df (text_end) *============================================= Data | &x1 = 0x0002158c (data) Edata | 0x000215a8 BSS | &pg = 0x000215c8 (bss) End | 0x000215cc *============================================= Heap | | x4p_0 = 0x000215d8 (heap) | x4p_1 = 0x000235e0 (heap) | x4p_2 = 0x000255e8 (heap) | x4p_3 = 0x000275f0 (heap) *============================================= ShrL | &malloc = 0xff2c56e4 (shr_library) *============================================= | &n = 0xffbef3cc (auto,3) | &x5 = 0xffbef374 (auto,3) | vp = 0xffbef170 (auto,3) *--------------------------------------------- | &n = 0xffbef154 (auto,2) | &x5 = 0xffbef0fc (auto,2) | vp = 0xffbeeef8 (auto,2) *--------------------------------------------- | &n = 0xffbeeedc (auto,1) | &x5 = 0xffbeee84 (auto,1) | vp = 0xffbeec80 (auto,1) *--------------------------------------------- | &n = 0xffbeec64 (auto,0) | &x5 = 0xffbeec0c (auto,0) | vp = 0xffbeea08 (auto,0) *--------------------------------------------- | : | &x3 = 0xffbef3f8 (auto) | &argc = 0xffbef454 (auto) | &argv = 0xffbef474 (auto) | &envp = 0xffbef47c (auto) Stack | *=============================================

    ■ 例2:共有ライブラリの作成

    base.c と main.c は、以下のとおりです。
    base.o から共用ライブラリと静的ライブラリを作ります。
    mains は共用ライブラリとリンクしますが、maina は静的ライブラリとリンクします。

    
    /*
       Function  : print each addresses
       FileName  : main.c
    */
    #include <stdio.h>
    #include <dlfcn.h>
    extern int etext, edata, end ;
    extern void prthline();
    extern int base();
    #define SZ_BUF  0x100
    
    /* libc shared_library_name        */
    #ifdef HPUX
    char *libc = "/usr/lib/libc.sl";
    #else
    #ifdef LINUX
    char *libc = "/lib/libc.so.6";
    #else
    #ifdef SUN
    char *libc = "/usr/lib/libc.so";
    #endif      /* SUN */
    #endif      /* Linux */
    #endif      /* HPUX  */
    
    static char *head1=
    "          *------ main        address   (size)  ---------------\n       0  |                      \n";
    
    int main(int argc,char *argv[],char *envp[]) {
       char  buf[SZ_BUF];
       int   *fp2;
       void  *vp=0,*hdl;
    
       fp2 = (int *)&base;
    /* get address of malloc() in libc shared_library     */
       hdl = dlopen(libc,RTLD_LAZY);
       if (! hdl)  printf("dlopen : %s\n",dlerror());
       else {
          vp = dlsym(hdl,"printf");
          if (! vp)  printf("dlsym : %s\n",dlerror());
       }
       printf(head1);
       printf("          | &main           = 0x%08x\t\t(text) \n",&main );
       printf("          | &base           = 0x%08x\t\t(text) \n",fp2 );
       printf("   Etext  |                   0x%08x\t\t(text_end)\n",&etext );
       prthline();
       printf("   Data   | \n");
       printf("   BSS    | &head1          = 0x%08x\t\t(bss)\n",head1 );
       printf("   Edata  |                   0x%08x \n",&edata );
       printf("   End    |                   0x%08x \n",&end );
       prthline();
       printf("   ShrL   | &printf         = 0x%08x\t\t(shr_library)\n",vp);
       prthline();
       printf("          | &buf            = 0x%08x(0x%04x)\t(auto)\n",
               &buf[0],sizeof(buf) );
       printf("          | &argc           = 0x%08x(0x%04x)\t(auto)\n",
               &argc ,sizeof(argc));
       printf("          | &argv           = 0x%08x\t\t(auto)\n",&argv[0] );
       printf("          | &envp           = 0x%08x\t\t(auto)\n",&envp[0] );
       printf("   Stack  | \n");
       prthline();
    
       base();
       printf("\nInput return_key , then end");
       fgets(buf,SZ_BUF,stdin);
       exit(0);
    }
    
    /* Function : print base addresses FileName : base.c */ #include <stdio.h> #define SZ_BUF 0x100 void prthline(); static char *head1="\n *------ base address (size) ---------------\n" ; int static_data = 0; int global_area1 [SZ_BUF]; int global_area2 [SZ_BUF]; int base() { int stack; printf(head1 ); printf(" shr_data | &static_data = 0x%08x(0x%04x)\t(shr_data)\n", &static_data,sizeof(static_data)); printf(" shr_bss | &global_area1 = 0x%08x(0x%04x)\t(shr_bss)\n", &global_area1[0],sizeof(global_area1)); printf(" | &global_area2 = 0x%08x(0x%04x)\t(shr_bss)\n", &global_area2[0],sizeof(global_area2)); prthline(); printf(" | &stack = 0x%08x\t\t(auto)\n",&stack); printf(" Stack | \n"); prthline(); return (0); } void prthline() { printf(" *----------------------------------------------------\n" ); }
    > gcc -G base.c -o libmylib.so > gcc -c -g -DSUN main.c > gcc -o mains main.o -ldl /home06/taka/cc/mem/libmylib.so > ldd mains libdl.so.1 => /usr/lib/libdl.so.1 /home06/taka/cc/mem/libmylib.so libc.so.1 => /usr/lib/libc.so.1 /usr/platform/SUNW,Ultra-4/lib/libc_psr.so.1
    > gcc -c -g base.c > ar -rcv libmylib.a base.o > gcc -c -g -DSUN main.c > gcc -o maina main.o /home06/taka/cc/mem/libmylib.a -ldl > ldd maina ldd maina libdl.so.1 => /usr/lib/libdl.so.1 libc.so.1 => /usr/lib/libc.so.1 /usr/platform/SUNW,Ultra-4/lib/libc_psr.so.1
    ozzy-21] mains *------ main address (size) --------------- 0 | | &main = 0x00010a24 (text) | &base = 0x000210a4 (text) Etext | 0x00011015 (text_end) *---------------------------------------------------- Data | BSS | &head1 = 0x00010d28 (bss) Edata | 0x000211e0 End | 0x00021340 *---------------------------------------------------- ShrL | &printf = 0xff303370 (shr_library) *---------------------------------------------------- | &buf = 0xffbef300(0x0100) (auto) | &argc = 0xffbef454(0x0004) (auto) | &argv = 0xffbef474 (auto) | &envp = 0xffbef47c (auto) Stack | *---------------------------------------------------- *------ base address (size) --------------- shr_data | &static_data = 0xff380e34(0x0004) (shr_data) shr_bss | &global_area1 = 0xff380e64(0x0400) (shr_bss) | &global_area2 = 0xff381264(0x0400) (shr_bss) *---------------------------------------------------- | &stack = 0xffbef27c (auto) Stack | *---------------------------------------------------- Input return_key , then end
    ozzy-19] maina *------ main address (size) --------------- 0 | | &main = 0x00010aa8 (text) | &base = 0x00010cf4 (text) Etext | 0x000112e1 (text_end) *---------------------------------------------------- Data | BSS | &head1 = 0x00010e78 (bss) Edata | 0x00021494 End | 0x00021df8 *---------------------------------------------------- ShrL | &printf = 0xff303370 (shr_library) *---------------------------------------------------- | &buf = 0xffbef300(0x0100) (auto) | &argc = 0xffbef454(0x0004) (auto) | &argv = 0xffbef474 (auto) | &envp = 0xffbef47c (auto) Stack | *---------------------------------------------------- *------ base address (size) --------------- shr_data | &static_data = 0x0002147c(0x0004) (shr_data) shr_bss | &global_area1 = 0x000214b4(0x0400) (shr_bss) | &global_area2 = 0x000218b4(0x0400) (shr_bss) *---------------------------------------------------- | &stack = 0xffbef27c (auto) Stack | *---------------------------------------------------- Input return_key , then end
    ozzy-13] /usr/proc/bin/pmap -x 15209 15209: maina Address Kbytes Resident Shared Private Permissions Mapped File 00010000 8 8 8 - read/exec maina 00020000 8 8 - 8 read/write/exec maina FF280000 664 656 656 - read/exec libc.so.1 FF334000 32 32 - 32 read/write/exec libc.so.1 FF33C000 8 8 - 8 read/write/exec [ anon ] FF370000 8 8 - 8 read/write/exec [ anon ] FF380000 16 16 16 - read/exec libc_psr.so.1 FF3A0000 8 8 8 - read/exec libdl.so.1 FF3B0000 128 128 128 - read/exec ld.so.1 FF3DE000 16 16 - 16 read/write/exec ld.so.1 FFBEC000 16 16 - 16 read/write/exec [ stack ] -------- ------ ------ ------ ------ total Kb 912 904 816 88
    ozzy-17] /usr/proc/bin/pmap -x 15234 15234: mains Address Kbytes Resident Shared Private Permissions Mapped File 00010000 8 8 8 - read/exec mains 00020000 8 8 - 8 read/write/exec mains FF280000 664 656 656 - read/exec libc.so.1 FF334000 32 32 - 32 read/write/exec libc.so.1 FF33C000 8 8 - 8 read/write/exec [ anon ] FF350000 16 16 16 - read/exec libc_psr.so.1 FF360000 8 8 - 8 read/write/exec [ anon ] FF370000 8 8 - 8 read/exec libmylib.so FF380000 8 8 - 8 read/write/exec libmylib.so FF3A0000 8 8 8 - read/exec libdl.so.1 FF3B0000 128 128 128 - read/exec ld.so.1 FF3DE000 16 16 - 16 read/write/exec ld.so.1 FFBEC000 16 16 - 16 read/write/exec [ stack ] -------- ------ ------ ------ ------ total Kb 928 920 816 104

    ■ 例3:共有メモリの作成と使用法

    非常に単純なサンプルですが、

    送信プログラムを実行するには、受信プログラムが起動時に表示する、 共有メモリ ID を、引数として指定する必要があります。

    
    /*
       Function : create shared_memory and read it
       FileName : shmc.c
    */
    #include    <stdio.h>
    #include    <sys/types.h>
    #include    <sys/ipc.h>
    #include    <sys/shm.h>
    #define SZ_SHR  0x200
    void die_err(char *);
        
    int main()
    {
        int   id;      /* id of shared_memory      */
        char  *adr;    /* body of  shared_memory   */
        
        if ((id=shmget(IPC_PRIVATE,SZ_SHR,IPC_CREAT|0666))==-1) {
            die_err("shmget");
        }
        
        if ((int)(adr=shmat(id,0,0)) == -1) {
            die_err("shmat");
        }
        else {
            printf("shared_buf    = 0x%08x\n\n",adr);
            printf("共有メモリ ID = %d\n",id);
            while(1) {
                printf("%s",adr);
                if (strcmp(adr,"end\n") == 0)  break ;
                sleep(3);
            }
            if (shmdt(adr) == -1)  die_err("shmdt");
        }
        if (shmctl(id,IPC_RMID,0) == -1) {
            die_err("shmctl");
        }
    }
    
    void die_err(char *msg) {
       perror(msg);
       exit(1);
    }
    
    /* Function : get shared_memory and write strings to it FileName : shm.c */ #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> void die_err(char *); #define SZ_BUF 0x200 /* sizeof(shared_memory) */ int main(int argc,char *argv[]) { int id; /* id of shared_memory */ char *adr; /* body of shared_memory */ char *cp; if (argc <= 1) { die_err("Usage : shm ID\n"); } id=atoi(argv[1]); if ((int)(adr=shmat(id,0,0)) == -1){ die_err("shmat"); } else { printf("shared_buf = 0x%08x\n",adr); while(1) { cp = fgets(adr,SZ_BUF,stdin); if (strcmp(cp,"end\n") == 0) break; } if (shmdt(cp) == -1) { die_err("shmdt"); } exit (0); } } void die_err(char *msg) { perror(msg); exit(2); }
    ozzy-6] /usr/proc/bin/pmap -x 361 361: shmc Address Kbytes Resident Shared Private Permissions Mapped File 00010000 8 8 8 - read/exec shmc 00020000 8 8 - 8 read/write/exec shmc FF280000 664 640 640 - read/exec libc.so.1 FF334000 32 32 - 32 read/write/exec libc.so.1 FF33C000 8 8 - 8 read/write/exec [ anon ] FF370000 8 8 - 8 read/write/exec [ anon ] FF380000 16 16 16 - read/exec libc_psr.so.1 FF390000 8 8 - 8 read/write/exec/shared [shmid=0x19d] FF3A0000 8 8 8 - read/exec libdl.so.1 FF3B0000 128 128 128 - read/exec ld.so.1 FF3DE000 16 16 - 16 read/write/exec ld.so.1 FFBEC000 16 16 - 16 read/write/exec [ stack ] -------- ------ ------ ------ ------ total Kb 920 896 800 96
    ozzy-7] /usr/proc/bin/pmap -x 362 362: shm 413 Address Kbytes Resident Shared Private Permissions Mapped File 00010000 8 8 8 - read/exec shm 00020000 8 8 - 8 read/write/exec shm FF280000 664 640 640 - read/exec libc.so.1 FF334000 32 32 - 32 read/write/exec libc.so.1 FF33C000 8 8 - 8 read/write/exec [ anon ] FF370000 8 8 - 8 read/write/exec [ anon ] FF380000 16 16 16 - read/exec libc_psr.so.1 FF390000 8 8 - 8 read/write/exec/shared [shmid=0x19d] FF3A0000 8 8 8 - read/exec libdl.so.1 FF3B0000 128 128 128 - read/exec ld.so.1 FF3DE000 16 16 - 16 read/write/exec ld.so.1 FFBEC000 16 16 - 16 read/write/exec [ stack ] -------- ------ ------ ------ ------ total Kb 920 896 800 96

    ■ 例4:セマフォによるロックメカニズム

    セマフォの初期値は 1 で、最初にやって来たプロセスは、 そのカウントが正であることを確認し、そこから 1 を引いて 0 にします。 これでプロセスは、コードかリソースの重要部分を占有したことになり、 それがセマフォによって保護されます。 プロセスがその重要な領域を離れるとき、 セマフォのカウントをインクリメントします。

    最善のケースは、その重要領域を所持しようとする他のプロセスが存在しない場合です。

    下記の例では、LOCK/LOCK するとデッドロックを体験できます。

    
    #include    <stdio.h>
    #include    <sys/types.h>
    #include    <sys/ipc.h>
    #include    <sys/sem.h>
    #define SZ_BUF  128
    
    union semun  {
        int   val;
        struct semid_ds *buf;
        ushort *array;
    }   c_arg;               /* semaphore  control info   */
        
    int sem_get(char *path) {
        int   sid;
    
        if ((sid=semget(ftok(path,0),1,0666|IPC_CREAT))== -1) {
            die_err("semget");
        }
    
        if (semctl(sid,0,GETVAL,c_arg)==0) {
            c_arg.val=1;
            if (semctl(sid,0,SETVAL,c_arg)==-1) {
                die_err("semctl");
            }
        } 
        else  die_err("semctl");
        return(sid);
    }
        
    int sem_lock(int sid) {
        struct sembuf  sb;
        
        sb.sem_num=0;
        sb.sem_op=-1;
        sb.sem_flg=0;
        if (semop(sid,&sb,1)== -1) {
            die_err("semop");
        }
        return(1);
    }
        
    int sem_unlock(int sid) {
        struct sembuf  sb;
        
        sb.sem_num=0;
        sb.sem_op=1;
        sb.sem_flg=0;
        if (semop(sid,&sb,1)== -1) {
            die_err("semop");
        }
        return(1);
    }
        
    int main() {
        int   sid,ret;
        char  buf[SZ_BUF];
        
        setbuf(stdout,NULL);
    
        if ((sid=sem_get("/tmp/aaa"))==-1) {
            exit(-1);
        }
        printf("sid=%d\n",sid);
        
        while(1){
            ret  = semctl(sid,0,GETVAL,c_arg);
            printf("<%d> [L]ock / [U]nlock : ",ret);
            if (fgets(buf,SZ_BUF,stdin) != NULL) {
                if (tolower(buf[0])=='l') {
                    ret = sem_lock(sid);
                    printf("sem_lock()=%d\n",ret);
                }
                else if (tolower(buf[0])=='u') {
                    ret = sem_unlock(sid);
                    printf("sem_unlock()=%d\n",ret);
                }
            }
        }
    }
    




● 関連項目 Solaris

ipcs(1),ldd(1),ld.so.1(1),cc(1),ld(1),ar(1),ipcrm(1),vmstat(1M),proc(1),pagesize(1)
mmap(2),shmctl(2), shmop(2),shmdt(2)
end(3C),malloc(3C),alloca(3C),valloc(3C),free(3C),dlopen(3X),dlsym(3X),dlerror(3X),dlclose(3X)
a.out(4),libdl(4),core(4)
environ(5)


● 関連項目 HP-UX

ipcs(1),ldd(1),ld.so.1(1),cc(1),ld(1),ar(1),ipcrm(1),vmstat(1M)
mmap(2),shmctl(2), shmop(2),shmdt(2)
end(3C),malloc(3C),alloca(3C),valloc(3C),free(3C),dlopen(3C),dlsym(3C),dlerror(3C),dlclose(3C)
a.out(4),dld.sl(4),core(4)
environ(5)