1. 共有メモリの原理
共有メモリ (共有メモリ) は、Linux プロセス間で通信する最も簡単な方法の 1 つです。
共有メモリを使用すると、異なるプロセスがメモリの同じブロックに対して読み取りと書き込みを行うことができます。
すべてのプロセスの共有メモリへのアクセスは、追加のシステム コールやカーネル操作を必要とせずに、独自のメモリ空間にアクセスするのと同じであり、同時に冗長なメモリ コピーを回避するため、この方法は最も効率的かつ最速のプロセス間通信方法です。 。
この最大限の自由は、共有メモリに不利な点ももたらします。カーネルは、共有メモリの同じアドレスに同時に書き込むなど、共有メモリ アクセスのための同期メカニズムを提供せず、後で書き込まれたデータが以前のデータを上書きします。したがって、共有メモリを使用するには、通常、読み取り/書き込み同期と相互排他のために他の IPC メカニズム (セマフォなど) を使用する必要があります。
基本的
Linux のメモリ管理メカニズムを理解すると、共有メモリの原理を簡単に理解できます。
ご存知のとおり、カーネルはページ単位でメモリを管理します。Linux では、ページのサイズは通常 4k です。
プログラム自体の仮想アドレス空間は線形であるため、カーネルは仮想アドレス空間から対応するページへのプロセスのマッピングを管理します。
共有メモリ空間が作成された後、カーネルは異なるプロセスの仮想アドレスを同じページにマップします。そのため、異なるプロセスでは、共有メモリが配置されているメモリ アドレスへのアクセスは最終的に同じページにマップされます。次の 2 つの図は、共有メモリの動作メカニズムと共有メモリの通信原理を示しています。
予防:
共有メモリのサイズは固定されており、共有メモリの作成時に決定されます。
データ競合やエラーを避けるために、プロセス間の共有メモリへの読み取りと書き込みを同期する必要があります。
共有メモリ通信では、共有メモリ領域に対するさまざまなプロセスのアクセス権を調整する必要があります。
共有メモリ通信は、メモリ領域を直接共有することでデータコピーのオーバーヘッドを回避し、より高いパフォーマンスを実現します。ただし、これには低レベルのメモリ操作が含まれるため、データの正確性とセキュリティを確保するために開発者は慎重に扱う必要があります。
2. 共有メモリのライフサイクル
共有メモリの有効期間はカーネルによって異なります。
プロセス間通信方式には、匿名パイプ、名前付きパイプ、メッセージ キュー、セマフォ、共有メモリの 5 つがあります。2 つのパイプラインのライフサイクルはプロセスに依存し、残りはカーネルに依存します。
3. 共有メモリが最も速い通信方法なのはなぜですか?
共有メモリの原理は、物理メモリ上に空間を直接オープンし、その空間を各プロセスの仮想アドレス空間の共有領域にマッピングするため、このときプロセスは、仮想アドレス。
他の通信方法としては、データをカーネル モードにコピーし、その後カーネル モードからユーザー モードにデータをコピーして動作させる方法があります。
共有メモリでは、ユーザー モードとカーネル モードの間でデータをコピーする手順が 2 つ少なくなるため、共有メモリが最も高速になります。
共有メモリは同期や相互排他を実行しないので、セマフォまたはミューテックスとともに使用するのが最善であることに注意してください。
第四に、共有メモリの操作プロセス
ステップ 1: 共有メモリを作成する
この手順では、識別子、サイズ、権限を指定する必要があります。成功するとハンドルが返されます。
使用される関数: int shmget(key_t key, size_t size, int shmflg);
パラメータ:
key: 共有メモリ識別子 キーはそれ自体で任意の数値として指定することも、System V 標準 IPC は、によって提供されるインターフェイス ftok を通じて生成することもできます。システムキー値
size: 共有メモリサイズ、最小はメモリページサイズ、4096バイト
shmflg: オプション情報(権限情報と共有メモリの操作方法)
ftok は次のように説明します。
ftok - パス名とプロジェクト識別子を System V IPC キーに変換します (ファイル名とプロジェクト ID を通じて System V 標準 IPC キー値を生成します)
key_t ftok(const char *パス名, int proj_id);
具体的な処理:proj_idは数字で、ftok関数でファイル名からファイルのinodeノード番号を取得し、そのinodeノード番号の一部を取り出し、proj_idの一部を取り出すを組み合わせてキー値を形成します。
ftok の欠点: ftok はファイル名と proj_id からキー値を生成するため、共有メモリに複数のプロセスが接続されることになり、ファイルが削除されると他のプロセスはこの共有メモリにアクセスできなくなります。ファイルを再作成しても、ファイルの i ノード番号が異なるため、この共有メモリにアクセスすることはできません。
ステップ 2: 共有メモリを仮想アドレス空間にマップする
仮想アドレス空間の最初のアドレスであるハンドルが必要です (マップ先を知るため)
void *shmat(int shmid, const void *shmaddr, int shmflg);
パラメータ: shmid: 共有メモリ作成shmaddr
によって返されるハンドル
: マッピングの最初のアドレス - 通常は NULL を設定します
shmflg:
0 読み取りおよび書き込み可能
SHM_RDONLY 読み取り専用
戻り値: 成功: マッピングの最初のアドレスを返します 失敗: return (void*)-1
ステップ 3: メモリ操作を実行する
ステップ 4: 関係のマッピングを解除する
int shmdt(const void *shmaddr);
パラメータ:
shmaddr: マッピングの最初のアドレス
戻り値: 成功の戻り値: 0 失敗の戻り値: -1
ステップ 5: 共有メモリを削除する
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
パラメータ:
shmid: ハンドル
cmd:
IPC_RMID の操作 共有メモリの削除
buf: 属性情報の設定/
取得 受信アドレスの最初のアドレスを取得したい場合は、 NULLの共有メモリを取得したいの
ですが取得できません すぐに削除されますが、現在のコネクション番号が判定され、0でない場合は以降の接続が拒否され、コネクション番号が0になるまで共有メモリは削除されません。
ステップ 6: デモ
/*共享内存的基本使用*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/shm.h>
#define IPC_KEY 0x12345678
#define PROJ_ID 0x12345678
#define SHM_SIZE 4096
int main()
{
//1、创建/打开共享内存
int shmid = shmget(IPC_KEY, SHM_SIZE, IPC_CREAT|0664);
if(shmid < 0){
perror("shmget error");
return -1;
}
//2、将共享内存映射到虚拟地址空间
char *shm_start = (char*)shmat(shmid, NULL, 0);
if(shm_start == (void*)-1){
perror("shmat error");
return -1;
}
//3、进行内存操作
int i = 0;
while(1){
printf("%s\n", shm_start);
sleep(1);
}
//4、解除映射关系
shmdt(shm_start);
//5、删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
関連インターフェース
1) 共有メモリを作成します (共有メモリ GET):
int shmget(key_t key, int size, int flag);
戻り値: 成功した場合はキーに関連する共有メモリ識別子を返し、失敗した場合は範囲 -1 を返します。
key: 共有メモリセグメントに名前を付けます。同じメモリを共有する複数のプロセスは同じキーを使用します。
size: 共有メモリ容量。
flag: 許可フラグビット。open のモードパラメータと同じ。
2) 共有メモリ アドレス空間に接続します (SHAred Memory ATTach):
void *shmat(int shmid, void *addr, int flag);
戻り値: 戻り値は共有メモリの実際のアドレスです。
shmid: shmget() によって返される識別子。
addr: アドレスの接続方法を決定します。
フラグ: アクセスモード。
3) 共有メモリから分離する (SHAred Memory DeTach):
int shmdt(const void *shmaddr);
戻り値: 呼び出しが成功した場合は 0 を返し、失敗した場合は -1 を返します。
shmaddr: shmat() によって返されるアドレス ポインタです。