Linux系统编程-进程间通信-共享内存

 一:共享内存

我们知道,当一个进程在运行起来的情况下,系统会为其自动创建0-4G的虚拟内存空间,根据虚拟地址和物理地址之间的映射关系,进程可以通过对虚拟内存地址的操作实现对物理页面的操作。一般情况下,每个进程的虚拟地址空间会与不同的物理地址进行映射,但是当使用共享内存进行通信时,系统会将同一段物理内存映射给不同的进程。

二:共享内存的特点

1. 共享内存的大小必须是物理页面大小的整数倍 即4k的整数倍

2. 共享内存使用前必须要和虚拟内存进行映射,映射完成之后,对于虚拟内存的操作就相当于直接对物理内存直接进行操作读写

3. 与申请堆空间类似,当通信完成之后,也应该释放物理内存,解除进程与物理内存之间的映射关系

4. 共享内存对于多个进程同一时间访问没有做限制,需要通过信号量这一机制来实现读写操作的同步 

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

#define SEGSIZE 4096 // 定义共享内存容量
typedef struct 
{
  char name[8];
  int age;
} Stu;

int main(int argc,char* argv[]){
  int shm_id, i;
  key_t key;
  char name[8];
  Stu *smap;
  key = ftok("/", 0); // 获取关键字
  if (key ==-1){
     perror("ftok error");
     return -1;
  }
  //1. 创建共享内存
  shm_id = shmget(key, SEGSIZE, IPC_CREAT|IPC_EXCL|0664);
  if (shm_id ==- 1){
      perror("create shared memory error\n");
      return - 1;
  }
  printf("shm_id=%d\n", shm_id);
  // NULL 表示不确定虚拟内存对应的地址 由系统决定地址
  smap = (Stu*)shmat(shm_id, NULL, 0); // 将进程与共享内存绑定 smap 是shmat 函数返回的是映射的虚拟内存地址
  memset(name, 0x00, sizeof(name));
  strcpy(name, "lisi");
  name[4]='0';
  for (i=0; i<3;i++) {
      name[4] += 1;
      strncpy((smap+i)->name, name, 5);
      (smap+i)->age = 20+i;
  }
  if (shmdt(smap) == -1){ // 删除绑定
     perror("detach error");
     return -1; 
  }
  return 0;
}
//shm_r.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/ipc.h>

#define SEGSIZE 4096 // 定义共享内存容量
typedef struct 
{
  char name[8];
  int age;
} Stu;

int main(int argc, char* argv[]){
  int shm_id, i;
  key_t key;
  Stu* smap;
  // shmid_ds 共享内存管理信息 共享内存属性信息的结构体
  struct shmid_ds buf;
  // ftok 通过“/”来获取当前路径下的key_t, key_t是用来做键值的,方便系统查找对应的内存映射 
  key = ftok("/", 0); 
  if (key ==-1){
      perror("ftok error\n"); 
      return -1;
  }
  printf("key=%d\n", key);
  shm_id = shmget(key, 0, 0); // 创建共享内存
  if (shm_id ==-1) {
      perror("shmget error\n");
      return -1;
  }
  printf("shm_id=%d\n", shm_id);
  smap = (Stu*)shmat(shm_id, NULL, 0); // 将进程与共享内存绑定
  for (i=0; i<3;i++){
      printf("name is %s, age is %d\n", (*(smap+i)).name, (*(smap+i)).age); 
  }
  if (shmdt(smap) == -1){
      perror("detach error\n");
      return - 1;
  }
  shmctl(shm_id, IPC_RMID, &buf); // 删除共享内存
  return 0;
}

输出如下:

zfz:Demo zhangfengzhou$ gcc shm_w.c -o shm_w
zfz:Demo zhangfengzhou$ gcc shm_r.c -o shm_r
zfz:Demo zhangfengzhou$ ./shm_w
shm_id=131072
zfz:Demo zhangfengzhou$ ./shm_r
key=262146
shm_id=131072
name is lisi1, age is 20
name is lisi2, age is 21
name is lisi3, age is 22
zfz:Demo zhangfengzhou$ 

三:总结:

使用共享内存有三个步骤:

1. 创建或获取已经存在的共享内存 shmget()

2. 将进程和共享内存进行映射绑定 shmat()

3. 映射绑定之后,将返回的指针强转为我们需要的某种类型指针

4. 对指针进行操作,就是对物理内存进行操作

5. 使用完指针之后,需要解除映射关系 shmdt()

6. 对于不再使用的共享内存,要即时删除共享内存 shmctl()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值