共有メモリとは

共有メモリとは複数プロセス間で共有可能なメモリ領域です。共有メモリを利用することでファイル等を介さずに複数プロセスがデータを共有して使用することができます。 UNIX系OS上のORACLEデータベースでMEMORY_TARGETパラメータを無効にしている場合SGAは共有メモリそのものになります。 本ページでは32bit Linux環境で共有メモリを作成するプログラムを作成し、挙動を確認してみます。

ソース

Linuxで共有メモリを使用するにはshmgetシステムコールで共有メモリを作成し、共有メモリを使用したいプロセスがshmatシステムコールにより共有メモリにアタッチすることで共有メモリにアクセスできるようになります。 また、shmdtシステムコールで共有メモリをデタッチ(切り離し)し、shmctlシステムコールで共有メモリを削除することができます。

以下のプログラムは256MBの共有メモリを作成し、共有メモリに対して全て書き込みを終えるとデタッチ及び共有メモリの削除を行うプログラムで本ページではこのプログラムを使って検証します。

shm_test.c
#include "stdio.h"
#include "sys/types.h"
#include "sys/ipc.h"
#include "sys/shm.h"
#include "string.h"
#include "stdlib.h"

#define PAGESIZE 4096     // Linuxの標準ページサイズ(4K)
#define SHMSIZE 268435456 // 共有メモリのサイズ(1024*1024*256=256MB)

int shmid;
int i;
char chr[PAGESIZE] = {0};
char *shmaddr;
char *tmp_shmaddr;
char tmp = '0';
char *input = &tmp;

main(int argc, char* argv[])
{
    
    //共有メモリ作成
    shmid = shmget(IPC_PRIVATE,SHMSIZE,IPC_CREAT|0666);
    if (shmid == -1) {
        printf("shmget error\n");
        exit(1);
    }
    printf("created shared memory(shmid=%i).\n",shmid);
    
    //共有メモリアタッチ
    shmaddr = shmat(shmid, (void *)0, 0);
    if (shmaddr == (char *)-1){
        printf("shmat error\n");
        exit(1);
    }
    printf("attached shared memory(shmaddr=%p).\n",shmaddr);
    tmp_shmaddr = shmaddr;
    
    //共有メモリ書きこみ
    for (i = 0; i <= SHMSIZE - 1;i = i + PAGESIZE){
        tmp_shmaddr = tmp_shmaddr + PAGESIZE;
        strcpy(tmp_shmaddr,chr);
        //16MB単位で処理を中断
        if (i % (PAGESIZE*PAGESIZE) == 0){
            printf("press any key...");
            gets(input);
        }
        printf("address(%p)\n",tmp_shmaddr);
    }
    printf("shared memory writed..\n");
    printf("press any key...");
    gets(input);

    //共有メモリデタッチ
    if (shmdt(shmaddr) == -1){
        printf("shmdt error\n");
        exit(1);
    }
    printf("detached shared memory.\n");
    
    //共有メモリ削除
    if(shmctl(shmid,IPC_RMID,0) == -1){
        printf("shmctl error\n");
        exit(1);
    }
    printf("deleted shared memory(id=%i).\n",shmid);
}

ソースのコンパイル

以下の手順でソースをコンパイルします。(getsシステムコールは危険なため使用しないほうがよいとの警告が出ますが無視します。)
[root@linux1 tmp]# cc -c shm_test.c
[root@linux1 tmp]# cc shm_test.o -o shm_test
shm_test.o: In function `main':
shm_test.c:(.text+0x11f): warning: the `gets' function is dangerous and should not be used.
[root@linux1 tmp]# 

実行及び検証

プログラムを実行すると共有メモリの作成及び共有メモリへのアタッチが実行され、 その後リターンを押す度に16MBの領域を書き込み、256MB書き込み終えた後共有メモリからのデタッチと共有メモリの削除が実施されます。
[root@linux1 tmp]#
[root@linux1 tmp]# ./shm_test
created shared memory(shmid=1605640).
attached shared memory(shmaddr=0xa7f83000).
press any key...
・・・
address(0xb7f83000)
shared memory writed..
press any key...
detached shared memory.
deleted shared memory(id=1605640).
[root@linux1 tmp]#
共有メモリが存在している間はipcsコマンドで確認するとshmid=1605640でバイトが256MBの共有メモリが作成されていることが確認できます。 また、nattchが1であることから共有メモリへアタッチしているプロセスが1つあることも確認できます。
[root@linux1 ~]# ipcs -m

------ 共有メモリセグメント --------
キー     shmid      所有者  権限     バイト  nattch     状態
0x00000000 0          root      644        790528     2          対象
0x00000000 32769      root      644        790528     2          対象
0x00000000 65538      root      644        790528     2          対象
0x00000000 98307      root      600        196608     2          対象
0x00000000 131076     root      600        196608     2          対象
0x00000000 163845     root      600        196608     2          対象
0x00000000 196614     root      600        196608     2          対象
0x00000000 1605640    root      666        268435456  1
メモリ推移も確認してみると以下のことがわかります。

・共有メモリを作成しただけでは物理メモリにもスワップにも領域は確保されていない(②より)
・書き込まれた領域のみ物理メモリに割り当てられる(③④より)
・物理メモリに割り当てられた共有メモリ領域はcachedに計上される(③④より)(※)
※・・・カーネル2.6以降の機能であるヒュージページを共有メモリに使用した場合は挙動が異なる。

①プログラム起動前
[root@linux1 ~]# ps aux | egrep "(shm_test|VSZ)" | grep -v grep ;free
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
             total       used       free     shared    buffers     cached
Mem:       2041652    1729908     311744          0     244064    1277796
-/+ buffers/cache:     208048    1833604
Swap:      2031608      34564    1997044
②256MBの共有メモリを作成後アタッチした直後
 psのVSZ(仮想メモリ)に256MB計上されましたがRSS(物理メモリ使用量)は増えていません
[root@linux1 ~]# ps aux | egrep "(shm_test|VSZ)" | grep -v grep ;free
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     14504  0.0  0.0 263652   356 pts/2    S+   19:31   0:00 ./shm_test
             total       used       free     shared    buffers     cached
Mem:       2041652    1729908     311744          0     244072    1277792
-/+ buffers/cache:     208044    1833608
Swap:      2031608      34564    1997044
③16MBの領域に書き込んだ直後
 psのRSSとfreeのcachedが16MB増えましたが、buffersとcacheを除いたメモリ使用量はほぼ変わりません
[root@linux1 ~]# ps aux | egrep "(shm_test|VSZ)" | grep -v grep ;free
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     14504  0.4  0.8 263652 16744 pts/2    S+   19:31   0:00 ./shm_test
             total       used       free     shared    buffers     cached
Mem:       2041652    1746428     295224          0     244076    1294188
-/+ buffers/cache:     208164    1833488
Swap:      2031608      34564    1997044
④256MBの領域に書き込んだ直後
 同じくcachedが256MB増えていますが、buffersとcacheを除いたメモリ使用量はほぼ変わりません
[root@linux1 ~]# ps aux | egrep "(shm_test|VSZ)" | grep -v grep ;free
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     14504  2.8 12.8 263652 262496 pts/2   S+   19:31   0:01 ./shm_test
             total       used       free     shared    buffers     cached
Mem:       2041652    1989664      51988          0     243976    1537480
-/+ buffers/cache:     208208    1833444
Swap:      2031608      34564    1997044
⑤プログラム終了後
 共有メモリの256MB分が解放されました
[root@linux1 ~]# ps aux | egrep "(shm_test|VSZ)" | grep -v grep ;free
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
             total       used       free     shared    buffers     cached
Mem:       2041652    1727604     314048          0     243980    1275356
-/+ buffers/cache:     208268    1833384
Swap:      2031608      34564    1997044

補足

共有メモリはOSコマンドのipcrm -m でも削除できます。 上記例の場合shmidが1605640のため ipcrm -m 1605640 で削除します。

★ORACLE案件承ります