Linuxのプロセス間通信 - 共有メモリ
共有メモリの紹介
共有メモリアクセスに異なるプロセスが、それは、それを修正するようにすることを、論理共有メモリを出して、文字通りに理解することができます。メモリを共有するための2つのプロセス間で共有され、渡されたデータを実行している非常に効率的な方法です。メモリの異なるプロセス間で共有通常、物理メモリの同じ期間に予定されています。プロセスが自分の共有メモリアドレス空間の同じ期間に接続することができ、それらが同じmalloc関数C言語関数におけるメモリアロケーションであるかのように、すべてのプロセスが、共有メモリアドレスにアクセスすることができます。プロセスは、共有メモリにデータを書き込む場合と、変更が即座に共有メモリの同じ期間にアクセスすることができ、他のプロセスに影響を与えます。
しかし、一つのことは特に注意:共有メモリは、同期メカニズムを提供していません。最初のプロセスの終了は、共有メモリへの書き込みを行う前に、すなわち、第二の方法は、それを読み始めた自動的なメカニズムの防止はありません。多くの場合、セマフォなど、共有メモリへのアクセスを同期するために他のメカニズムを使用する必要があり、我々はそう。
2回の共有メモリ使用量
◆共有メモリを作成します
INTたshmget(key_tのキー、size_tのサイズ、int型のshmflg)。
◇リターンキーに関連付けられた最初のパラメータは、メモリ識別子(非負の整数)という名前の共有メモリ・セグメントたshmget関数が成功し、その後の機能のために共有メモリを共有しました。呼び出しが戻るために失敗した-1。
☆同じ共有メモリリソースにアクセスするための関数の値を返すことができ、プロセスを表すプログラムがアクセスすべての共用メモリに使用することができる他のプロセスは、間接的であり、最初のプログラムたshmget関数を呼び出すと、キーを提供することにより、次いで、対応する共有メモリ識別子(たshmget関数の戻り値)、直接使用のみたshmgetセマフォ機能キー、関数semgetからシステムによって返されたセマフォ識別子で使用されるすべての他の信号の量の関数を生成します。
◇バイトの2番目のパラメータ、サイズが必要な共有メモリ容量を指定しました。
それは共有メモリにキーを特定することである場合◇三番目のパラメータ、shmflg権限記号、同じモードパラメータが開いにおけるその役割と機能は、あなたがIPC_CREATを行うか、操作することができ、それを作成し、存在しません。ファイル許可フラグにメモリの読み取りおよび書き込み権限を共有、他の人が作成している間のような、例えば、プロセスを表し0644は、共有メモリの読み取りと書き込みのデータへのプロセスメモリの作成者が所有する共有メモリを作成することができますプロセスは、共有メモリを読み取ることができます。
◆共有メモリアクセスを開始
void *型にshmat(int型shm_id、CONST void *型shm_addr、INT shmflg)。
完了したら、最初の共有メモリを作成し、それが任意のプロセスによってアクセスすることができない◇、にshmat関数の役割は、現在のプロセスのアドレス空間に接続された共有メモリと共有メモリへのアクセスを開始するために使用されます。
◇最初のパラメータは、shm_idたshmgetは共有メモリを特定の関数によって返されます。
◇第2のパラメータは、共有メモリアドレスを選択するようにシステムに伝えるために、空通常、現在のプロセスのアドレス位置に接続された共有メモリを指定shm_addr。
◇3番目のパラメータは、shm_flgは、典型的には0、ビットのグループです。
戻り値共有メモリポインタの最初のバイトへのポインタを◇呼び出しが成功し、-1を返し呼び出しが失敗した場合。
◆共有メモリは、現在のプロセスとは別に
int型にshmdt(のconst void *型は、shmaddr)。
◇この関数は、現在のプロセスから別の共有メモリを用いています。共有メモリは共有メモリの現在のプロセスが使用できなくなっただけのこと、それを削除しないように隔離されていないことに注意してください。
◇パラメータは関数によって返さは、shmaddrにshmatアドレスポインタで、0を返します呼び出しが成功した場合、失敗した場合は-1。
◆共有メモリ制御
int型shmctl(int型shm_id、int型のコマンド、構造体するshmid_ds * BUF)。
◇最初のパラメータ、shm_idたshmget関数は、共有メモリ識別子を返します。
◇第二のパラメータ、コマンド動作が取られるべきである、それは次の3つの値をとることができます。
IPC_STATは:関連する共有メモリの現在値と、共有メモリの現在の設定値に関連付けられたデータ構造をするshmid_dsすなわちするshmid_dsカバレッジ値。
IPC_SET:プロセスは、現在の値が値構造特定するshmid_dsに設定されているに関連付けられている共有メモリを置くのに十分な権限を持っている場合
IPC_RMID:共有メモリセグメントを削除します。
◇第三のパラメータは、buf構造体は、共有メモリ・アクセス・パターンおよび構造へのポインタです。
struct shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
- }
三つの例
shmdata.hソース:
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER
#define TEXT_SZ 2048
struct shared_use_st
{
int written;/* 作为一个标志,非0:表示可读,0表示可写 */
char text[TEXT_SZ];/* 记录写入和读取的文本 */
};
#endif
shmread.cソース
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/shm.h>
#include"shmdata.h"
- #define MEM_KEY(1234)
int main()
{
int running =1; //程序是否继续运行的标志
void*shm = NULL; //分配的共享内存的原始首地址
struct shared_use_st *shared;//指向shm
int shmid; //共享内存标识符
//创建共享内存
shmid = shmget((key_t)MEM_KEY,sizeof(struct shared_use_st),0666|IPC_CREAT);
if(shmid ==-1)
{
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid,0,0);
if(shm ==(void*)-1)
{
fprintf(stderr,"shmat failed\n");
exit(EXIT_FAILURE);
}
printf("\nMemory attached at %X\n",(int)shm);
//设置共享内存
shared =(struct shared_use_st*)shm;
shared->written =0;
while(running)//读取共享内存中的数据
{
//没有进程向共享内存定数据有数据可读取
if(shared->written !=0)
{
printf("You wrote: %s", shared->text);
sleep(rand()%3);
//读取完数据,设置written使共享内存段可写
shared->written =0;
//输入了end,退出循环(程序)
if(strncmp(shared->text,"end",3)==0)
running =0;
}
else//有其他进程在写数据,不能读取数据
sleep(1);
}
//把共享内存从当前进程中分离
if(shmdt(shm)==-1)
{
fprintf(stderr,"shmdt failed\n");
exit(EXIT_FAILURE);
}
//删除共享内存
if(shmctl(shmid, IPC_RMID,0)==-1)
{
fprintf(stderr,"shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
shmwrite.cソース
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/shm.h>
#include"shmdata.h"
#define MEM_KEY (1234)
int main()
{
int running =1;
void*shm = NULL;
struct shared_use_st *shared = NULL;
char buffer[BUFSIZ +1];//用于保存输入的文本
int shmid;
//创建共享内存
shmid = shmget((key_t)MEM_KEY,sizeof(struct shared_use_st),0666|IPC_CREAT);
if(shmid ==-1)
{
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid,(void*)0,0);
if(shm ==(void*)-1)
{
fprintf(stderr,"shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %X\n",(int)shm);
//设置共享内存
shared =(struct shared_use_st*)shm;
while(running)//向共享内存中写数据
{
//数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本
while(shared->written ==1)
{
sleep(1);
printf("Waiting...\n");
}
//向共享内存中写入数据
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared->text, buffer, TEXT_SZ);
//写完数据,设置written使共享内存段可读
shared->written =1;
//输入了end,退出循环(程序)
if(strncmp(buffer,"end",3)==0)
running =0;
}
//把共享内存从当前进程中分离
if(shmdt(shm)==-1)
{
fprintf(stderr,"shmdt failed\n");
exit(EXIT_FAILURE);
}
sleep(2);
exit(EXIT_SUCCESS);
}
◆コード解析:
◇プログラムは、共有メモリを作成し、自分のアドレス空間に接続しshmread。共有メモリの先頭に構造struct_use_stを使用してください。構造が0、プログラム待機に設定されている共有メモリに書き込まれ、それへの書き込みデータ、共有メモリ内の他のプロセスがある場合、書き込まれたフラグを有しています。それが0でない場合、プロセスは、書き込みデータへの共有メモリがないことを示し、プログラムが共有メモリと出力からデータを読み取り、次いで0に共有メモリに書き込まれた設定をリセット、即ちプロセスshmwriteに書き込むことを許可データ。
◇プログラムは、共有メモリを取得し、独自のアドレス空間に接続しshmwrite。書かれた共有メモリをチェックし、それがない場合は、共有メモリ内のデータが終了していない表し、他方が待機するようユーザーを読み、プロンプトのプロセスを完了するのを待ち、0です。共有メモリは、共有メモリへの他のプロセスが読み取らないことを示し、ゼロに書き込まれている場合、ユーザーがテキストを入力するように要求し、再度共有メモリは、書き込みが完了したことを示す、1に書き込ま設定され、他のプロセスは、共有メモリ上に読み込むことができます。
前の例について◆セキュリティディスカッション
このプログラムは安全ではない、複数のプログラムが同時に共有メモリへの読み込みと書き込みデータがある場合、問題が発生します。あなたは、同時に、例えば、0のプロセスに書き込まれた場合にのみ、プロセスは唯一の0:00読み取ることが書かれていない場合、共有メモリにデータを書き込むことができ、使用を変更するために書かれたことができると思うかもしれません書かれた、インクリメント読み出し動作の後にデクリメントされます。これは、ビット読み書きロックファイルロック機能のようなものです。一見は、動作しているようです。しかし、これはアトミック操作ではありませんので、この練習はラインではありません。書かれた時に想像して2つのプロセスが同時に存在する場合、0である共有メモリにアクセスし、それらを0として書き込まれ、そう書かれている両方のプロセス、明らかではないでしょう。1が書き込まれたときに2つのプロセスが完了すると、このような読み出し動作と2つのプロセスがあり、同時に、共有メモリは、複数ある読み取られ、書き込ま-1となります。
プログラムが安全に行わ取得するために、重要な領域は、アトミック操作であることを保証するために操作に、それにより2進データ同期プロセスが存在すべきです。例えば、信号の量は、以前の同期処理に話さ。すべてのアトミックセマフォ操作以来。
共有メモリを使用しての3つの長所と短所
◇利点:我々は共有メモリ通信の使用を見ることができ、本当にプロセスの間に非常に便利ですが、また、インタフェース機能、データ共有もプロセス間のデータ転送ませんが、メモリへの直接アクセス、また加速プログラムを、単純なことができます効率。また、匿名パイプライン処理の通信は、いくつかの父と息子の関係を持って要求されていません。
◇短所:共有メモリは、他の手段によって、多くの場合、プロセス間の同期作業に、プロセス間通信用の共有メモリの使用で私たちを行い同期化するためにメカニズムを、提供されていません。
四の、より良い例
1、server.c
/*server.c:向共享内存中写入People*/
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<string.h>
#include"credis.h"
int semid;
int shmid;
/*信号量的P操作*/
void p()
{
struct sembuf sem_p;
sem_p.sem_num=0;/*设置哪个信号量*/
sem_p.sem_op=-1;/*定义操作*/
if(semop(semid,&sem_p,1)==-1)
printf("p operation is fail\n");
/*semop函数自动执行信号量集合上的操作数组。
int semop(int semid, struct sembuf semoparray[], size_t nops);
semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。*/
}
/*信号量的V操作*/
void v()
{
struct sembuf sem_v;
sem_v.sem_num=0;
sem_v.sem_op=1;
if(semop(semid,&sem_v,1)==-1)
printf("v operation is fail\n");
}
int main()
{
structPeople{
char name[10];
int age;
};
key_t semkey;
key_t shmkey;
semkey=ftok("../test/VenusDB.cbp",0);//用来产生唯一的标志符,便于区分信号量及共享内存
shmkey=ftok("../test/main.c",0);
/*创建信号量的XSI IPC*/
semid=semget(semkey,1,0666|IPC_CREAT);//参数nsems,此时为中间值1,指定信号灯集包含信号灯的数目
//0666|IPC_CREAT用来表示对信号灯的读写权限
/*
从左向右:
第一位:0表示这是一个8进制数
第二位:当前用户的经权限:6=110(二进制),每一位分别对就 可读,可写,可执行,6说明当前用户可读可写不可执行
第三位:group组用户,6的意义同上
第四位:其它用户,每一位的意义同上,0表示不可读不可写也不可执行
*/
if(semid==-1)
printf("creat sem is fail\n");
//创建共享内存
shmid=shmget(shmkey,1024,0666|IPC_CREAT);//对共享内存
if(shmid==-1)
printf("creat shm is fail\n");
/*设置信号量的初始值,就是资源个数*/
union semun{
int val;
struct semid_ds *buf;
unsignedshort*array;
}sem_u;
sem_u.val=1;/*设置变量值*/
semctl(semid,0,SETVAL,sem_u);//初始化信号量,设置第0个信号量,p()操作为非阻塞的
/*将共享内存映射到当前进程的地址中,之后直接对进程中的地址addr操作就是对共享内存操作*/
structPeople*addr;
addr=(structPeople*)shmat(shmid,0,0);//将共享内存映射到调用此函数的内存段
if(addr==(structPeople*)-1)
printf("shm shmat is fail\n");
/*向共享内存写入数据*/
p();
strcpy((*addr).name,"xiaoming");
/*注意:①此处只能给指针指向的地址直接赋值,不能在定义一个 struct People people_1;addr=&people_1;因为addr在addr=(struct People*)shmat(shmid,0,0);时,已经由系统自动分配了一个地址,这个地址与共享内存相关联,所以不能改变这个指针的指向,否则他将不指向共享内存,无法完成通信了。
注意:②给字符数组赋值的方法。刚才太虎了。。*/
(*addr).age=10;
v();
/*将共享内存与当前进程断开*/
if(shmdt(addr)==-1)
printf("shmdt is fail\n");
}
2、clinet.c
/*client.c:从共享内存中读出People*/
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semid;
int shmid;
/*信号量的P操作*/
void p()
{
struct sembuf sem_p;
sem_p.sem_num=0;
sem_p.sem_op=-1;
if(semop(semid,&sem_p,1)==-1)
printf("p operation is fail\n");
}
/*信号量的V操作*/
void v()
{
struct sembuf sem_v;
sem_v.sem_num=0;
sem_v.sem_op=1;
if(semop(semid,&sem_v,1)==-1)
printf("v operation is fail\n");
}
int main()
{
key_t semkey;
key_t shmkey;
semkey=ftok("../test/client/VenusDB.cbp",0);
shmkey=ftok("../test/client/main.c",0);
structPeople{
char name[10];
int age;
};
/*读取共享内存和信号量的IPC*/
semid=semget(semkey,0,0666);
if(semid==-1)
printf("creat sem is fail\n");
shmid=shmget(shmkey,0,0666);
if(shmid==-1)
printf("creat shm is fail\n");
/*将共享内存映射到当前进程的地址中,之后直接对进程中的地址addr操作就是对共享内存操作*/
structPeople*addr;
addr=(structPeople*)shmat(shmid,0,0);
if(addr==(structPeople*)-1)
printf("shm shmat is fail\n");
/*从共享内存读出数据*/
p();
printf("name:%s\n",addr->name);
printf("age:%d\n",addr->age);
v();
/*将共享内存与当前进程断开*/
if(shmdt(addr)==-1)
printf("shmdt is fail\n");
/*IPC必须显示删除。否则会一直留存在系统中*/
if(semctl(semid,0,IPC_RMID,0)==-1)
printf("semctl delete error\n");
if(shmctl(shmid,IPC_RMID,NULL)==-1)
printf("shmctl delete error\n");
}
ファイブ父と息子は、メモリサブプロセスを共有しました
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#define SHM_KEY 0x33
#define SEM_KEY 0x44
union semun {
int val;
struct semid_ds *buf;
unsignedshort*array;
};
int P(int semid)
{
struct sembuf sb;
sb.sem_num =0;
sb.sem_op =-1;
sb.sem_flg = SEM_UNDO;
if(semop(semid,&sb,1)==-1){
perror("semop");
return-1;
}
return0;
}
int V(int semid)
{
struct sembuf sb;
sb.sem_num =0;
sb.sem_op =1;
sb.sem_flg = SEM_UNDO;
if(semop(semid,&sb,1)==-1){
perror("semop");
return-1;
}
return0;
}
int main(int argc,char**argv)
{
pid_t pid;
int i, shmid, semid;
int*ptr;
union semun semopts;
/* 创建一块共享内存, 存一个int变量 */
if((shmid = shmget(SHM_KEY,sizeof(int), IPC_CREAT |0600))==-1){
perror("msgget");
}
/* 将共享内存映射到进程, fork后子进程可以继承映射 */
if((ptr =(int*)shmat(shmid, NULL,0))==(void*)-1){
perror("shmat");
}
*ptr =0;
/* 创建一个信号量用来同步共享内存的操作 */
if((semid = semget(SEM_KEY,1, IPC_CREAT |0600))==-1){
perror("semget");
}
/* 初始化信号量 */
semopts.val =1;
if(semctl(semid,0, SETVAL, semopts)<0){
perror("semctl");
}
if((pid = fork())<0){
perror("fork");
}elseif(pid ==0){/* Child */
/* 子进程对共享内存加1 */
for(i =0; i <100; i++){
P(semid);
(*ptr)++;
V(semid);
printf("child: %d\n",*ptr);
}
}else{/* Parent */
/* 父进程对共享内存减1 */
for(i =0; i <100; i++){
P(semid);
(*ptr)--;
V(semid);
printf("parent: %d\n",*ptr);
}
waitpid(pid);
sleep(2);
/* 如果同步成功, 共享内存的值为0 */
printf("finally: %d\n",*ptr);
}
return0;
}