プロセス通信用の共有メモリshmget()、shmat()、shmdt()、shmctl()

共有メモリは、System Vバージョンの最後のプロセス間通信方式です。共有メモリは、その名前が示すように、2つの無関係なプロセスが同じ論理メモリにアクセスできるようにします。共有メモリは、実行中の2つのプロセス間でデータを共有および転送する非常に効果的な方法です。異なるプロセス間で共有されるメモリは通常、同じ物理メモリです。プロセスは同じ物理メモリを独自のアドレス空間に接続でき、すべてのプロセスは共有メモリ内のアドレスにアクセスできますプロセスが共有メモリにデータを書き込む場合、変更は同じ共有メモリにアクセスできる他のプロセスにすぐに影響します。

特別な注意:共有メモリは同期メカニズムを提供していません。つまり、最初のプロセスが共有メモリへの書き込み操作を終了する前に、2番目のプロセスが読み取りを開始するのを防ぐ自動メカニズムがないため、通常、他のメカニズムを使用して、セマフォなどの共有メモリへのアクセスを同期します

以下は、共有メモリのIPCの説明と分析です。

 

共有メモリの通信原理


Linuxでは、各プロセスに独自のプロセス制御ブロック(PCB)とアドレス空間(Addr Space)があり、対応するページテーブルがあり、プロセスの仮想アドレスを物理アドレスにマッピングします。メモリ管理ユニット(MMU)が管理します。2つの異なる仮想アドレスは、ページテーブルを介して物理空間の同じ領域にマップされ、それらが指す領域は共有メモリです。

共有メモリの通信原理の概略図:

上記の図についての私の理解は次のとおりです。2つのプロセスがページテーブルを介して仮想アドレスを物理アドレスにマップすると、物理アドレスに共有メモリ領域、つまり共有メモリがあり、両方のプロセスで同時に見ることができます。 。このようにして、プロセスが書き込み操作を実行すると、別のプロセスの読み取り操作でプロセス間通信を実現できます。ただし、書き込み中にプロセスが読み取られないようにするため、セマフォを使用して同期と相互排他を実現します。

共有メモリの場合、参照カウントの原則が使用され、プロセスが共有ストレージ領域を離れると、カウンターが1だけ減り、ハンガーが成功すると、カウンターが1だけ増分されます。プロセスが終了すると、それに接続されている共有ストレージ領域が自動的に切り離されます。

共有メモリが最も速いのはなぜですか?

上の図の助けを借りて、Proc Aプロセスはメモリにデータを書き込み、Proc Bプロセスはメモリからデータを読み取ります。この期間中に、合計2つのコピーが発生しました

(1)プロセッサAから共有メモリ(2)共有メモリからプロセッサB

メモリ上で直接動作するため、共有メモリの速度が向上します。

 

共有メモリのインターフェース機能と命令

1.システムの共有ストレージセグメントを表示する

ipcs -m

2.システムの共有ストレージセグメントを削除します

ipcrm -m [shmid]

3.shmget():共有メモリを作成する

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

[パラメータキー]:システムの一意のIPCリソースを識別するftokによって生成されたキー識別子。

【パラメータサイズ】:共有メモリのサイズを申請する必要があります。オペレーティングシステムでは、メモリアプリケーションの最小単位はページです。ページは4kバイトです。メモリの断片化を回避するために、通常、メモリサイズはページの整数倍に適用されます。

[パラメータshmflg]:新しい共有メモリを作成する場合は、IPC_CREAT、IPC_EXCLを使用する必要があります。すでに存在する場合は、IPC_CREATを使用するか、直接0を渡すことができます。

[戻り値]:shmflgのパラメーターに応じて、成功時に新規または既存の共有メモリ識別子を返します。失敗した場合は-1を返し、エラーコードを設定します。

 

4.shmat():共有メモリをマウントする

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

[パラメーターshmid]:共有ストレージセグメントの識別子。

[パラメータ* shmaddr]:shmaddr = 0の場合、ストレージセグメントは、カーネルによって選択された最初の使用可能なアドレスに接続されます(推奨)。

[パラメータshmflg]:SHM_RDONLYビットが指定されている場合、セグメントは読み取り専用モードで接続されます。それ以外の場合、セグメントは読み取り/書き込みモードで接続されます。

[戻り値]:共有ストレージセグメントのポインター(仮想アドレス)が正常に返され、カーネルは共有ストレージセグメントに関連付けられているshmid_ds構造体のshm_nattchカウンターを1(参照カウントと同様)増やします。エラーは-1を返します。
 

5.shmdt():共有メモリを関連付ける

プロセスが共有メモリを必要としない場合は、関連付ける必要があります。この関数は指定された共有メモリ領域を削除しませんが、以前にshmat関数で接続された共有メモリ領域を現在のプロセスから分離します。

int shmdt(const void * shmaddr);

[パラメータ* shmaddr]:接続後に返されるアドレス。

[戻り値]:0を正常に返し、shmid_ds構造体のshm_nattchカウンターを1減らし、エラーの場合は-1を返します。

 

6.shmctl():共有メモリを破棄します

int shmctl(int shmid、int cmd、struct shmid_ds * buf);

[パラメーターshmid]:共有ストレージセグメント識別子。

[パラメータcmd]:指定された実行操作IPC_RMIDに設定されている場合、共有メモリを削除できることを示します。

[パラメータ* buf]:NULLに設定するだけです。

[戻り値]:成功した場合は0、失敗した場合は-1を返します。
 

共有メモリをシミュレートする

サーバーを使用して共有ストレージセグメントを作成し、クライアントが共有ストレージセグメント識別子を取得します。2つが関連付けられた後、サーバーはデータを共有ストレージセグメントに書き込み、クライアントはデータを共有領域から読み取ります。通信が終了すると、サーバーとクライアントは共有領域から切断され、サーバーは共有ストレージセグメントを解放します。

comm.h

//comm.h
#ifndef _COMM_H__
#define _COMM_H__
 
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
 
#define PATHNAME "."
#define PROJ_ID 0x6666
 
int CreateShm(int size);
int DestroyShm(int shmid);
int GetShm(int size);
#endif

 comm.c

//comm.c
#include"comm.h"
 
static int CommShm(int size,int flags)
{
	key_t key = ftok(PATHNAME,PROJ_ID);
	if(key < 0)
	{
		perror("ftok");
		return -1;
	}
	int shmid = 0;
	if((shmid = shmget(key,size,flags)) < 0)
	{
		perror("shmget");
		return -2;
	}
	return shmid;
}
int DestroyShm(int shmid)
{
	if(shmctl(shmid,IPC_RMID,NULL) < 0)
	{
		perror("shmctl");
		return -1;
	}
	return 0;
}
int CreateShm(int size)
{
	return CommShm(size,IPC_CREAT | IPC_EXCL | 0666);
}
int GetShm(int size)
{
	return CommShm(size,IPC_CREAT);
}

client.c

//client.c
#include"comm.h"
 
int main()
{
	int shmid = GetShm(4096);
	sleep(1);
	char *addr = shmat(shmid,NULL,0);
	sleep(2);
	int i = 0;
	while(i < 26)
	{
		addr[i] = 'A' + i;
		i++;
		addr[i] = 0;
		sleep(1);
	}
	shmdt(addr);
	sleep(2);
	return 0;
}

server.c

//server.c
#include"comm.h"
 
int main()
{
	int shmid = CreateShm(4096);
 
	char *addr = shmat(shmid,NULL,0);
	sleep(2);
	int i = 0;
	while(i++ < 26)
	{
		printf("client# %s\n",addr);
		sleep(1);
	}
	shmdt(addr);
	sleep(2);
	DestroyShm(shmid);
	return 0;
}

運用実績

 

要約:

(1)利点:プロセス間の通信に共有メモリを使用すると非常に便利で、関数のインターフェイスが比較的シンプルであることがわかります。データを共有すると、プロセス間でデータが直接送信されなくなり、メモリに直接アクセスして高速化されますプログラムの効率。

(2)欠点:共有メモリは同期メカニズムを提供しないため、プロセス間の通信に共有メモリを使用する場合、プロセス間の同期を確保するために他の手段を使用することがよくあります。
 

115件の元の記事を公開 29のような 訪問者50,000以上

おすすめ

転載: blog.csdn.net/huabiaochen/article/details/104991332