53-System V 共享内存初体验

为了能快速掌握 System V IPC,本篇以最少量的文字让初学者先初步掌握共享内存,后续文章,再慢慢深入。

共享内存只是 System V IPC 中的一种,前面的进程通信总览中我们讲过 System V IPC 包括了共享内存、消息队列和信号量。

1. 共享内存

为了能够快速上手共享内存,下面直接介绍一下大致的方法:

  • 根据已知的键(key) 使用 get 函数获取或者创建内核对象,并且返回内核对象的 id 号。
  • 根据 id 号获取内存地址。
  • 向内存读写数据。

内核对象,可以理解为位于内核空间中的某种类型的结构体。

先简单介绍一下,键值必须是我们事先约定好的,通过 get 函数获取内核对象 id 号这种方式我们以前应该见到过类似的,就好像是一对 <key, value> 。这里的 get 函数,通常都是以 get 为后缀的函数名,在共享内存里,对应的这个 get 函数名字为 shmget,其中 shm 的意思是 share memory。

根据 key 获取的 id 号,它唯一的标识了内核中的一个对象。此时,我们可以根据此 id 获取内核对象的相关信号。对于共享内存,使用此 id 可以将内核对象中的内存挂接到用户空间的线性地址。

接下来看实例。

2. 实例

该实例有两个程序,程序 a 创建一个共享内存的内核对象,获取内存后,向其写入数据。程序 b 获取此内核对象后挂接内存,读取数据然后打印。

详细过程见注释部分,请认真阅读。

2.1 程序 a

// a.c
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>

int main() {
  // shmget 函数通过事先约定的键值 0x8888 创建(IPC_CREAT)一个内核对象并返回其 id。如果 0x8888 对应的内核对象存在,就失败(IPC_EXCL)。0664 是该内核对象的权限。第二个参数表示创建的共享内存大小。
  int id = shmget(0x8888, 4096, IPC_CREAT | IPC_EXCL | 0664);
  // 如果失败就退出
  if (id < 0) {
    perror("shmget");
    return -1; 
  }

  // 打印获取到的内核对象 id
  printf("id = %d\n", id);

  // 使用函数 shmat (share memory attach) 将内核对象维护的内存挂接到指定线性地址(第二个参数)
  // 如果第二个参数为 0,则系统帮你选择一个合适的线性地址。
  char *buf = shmat(id, NULL, 0); 

  // 如果挂接失败就退出
  if (buf == (char*)-1) {
    perror("shmat");
    return -1; 
  }

  // 将数据拷贝到共享内存
  strcpy(buf, "hello, share memory!\n");

  // 使用 shmdt(share memory detach) 将挂接的内存卸载
  if (shmdt(buf) < 0) {
    perror("shmdt");
    return -1; 
  }

  return 0;
}

2.2 程序 b

// b.c
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>

int main() {
  // 根据事先约定后的键值获取内核对象 id,这时候后面两个参数都可以为 0.
  int id = shmget(0x8888, 0, 0); 
  if (id < 0) {
    perror("shmget");
    return -1; 
  }

  printf("id = %d\n", id);

  // 挂接内存
  char *buf = shmat(id, NULL, 0); 

  if (buf == (char*)-1) {
    perror("shmat");
    return -1; 
  }

  // 打印数据
  printf("%s", buf);

  // 卸载
  if (shmdt(buf) < 0) {
    perror("shmdt");
    return -1; 
  }

  // 删除内核对象
  if (shmctl(id, IPC_RMID, NULL) < 0) {
    perror("shmctl");
    return -1; 
  }

  return 0;
}

2.3 编译

$ gcc a.c -o a
$ gcc b.c -o b

2.4 运行

  • 运行程序 a
$ ./a

如果你的程序运行成功,会在屏幕打印内核对象的 id 号。另外通过命令ipcs -m 可以看到刚刚创建的共享内存内核对象。


这里写图片描述
图1 刚刚通过程序 a 创建的内核对象

  • 运行程序 b
$ ./b

屏幕打印如下结果:


这里写图片描述
图2 程序 b 的运行结果

3. 总结

本篇初步展示共享内存的使用方法。同学需要理解向共享内存中写稿数据后,即使进程结束了,共享内存中的数据都还在,因为这些数据是保存在内核空间中的。后面将详细讲解 System V 的相关概念。

本篇需要掌握:

  • 初步理解如何获取内核对象
  • 知道进程间通信大致的实现原理(利用内核空间)
  • 初步了解 ipcs 命令,可以使用 ipcs -h 命令了解更多使用方法
  • 知道 System V IPC 的三种方式
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值