Linux共有メモリと機能の詳細な説明(例を含む)

共有メモリの概念

共有メモリとは、複数のプロセスがメモリのセクションの共通メモリを独自のプロセススペースにマッピングして、データの共有と送信を実現できることを意味します。これはカーネルレベルのリソースであり、すべてのプロセス間で最速の通信方法です。

シェル環境では、ipcsを使用して、現在のコンピューターなど、現在のシステムIPCのステータスを表示できます。

$ ipcs

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 2260992    deeplearni 600        524288     2          dest
0x00000000 2490369    deeplearni 600        67108864   2          dest
0x00000000 163842     root       777        7680       2
0x00000000 196611     root       777        8294400    2
0x00000000 229380     root       777        4096       2
0x00000000 262149     root       777        8192       2
0x00000000 294918     root       777        12288      2
...省略
0x00000000 2064444    root       777        233472     2
0x00000000 2097213    root       777        237568     2
0x00000000 2129982    root       777        241664     2
0x00000000 2162751    root       777        245760     2
0x00000000 2654272    deeplearni 600        524288     2          dest
0x00000000 2687041    deeplearni 600        524288     2          dest
0x00000000 2719810    deeplearni 600        524288     2          dest
0x00000000 2752579    deeplearni 600        524288     2          dest
0x00000000 2981956    deeplearni 600        524288     2          dest
0x00000000 2949189    deeplearni 600        524288     2          dest
0x00000000 3014726    deeplearni 600        67108864   2          dest

------ Semaphore Arrays --------
key        semid      owner      perms      nsems

このコマンドは、追加のパラメーターを使用してIPCを個別に表示します:-m(共有メモリー)、-q(メッセージ・キュー)、-s(セマフォ)。

複数のプロセスが同じメモリ領域へのアクセス権を持っているため、各プロセス間の同期の問題を解決する必要があり、セマフォで制御できます。

[記事の利点] Xiaobianは自分のC / C ++ Linuxグループを推奨しています:812855908!共有したほうがいいと思う学習本やビデオ資料をいくつか整理しました。必要に応じて追加できます。
ここに画像の説明を挿入
ここに画像の説明を挿入

共有メモリセグメントごとに、カーネルはshmid_dsタイプの構造を維持します。

// 摘自所用ubuntu18.04电脑中的/usr/include/i386-linux-gnu/bits/shm.h
struct shmid_ds
  {
    
    
    struct ipc_perm shm_perm;           /* operation permission struct */
    size_t shm_segsz;                   /* size of segment in bytes */
    __time_t shm_atime;                 /* time of last shmat() */
#ifndef __x86_64__
    unsigned long int __glibc_reserved1;
#endif
    __time_t shm_dtime;                 /* time of last shmdt() */
#ifndef __x86_64__
    unsigned long int __glibc_reserved2;
#endif
    __time_t shm_ctime;                 /* time of last change by shmctl() */
#ifndef __x86_64__
    unsigned long int __glibc_reserved3;
#endif
    __pid_t shm_cpid;                   /* pid of creator */
    __pid_t shm_lpid;                   /* pid of last shmop */
    shmatt_t shm_nattch;                /* number of current attaches */
    __syscall_ulong_t __glibc_reserved4;
    __syscall_ulong_t __glibc_reserved5;
  };

共有メモリの関連操作

共有メモリの作成/オープン共有メモリ
を作成するには、shmget()関数が必要です。プロトタイプは次のとおりです。

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flag);

共有メモリのIDは、作成が成功したときに返され、エラーが発生したときに-1が返されます。

パラメータキーは共有メモリのキー値、パラメータサイズは作成された共有メモリのサイズ、パラメータフラグは呼び出し元の関数の操作タイプです。パラメータキーとパラメータフラグによって決定されるshmget()の関数:

  • キーがIPC_PRIVATEの場合、新しい共有メモリが作成され、フラグ値は無効です。
  • キーがIPC_PRIVATEでなく、フラグにIPC_CREATビットが設定されているが、IPC_EXCLビットが設定されていない場合、キーがカーネル内の既存の共有メモリキー値である場合は開かれ、そうでない場合は新しい共有メモリが開かれます。作成されます。
  • キーがIPC_PRIVATEでなく、フラグにIPC_CREATビットとIPC_EXCLビットが設定されている場合は、共有メモリを作成する操作のみが実行されます。キーがカーネル内の既存の共有メモリキー値である場合、EEXISTエラーが返されます。


共有メモリのアタッチ(マッピング)共有メモリを作成した後、プロセスがそれを使用する場合は、このメモリ領域を独自のプロセススペース(またはアドレスマッピング)にアタッチする必要があります。これには、shmat()関数が必要です。

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int *shmat(int shmid, const void *addr, int flag);

操作が成功すると、共有メモリセグメントへのアドレスポインタが返され、エラー-1が返されます。

パラメータshmidは、共有メモリのIDです。パラメータaddrとパラメータフラグは一緒に、導入されるアドレス値を示します。通常、次の2つの用途しかありません。

  • addrは0であり、カーネルが最初の参照可能な位置を決定できることを示します
  • addrがゼロ以外であり、SHM_RNDがフラグで指定されている場合、この段落はaddrが指す位置に導入されます。

shmat()関数が正常に実行された後、shmid共有メモリセグメントのshmid_ds構造体のshm_nattchカウンター値が1増加します。

共有メモリの分離
プロセスが共有メモリを使い果たした場合、shmdt()関数を使用して、プロセスをプロセススペースから切り離す必要があります。

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(void *addr);

正常に実行された場合は0を返し、失敗した場合は-1を返します。

パラメータaddrは、共有メモリセグメントのアドレスポインタであるshmat()の呼び出しの戻り値です。shmdt()関数が正常に実行された後、shm_nattchカウンター値は1減少します。

共有メモリ制御
shmctl()を使用して、共有メモリセグメント(関数プロトタイプ)でさまざまな制御操作を実行します。

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_s *buf);

正常に実行された場合は0を返し、失敗した場合は-1を返します。

パラメータshmidは共有メモリのIDであり、パラメータcmdは実行する操作を示します。パラメータ* bufと組み合わせて使用​​されます

shmidが指す共有メモリのshmid_ds構造を取得し、パラメータbufが指す構造に値を割り当てます。

プログラミング例

基本的な手順:

  • キーftok()を生成します
  • キーを使用して共有メモリを作成/取得します、shmget()
  • 共有メモリのマッピング、仮想アドレスの取得、shmat()
  • アドレスポインタを介して共有メモリを使用する
  • マッピングを削除します、shmdt()
  • 共有メモリを破棄します、shmctl()

例1
プロセス1は、共有メモリを作成し、データshm1.cを書き込みます。

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

int main()
{
    
    
    // generate key
    key_t key = ftok("./", 200);
    printf("key=%#x\n", key);

    // create a share memory
    int shmid = shmget(key, 8, IPC_CREAT|0666|IPC_EXCL);
    if(shmid == -1)
    {
    
    
        perror("shmget failed\n");
        exit(1);
    }
    printf("shmid=%#x\n", shmid);

    // map share memory to get the virtual address
    void *p = shmat(shmid, 0, 0);
    if((void *)-1 == p)
    {
    
    
        perror("shmat failed");
        exit(2);
    }

    // write data to share memory
    int *pi = p;
    *pi = 0xaaaaaaaa;
    *(pi+1) = 0x55555555;

    // remove the map
    if(shmdt(p) == -1)
    {
    
    
        perror("shmdt failed");
        exit(3);
    }

    // delete the share memory
    printf("use Enter to destroy the share memory\n");
    getchar();
    if(shmctl(shmid, IPC_RMID, NULL) == -1)
    {
    
    
        perror("shmctl");
        exit(4);
    }

    return 0;
}

プロセス2は、共有メモリshm2.cの内容を読み取ります。

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

int main()
{
    
    
    // generate key
    key_t key = ftok("./", 200);
    printf("key=%#x\n", key);

    // get the share memory
    int shmid = shmget(key, 0, 0);
    if(shmid == -1)
    {
    
    
        perror("shmget failed\n");
        exit(1);
    }
    printf("shmid=%#x\n", shmid);

    // map share memory to get the virtual address
    void *p = shmat(shmid, 0, 0);
    if((void *)-1 == p)
    {
    
    
        perror("shmat failed");
        exit(2);
    }

    // read: get data from the share memory
    int x = *((int *)p);
    int y = *((int *)p+1);
    printf("x=%#x y=%#x\n", x, y);

    // remove the map
    if(shmdt(p) == -1)
    {
    
    
        perror("shmdt failed");
        exit(3);
    }

    return 0;
}

テストをコンパイルして実行し、最初にシェルでshm1プログラムを実行します。

$ ./shm1
key=0xc81102ed
shmid=0x2e8047
use Enter to destroy the share memory

最初に別のシェルでshm2プログラムを実行します。

$ ./shm2
key=0xc81102ed
shmid=0x2e8047
x=0xaaaaaaaa y=0x55555555

通信が成功したことがわかります。最初のシェルで「Enter」を押して、共有メモリを破棄します。

例2
例1では、ftok()関数によって生成されたキーを使用して共有メモリを作成します。この例では、IPC_PRIVATEパラメーターを使用して共有メモリを作成します。

共有メモリを作成し、そのID番号create_shm.cを出力します。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFSZ 4096

int main(void)
{
    
    
    int shm_id;

    shm_id = shmget(IPC_PRIVATE, BUFSZ, 0666);
    if(shm_id < 0)
    {
    
    
        printf("shmget failed!\n");
        exit(1);
    }
    printf("create a shared memory segment successfully: %d\n", shm_id);

    system("ipcs -m");

    exit(0);
}

指定されたIDで共有メモリを開き、コンテンツwrite_shm.cを書き込みます。

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

typedef struct
{
    
    
   char name[4];
   int age;
}people;

int main(int argc, char **argv)
{
    
    
   int shm_id, i;
   char temp;
   people *p_map;

   if(argc != 2)
   {
    
    
       printf("USAGE:atshm <identifier>\n");
       exit(1);
   }

   // get share memory ID from input
   shm_id = atoi(argv[1]);// str to int

   // map the share memory to get the virtul address
   p_map = (people *)shmat(shm_id, NULL, 0);

   // write
   temp = 'a';
   for(i=0; i<10; i++)
   {
    
    
       temp+=1;
       memcpy((*(p_map+i)).name, &temp, 1);
       (*(p_map+i)).age=20+i;
   }

   // remove map
   if(shmdt(p_map)==-1)
       perror("detach error!\n");

   return 0;
}

指定されたIDで共有メモリを開き、コンテンツread_shm.cを読み取ります。

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

typedef struct
{
    
    
   char name[4];
   int age;
}people;

int main(int argc, char** argv)
{
    
    
   int shm_id, i;
   people *p_map;

   if(argc != 2)
   {
    
    
       printf("USAGC: atshm <identifier>\n");
       exit(1);
   }

   // get the share memory ID from input
   shm_id = atoi(argv[1]);// str to int

   // map the share memory to get the virtual address
   p_map = (people*)shmat(shm_id, NULL, 0);

   // read data
   for(i=0; i<10; i++)
   {
    
    
       printf("name:%s   ", (*(p_map+i)).name);
       printf("age %d\n", (*(p_map+i)).age);
   }

   // remove the map
   if(shmdt(p_map)==-1)
       perror("detach error!\n");

   return 0;
}

上記の3つのプログラムをコンパイルするには、最初にcreate_shmプログラムを実行して、共有メモリを作成します。

$ ./create_shm
create a shared memory segment successfully: 3080264

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 2260992    deeplearni 600        524288     2          dest
0x00000000 2490369    deeplearni 600        67108864   2          dest
0x00000000 163842     root       777        7680       2
0x00000000 196611     root       777        8294400    2
0x00000000 229380     root       777        4096       2
...省略
0x00000000 2031675    root       777        229376     2
0x00000000 2064444    root       777        233472     2
0x00000000 2097213    root       777        237568     2
0x00000000 2129982    root       777        241664     2
0x00000000 2162751    root       777        245760     2
0x00000000 2654272    deeplearni 600        524288     2          dest
0x00000000 2687041    deeplearni 600        524288     2          dest
0x00000000 2719810    deeplearni 600        524288     2          dest
0x00000000 2752579    deeplearni 600        524288     2          dest
0x00000000 2981956    deeplearni 600        524288     2          dest
0x00000000 2949189    deeplearni 600        524288     2          dest
0x00000000 3014726    deeplearni 600        67108864   2          dest
0xc81102ed 3047495    deeplearni 666        8          0
0x00000000 3080264    deeplearni 666        4096       0

プログラムが3080264のshmidで共有メモリを作成したことがわかります。次に、write_shmプログラムを実行できます。パラメータとして、ID番号を取得する必要があります。

$ ./write_shm 3080264

出力はありませんが、プログラムはコンテンツを共有メモリに書き込み、共有メモリは削除されていません。read_shmプログラムを実行して、共有メモリの内容を読み取ることができます。

$ ./read_shm 3080264
name:b   age 20
name:c   age 21
name:d   age 22
name:e   age 23
name:f   age 24
name:g   age 25
name:h   age 26
name:i   age 27
name:j   age 28
name:k   age 29

ご覧のとおり、プログラムは共有メモリに格納されている10文字を読み取ります。

さらに、次のようなipcrm -m共有メモリIDの形式を使用して、コマンドを使用して不要な共有メモリを削除することもできます。

$ ipcrm -m 3080264

この時点で、ipcs -mコマンドを使用して、shmidが3080264の共有メモリがないことを確認します。

おすすめ

転載: blog.csdn.net/qq_40989769/article/details/110531512