共有メモリ

目次

1.共有メモリ 2.共有メモリを使用する手続き 3.サンプルプログラム

共有メモリ

共有メモリは、データをやり取りしたり共有するための方法をプロセスに 提供するもので、2つ以上のプロセスが特定のメモリ領域を共有できます。 そのため、コピーをする必要がないため、高速なIPCであるといえます。 またメモリ上の一貫性を保つためには、メモリの参照を同期させるため セマフォが使用されます。 カーネルが共有メモリ領域を管理するためには次の構造体を使います。
include/linux/shm.h
/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct shmid_ds {
        struct ipc_perm         shm_perm;       /* operation perms */
        int                     shm_segsz;      /* size of segment (bytes) */
        __kernel_time_t         shm_atime;      /* last attach time */
        __kernel_time_t         shm_dtime;      /* last detach time */
        __kernel_time_t         shm_ctime;      /* last change time */
        __kernel_ipc_pid_t      shm_cpid;       /* pid of creator */
        __kernel_ipc_pid_t      shm_lpid;       /* pid of last operator */
        unsigned short          shm_nattch;     /* no. of current attaches */
        unsigned short          shm_unused;     /* compatibility */
        void                    *shm_unused2;   /* ditto - used by DIPC */
        void                    *shm_unused3;   /* unused */
};

共有メモリを使用する手続き

共有メモリを使うには、
1、共有メモリ領域を作成する
2、共有メモリにアクセスして内容の読み書きをする
3、共有メモリを消去する
の3つの手順が必要です。次にそれぞれを解説していこうと思います。

1、共有メモリ領域を作成する

まず要求されたサイズを持つ新しい共有メモリを作成するためにプロセスは、 shmget()システムコールを発行します。、

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, int size, int shmflg);  

2、共有メモリにアクセスして内容の読み書きをする

shm_get()関数を使って共有メモリの識別子を獲得した後、 プロセスはshmat()システムコールを呼び出します。 このシステムコールは、プロセスアドレス空間内の新しい領域の 先頭アドレスを返します。 先頭アドレスさえ手に入れてしまえばメモリの内容を読み書きできる ようになります。
#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

3、共有メモリを消去する

shmctl()関数を使用します。第1引数に消去したい共有メモリ のID番号、第2引数にIPC_RMIDと記述すれば、解放されます。
#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);  
アドレス空間から共有メモリを開放したい場合は、プロセスはshmdt() システムコールを呼び出します。 この場合は、システムから識別子と結び付けられた構造体を 削除しません。上記のようにプロセスがIPC_RMIDコマンドをしていして shmctlを呼ばない限り、記述子は存在し続けます。
#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void *shmaddr);  

サンプルプログラム

以上の事を考慮して簡単なプログラムを作成しました。 特定のシステムが異なるデータをどこに接続したかを表示するプログラム です。
サンプルプログラムコード
#include	<sys/types.h>
#include	<sys/ipc.h>
#include	<sys/shm.h>
#include	<stdlib.h>

#define	ARRAY_SIZE	 40000
#define	MALLOC_SIZE	100000
#define	SHM_SIZE	100000
#define	SHM_MODE	(SHM_R | SHM_W)	/*ユーザが読み書き可能*/

char	array[ARRAY_SIZE];	/*初期化されていないデータ(bss)*/

int 
main(void)
{
	int		shmid;
	char	*ptr, *shmptr;

	/*初期化されていないデータ(bss)*/
	printf("array[] from %x to %x\n", &array[0], &array[ARRAY_SIZE]);

	/*スタック*/
	printf("stack around %x\n", &shmid);

	/*マロックサイズ100,100バイト確保*/
	if ( (ptr = malloc(MALLOC_SIZE)) == NULL)
		error("malloc error");
	printf("malloced from %x to %x\n", ptr, ptr+MALLOC_SIZE);

	/* IPC_PRIVATEをキーとしてSHM_SIZEバイトの共有メモリを新規に
	   準備し、SHM_MODEでユーザに読み書きを許す */
	if ( (shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0)
		error("shmget error");

	/* 共有メモリの先頭をオプションなしで確保
	   これでshmptrが指すメモリは他のプロセスと共有 */
	if ( (shmptr = shmat(shmid, 0, 0)) == (void *) -1)
		error("shmat error");
	printf("shared memory attached from %x to %x\n",
				shmptr, shmptr+SHM_SIZE);

	/* 共有メモリ識別子を削除 */
	if (shmctl(shmid, IPC_RMID, 0) < 0)
		error("shmctl error");

	exit(0);
}


実行結果として以下のように出力されます。
array[] from 8049900 to 8053540
stack around bffff7c8
malloced from 8053548 to 806bbe8
shared memory attached from 4014b000 to 401636a0

実行結果でのメモリ配置は以下のようになります。

●プログラム引数とプログラム環境。
●プログラム・スタック。 プログラムが実行を続けるうちに拡大するのが普通です。 一般に、ヒープに向かって下の方向に拡大してゆきます。
●ヒープ。 プログラムが実行を続けるうちに、拡大する傾向があります。 通常は、スタックに向かって上の方向に拡大してゆきます。
●BSS セグメント。 グローバルに使用可能な非初期化データ (グローバル変数など) を格納します。 
●データ・セグメント。 グローバルな使用が可能な (普通はグローバル変数) を格納します。 
●テキスト・セグメント。 読み取り専用のプログラム・コードを格納します。