Linuxシステムコール、共有メモリの使い方

UNIX / Linux システムコール・プログラミング

プロセス間通信(IPC)、共有メモリ

共有メモリとは?

カーネルが管理するメモリ空間をユーザープロセスに”見えるようにする”ことで、複数のプロセス間で同じメモリ空間を利用することができます。
プロセスからは通常のメモリと同様に見えるため、同じエリアに対しての同時書き込みなどについては、排他制御を行う、あるいはアトミックな操作とするなどの対応が必要になります。

共有メモリは SYSTEM V IPC です。


それでは、早速サンプルコードについて解説します。
今回のコードは2つのプログラムで構成されています。

shm_r.c
共有メモリは、shmget()システムコールにより共有メモリを取得します。このときの引数の指定で”ない場合は作成する”、パーミッションを与えるなどの指定を行います。生成された共有メモリにアクセスするため、shmat()システムコールでポインタを得るます。
以降、このプロセスはshmat()システムコールで得たポインタを通じ、自由に共有メモリにアクセスすることができます。

この、サンプルshm_r.cは、共有メモリを生成・アタッチすると1秒毎にメモリの更新をチェックし、更新された場合は内容を表示します。このときの更新内容が "Quit"であった場合、共有メモリをデタッチし、共有メモリを破棄します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

#define DATA_SIZE 2048

int main(void) {

    int  id;
    char *shmData;
    char data[DATA_SIZE];

    // Shared memory create a new with IPC_CREATE
    if((id = shmget(IPC_PRIVATE, DATA_SIZE, IPC_CREAT|0666)) == -1){
        perror("shmget()");
        exit(-1);
    }

    printf("Shm ID = %d\n",id);

    // Shared memory attach and convert char address
    if((shmData = (char *)shmat(id, NULL, 0)) == (void *)-1){
        perror("shmat()");
    } else {
        // Write initial data to shared memory
        strcpy(shmData, "INITIALIZED DATA");
        memset(data, 0x00, DATA_SIZE);

        // Check updateing every a second.
        while( 1 ){
            // Does shared memory updated?
            if (strcmp(shmData, data) != 0) {
                printf("%s\n", shmData);
                strcpy(data, shmData);

                // Does shared memory  updated same 'Quit'?
                if (strcmp(data, "Quit") == 0) {
                    break;
                }
            }
            sleep( 1 );
        }

        // Detach shred memory
        if(shmdt( shmData )==-1){
            perror("shmdt()");
        }
    }

    // Remove shred memory
    if(shmctl(id, IPC_RMID, 0)==-1){
        perror("shmctl()");
        exit(EXIT_FAILURE);
    }

    return 0;
}



shm_w.c
もう1つのサンプルです。
こちらは、先にshm_rが起動していることを前提としています。
起動するときの引数として、shm_rが起動したとき生成した共有メモリのIDを表示していますが、このIDを第1引数として与え、第2引数に文字列を与えます。

shm_wは、shmat()システムコールで共有メモリIDを使ってポインタの取得を行い、第2引数に設定された文字を共有メモリに書き込み、shmdt()システムコールをつかって共有メモリのデタッチをして終了するだけです。

shm_wが共有メモリを更新し、shm_rが更新を検出して表示しますが、shm_wが"Quit"を入力することで、shm_rも終了することができます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

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

    int   id;
    char  *shmData;

    if( argc <= 2) {
        fprintf(stderr, "Usage: shm_w shm_id string\n");
        exit(EXIT_FAILURE);
    }

    // Attache shared memory
    id = atoi(argv[1]);
    if((shmData = (char *)shmat(id, 0, 0)) == (void *)-1) {
        perror("shmat()");
        exit(EXIT_FAILURE);
    } else {
        strcpy(shmData, argv[2]);
        fprintf(stderr, "Written:%s.\n", shmData);
        // Detach shared memory
        if( shmdt( shmData ) == -1) {
            perror("shmdt()");
            exit(EXIT_FAILURE);
        }
    }

    return 0;
}



共有メモリって?

Linuxのメモリ空間でも説明しましたが、ユーザー空間で動作するプログラム(プロセス)は論理アドレス上で動作しています。CPUのMMUの機能を使って物理アドレスから論理アドレスへマッピングしたアドレス空間上で動作しています。

shmget()システムコールでIPC_CREATを指定することで、新たな共有メモリが生成されますが、このメモリはカーネルが管理しているメモリの1部を要求された共有メモリに割り当てる動作をします。しかし、カーネルは物理アドレスとしてメモリ空間を管理しています。このため、shmat()システムコールによって論理空間上にマッピングすることでユーザープロセス側でもアクセスが可能となります。
また、shmat()システムコールでは共有メモリの論理的なIDを指定しています。このため、別々の論理空間上のプロセスから同じIDでアタッチを行うことで同じ共有メモリへのマッピングが行われ、同じメモリ空間へのアクセスが可能となる仕組みとなっています。

生成中の共有メモリを確認するには?
ipcsコマンドで確認できます。
  ipcs -m
と、することで生成中の共有メモリを確認できます。
今回のサンプルでshm_rを起動した状態でこのコマンドを入力すると、生成したIDの共有メモリを確認できます。
もし、プログラムのバグで後片付けをちゃんとせずにプログラム終了してしまったときは、コマンドで確認してください。そしてipcrmコマンドで削除することができます。


Have a Happy Hucking!!



Lightning Brains

コメント

このブログの人気の投稿

Linuxシステムコール、メッセージキューの使い方

Linuxシステムコール、セマフォの使い方