入り口に戻る

原文はこちら

翻訳にあたって、オープンソースグループ・ジャパンの MIT ライセンスの訳を参考にした。

MIT-SHM(The MIT Shared Memory Extension)(日本語訳)

共有メモリの拡張はどのように機能するのか

Jonathan Corbet

National Center for Atmospheric Research
Atmospheric Technology Division

Edited by

Keith Packard

MIT X Consortium

X Version 11, Release 7.7

Version 1.0

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium.

X Window System is a trademark of The OpenGroup.

(訳)

(以下に定める条件に従い、本ソフトウェアおよび関連文書のファイル(以下「ソフトウェア」)の複製を取得するすべての人に対し、ソフトウェアを無制限に扱うことを無償で許可する。これには、ソフトウェアの複製を使用、複写、変更、結合、掲載、頒布、サブライセンス、および/または販売する権利、およびソフトウェアを提供する相手に同じことを許可する権利も無制限に含まれる。)

(上記の著作権表示および本許諾表示を、ソフトウェアのすべての複製または重要な部分に記載するものとする。)

(ソフトウェアは「現状のまま」で、明示であるか暗黙であるかを問わず、何らの保証もなく提供される。ここでいう保証には、商品性、特定の目的への適合性、および権利非侵害についての保証も含まれるが、それに限定されるものではない。X CONSORTIUM は、契約行為、不法行為、またはそれ以外であろうと、ソフトウェアに起因または関連し、あるいはソフトウェアの使用またはその他の扱いによって生じる一切の請求、損害、その他の義務について何らの責任も負わないものとする。)

(X Consortium の名称は、この表示に記載されている場合を除き、X Consortium の事前の書面による承認を得ずに、宣伝であろうとその他の形であろうと、ソフトウェアの販売を促進するもの、またはソフトウェアの使用その他の扱いを奨励するものに使用してはならない。)

(X Window System は The OpenGroup の商標である。)

本文書は、MIT-SHM 共有メモリ拡張(MIT-SHM shared memory extension)の使い方を簡潔に説明するものである。正確な記述を心掛けたが、間違っている箇所が見つかったとしても驚きはしない。もし誤った箇所を見つけたら、訂正するつもりなので知らせてほしい。とは言っても、この文書は「現状のまま」(as is)で受け取ってほしい(eman improvement over what was there before, but certainly not the definitive word.)。(訳註:最後の一文がわからない。)


目次

1. 動作条件
2. 提供する機能
3. 共有メモリ拡張の使い方
4. 共有メモリ版 XImage の使い方
5. 共有メモリ版ピクスマップの使い方

第1章 動作環境

共有メモリ拡張は、一部の X サーバにしか実装されていない。自分のサーバが共有メモリ拡張に対応しているか否かを調べるには、xdpyinfo(1) を使用する。重要なのは、この拡張を使うには、自分のシステムが SYSV の共有メモリ・プリミティブ(機能の基本要素)を備えていなければならないということである。この拡張には、mmap に基く版は存在しない。Sun のシステムで共有メモリを使うには、カーネルを構築する際に SYSV 共有メモリを有効にしておかなければならない(これはデフォルトの構成ではない)。さらには、Sun のシステムにおいても Digital のシステムにおいても、共有メモリの最大サイズを大きくする必要がある。デフォルトのサイズではあまりに小さくて、有用な動作をすることができない。

第2章 提供する機能

提供する基本機能は、共有メモリ版 XImage である。これは、XImage のインタフェイスの形態(version)の1つであり、実際の画像データが共有メモリ・セグメントに格納されるものである。そのため、共有メモリ版 XImage は Xlib のプロセス間通信経路を通る必要がない。大きな画像の場合、この機能を用いることでかなり効率を良くすることができる。

これに加えて、共有メモリ版ピクスマップを備えた実装も存在する。共有メモリ版ピクスマップとは、X サーバが指定した形式(訳註:XYフォーマットもしくはZフォーマット)のピクセルの2次元の配列であって、画像データを共有メモリ・セグメントに格納するものである。共有メモリ版ピクスマップを使用する場合、Xlib の関数(routine)を全く使わずに(共有メモリ版の)ピクスマップの内容を変更することができる。共有メモリ版ピクスマップは、X サーバがピクスマップ・データに対して通常の仮想メモリを使える場合に限って、使用できる。逆に、ピクスマップが特別なグラフィクス・ハードウェア(magic graphics hardware)に格納されている場合、アプリケーションはサーバとピクスマップ・データを共有することはできない。xdpyinfo(1) は、この点に関する情報を教えてはくれない。

第3章 共有メモリ拡張の使い方

共有メモリ拡張を利用するコードでは、いくつかのヘッダ・ファイルを取り込まなければならない。

#include <X11/Xlib.h>          /* 当然必要 */
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>

勿論、プログラムの組み立てを行っているシステムが共有メモリに対応していない場合、XShm.h というファイルは存在しない可能性がある。#ifdefs を大いに活用してほしい。

共有メモリ拡張機能を利用するコードは、まずサーバが同拡張機能を提供しているかどうかを調べるべきである。というのは、実行環境がネット越しであったり、その他の共有メモリ拡張機能が働かない環境であったりすることもあるからである。この確認を実行するには、次のどちらかの関数を呼び出す。

Status XShmQueryExtension(Display *display);

もしくは

Status XShmQueryVersion(Display *display, int *major, int *minor, Bool *pixmaps);

ここでの display は、もちろん、あなたが使用しているディスプレイである。共有メモリ拡張が使用可能であれば、どちらの関数の戻り値も True となる。そうでなければ、プログラムは従来の Xlib 関数を使って処理を進めるべきである。共有メモリ拡張が利用可能な場合、XShmQueryVersion は major、minor、pixmaps にも値を返す。major と minor は、共有メモリ拡張の実装の版の番号である。pixmaps には、共有メモリ版ピクスマップが使える場合、True が返る。

第4章 共有メモリ版 XImage の使い方

共有メモリ版 XImage を操作する際の基本手順は以下の通り。

  1. 共有メモリ版 XImage の構造体を作成する。

  2. 画像データを格納する共有メモリ・セグメントを作成する。

  3. サーバに共有メモリ・セグメントの情報を知らせる。

  4. 通常の XImage と同じように、共有メモリ版 XImage を使用する。

共有メモリ版 XImage を作成するには、次の関数を用いる。

XImage *XShmCreateImage(Display *display, Visual *visual, unsigned int depth, int format, char *data, XShmSegmentInfo *shminfo, unsigned int width, unsigned int height);

ほとんどの引数は XCreateImage のものと同じであるので、ここで1つ1つ説明したりはしない。しかしながら、引数「offset」「bitmap_pad」「bytes_per_line」はこちらには存在しない。これらの数値はサーバ自身が定めることになり、読者のコードはその数値に従う必要がある。既に共有メモリ・セグメントを確保している場合(次に説明)の除いて、ポインタ「data」には NULL を渡す。

1つだけ「shminfo」という引数が追加されている。これは XShmSegmentInfo 型構造体へのポインタである。 プログラマはこの構造体を1つは確保しなければならず、共有メモリ型 XImage が存続する限りこの構造体も存続させなければならない。XShmCreateImage を呼び出すまでは、この構造体を初期化する必要は無い。

全て上手く行った場合、戻り値は XImage 構造体であり、以後の手順ではこの構造体を使用することができる。

次は共有メモリ・セグメントを作成する。これは XImage の作成の後に実施するのが良い。なぜかというと、どれだけのメモリを確保すれば良いのかを知るには、作成した XImage の中の情報を利用する必要があるからである。共有メモリ・セグメントを作成するには次のように関数を呼び出す。

shminfo.shmid = shmget (IPC_PRIVATE,
          image->bytes_per_line * image->height, IPC_CREAT|0777);

(共有メモリ版 XImage が image という名前の変数に入っていると仮定する。) 読者は、勿論、規則(the Rules)に従って全てのシステム・コールに対してエラー・チェックをするものとする。また、XImage を作成する時に使った width ではなく、bytes_per_line フィールドを使わなければならない。これらは互いに異なっている可能性が高いからである。

システムから返ってきた共有メモリの ID は XShmSegmentInfo 型構造体の shminfo に格納されることに注意。サーバは、共有メモリ・セグメントに接続する(attach itself)ために、この ID を必要とする。

また、多くのシステムにおいてセキュリティ上の理由から、X サーバは、共有メモリ・セグメントが「other」(作成者以外)から読み書き可能である場合に限って、同セグメントの付加(attach)を受け入れるということにも注意。X サーバがローカル・トランスポート越しに X クライアントの uid を知ることができるシステムにおいては、同クライアントの uid に依らなければ共有メモリ・セグメントを読み書きできないようにすることができる。

次は、この共有メモリ・セグメントを読者のプロセスに接続する(attach)。

shminfo.shmaddr = image->data = shmat (shminfo.shmid, 0, 0);

shmat で返るアドレスは、XImage の構造体と shminfo の構造体の両方に格納するものとする。

shminfo の構造体の情報を埋める作業の締めとして、読者は、サーバがどのように共有メモリ・セグメントに接続すれば良いのかを決める必要があり、そして以下のように「readOnly」フィールドを設定する必要がある。通常、次のようにコードを書く。

shminfo.readOnly = False;

ここに True を設定した場合、サーバはこのセグメントに書き込むことができなくなり、結果として XShmGetImage の呼び出しは失敗に終わる。

最後に、用意した共有メモリ・セグメントを接続するようサーバに命令する。これには次の関数を用いる。

Status XShmAttach (display, shminfo);

全てが上手く行った場合、Status として 0 でない値が返り、読者の XImage は使用可能になる。

共有メモリ版 XImage を X のドローアブルに書き込むには、XShmPutImage を用いる。

Status XShmPutImage (Display *display, Drawable d, GC gc, XImage *image, int src_x, int src_y, int dest_x, int dest_y, unsigned int width, unsigned int height, bool send_event);

インタフェイスは XPutImage のものと同じであるので、私はタイピングの手間を省いて X プロトコルの文書と同じ説明はしない。しかしながら、1つだけ追加の引数があり、名を「send_event」という。この引数に True を渡した場合、画像の書き込みが終了した時に、サーバは完了を告げるイベントを生成する。その結果、共有メモリ・セグメントの操作を再開しても問題ない状態になった時、読者のプログラムはそれを知ることができるようになる。

完了を告げるイベントの型は XShmCompletionEvent 型であり、定義は次の通り。

typedef struct {
    int type;              /* イベントの型 */
    unsigned long serial;  /* 最後に処理されたリクエストの通し番号 */
    Bool send_event;       /* SendEvent リクエストから送られてきた場合 true */
    Display *display;      /* この Display からイベントを読み込んだ */
    Drawable drawable;     /* リクエストのドローアブル */
    int major_code;        /* ShmReqCode */
    int minor_code;        /* X_ShmPutImage */
    ShmSeg shmseg;         /* リクエストで使われた ShmSeg */
    unsigned long offset;  /* 使用中の ShmSeg の中のオフセット(位置) */
} XShmCompletionEvent;

イベントの型(type)の実際の値は、実行時に次のコードで確定することができる。

int CompletionType = XShmGetEventBase (display) + ShmCompletion;

完了を告げるイベントが到着する前に共有メモリ・セグメント(の内容)を変更した場合、スクリーン上に現れる結果は、整合したものにならない可能性がある。

画像データを共有メモリ版 XImage に読み込むには、次の関数を用いる。

Status XShmGetImage (Display *display, Drawable d, XImage *image, int x, int y, unsigned long plane_mask);

display は操作対象のディスプレイである。d は読み込み元となるドローアブルであり、image は書き込み先となる XImage である。x と y はドローアブル d の中のオフセット(位置)である。plane_mask は、どのプレーンを読み込むべきなのかを定める。

共有メモリ版 XImage を破棄するには、まずサーバに対して対象となる共有メモリ版 XImage を切り離す(detach)よう指示し、それから共有メモリ・セグメントそのものを破棄する。コードは次のようになる。

XShmDetach (display, shminfo);
XDestroyImage (image);
shmdt (shminfo.shmaddr);
shmctl (shminfo.shmid, IPC_RMID, 0);

第5章 共有メモリ版ピクスマップの使い方

X の画像では、いづれの画像形式も使用できるけれども、共有メモリ拡張では、共有メモリ版ピクスマップに格納するデータの形式として、ただ1つの形式(即ち XYPixmap もしくは ZPixmap)しか使えない。この形式は、画像の深さ(depth)(1 ビットのピクスマップにおいては、形式が何であっても全く関係ない)とは関係なく、スクリーンとも関係がない。サーバが使用している形式を知るには、XShmPixmapFormat を用いる。

int XShmPixmapFormat(Display *display);

読者のアプリケーションがサーバのピクスマップ・データの形式(bits-per-pixel その他を含む)に対応している場合、共有メモリ版 XImage のところで述べたのと全く同じ方法で、共有メモリ・セグメントと shminfo 構造体を作成する。必ずしも XImage を先に作らなければいけないわけではないが、そのようにすると、オーバーヘッドが少なくなり、適切な bytes_per_line の値を使えるようになる。

shminfo 構造体の各フィールドを埋め終えたら、単純に次の関数を呼べば良い。

Pixmap XShmCreatePixmap(Display *display, Drawable d, char *data, XShmSegmentInfo *shminfo, unsigned int width, unsigned int height, unsigned int depth);

引数は、追加された2つのもの(data と shminfo)を除いて、XCreatePixmap と全て同じである。shminfo は、前から使っているお馴染みの shminfo 構造体である。data は、共有メモリ・セグメントを指すポインタであり、shminfo.shmaddr フィールドと同じであるはず。なぜこれが別個の引数になっているのか、私にはわからない。

全て上手く行けば、ピクスマップが返るので、これをいつも通りの方法で操作することができる。その際には、共有メモリ・セグメントのおかげで同ピクスマップの内容を直に調整できるというオマケがついてくる。共有メモリ版ピクスマップは、普通に XFreePixmap で破棄する。但し、共有メモリ・セグメント自体は、上述のように切り離し(detach)且つ破棄するものとする。

入り口に戻る