【Linux】System V共享内存

前言

目录

System V, 曾经也被称为 AT&T System V,是Unix操作系统众多版本中的一支

System V引入了三种高级进程间的通信机制:消息队列、共享内寸和信号量

共享内存是最快速的进程间通信方式,原因在于进行通信的进程pcb的地址空间的共享区同时映射到了一块物理内存上,通过虚拟地址直接找到访问上面的数据;这些进程间数据传递不再涉及到内核的系统接口的调用

在申请了共享内存,进程1、进程2分别挂接对应的共享内存到自己的地址空间,可以正常通信了

而基于管道的进程间通信,则需要调用read、write等系统接口。写端将数据先写入到内核的文件缓冲区,读端从缓冲区进行读取,这些操作都是要调用系统接口的。

换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

在这里插入图片描述

1.共享内存的数据结构

struct shmid_ds {
	struct ipc_perm shm_perm; /* operation perms */
	int shm_segsz; /* size of segment (bytes) */
	__kernel_time_t shm_atime; /* last attach time */
	__kernel_time_t shm_dtime; /* last detach time */
	__kernel_time_t shm_ctime; /* last change time */
	__kernel_ipc_pid_t shm_cpid; /* pid of creator */
	__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
	unsigned short shm_nattch; /* no. of current attaches */
	unsigned short shm_unused; /* compatibility */
	void *shm_unused2; /* ditto - used by DIPC */
	void *shm_unused3; /* unused */
};

2.共享内存函数

  • 创建共享内存
int shmget(key_t key, size_t size, int shmflg);

参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存的标识码;失败返回-1
关于shmflg:
在这里插入图片描述

  • 生成key
    key由ftok函数得到,返回一个共享内存的唯一标识码shmid
key_t ftok(const char *pathname, int proj_id);
  • 将共享内存段连接到进程地址空间
void *shmat(int shmid, const void *shmaddr, int shmflg);

参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY,一般设置为0
返回值:成功返回一个指针,类似于malloc在堆上开辟空间一样;失败返回-1

  • 将进程地址空间与共享内存解绑定
int shmdt(const void *shmaddr);

参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
ipc资源不会因为进程退出而被释放,而是随内核,要么关机,要么使用系统接口释放
指令释放共享内存:ipcrm shmid

  • 用于控制共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
在这里插入图片描述

3.命令行查看共享内存

ipcs -m

在这里插入图片描述
shmid:共享内存编号
owner:创建者
perms:权限
bytes:大小
nattch:被多少进程连结
status:其中显示“dest”表示共享内存段已经被删除,但是还有用户在使用它,当该段内存的mode字段设置为 SHM_DEST时就会显示“dest”。

4.实验:server与client通信

实验现象
在这里插入图片描述

  • common.h
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#define PATH_NAME  "/root/class101/linux/lesson20/sharememory" 
#define PROJ_ID 18733213
#define SIZE 4097
  • server.c
#include "common.h"
int main() {
  key_t key = ftok(PATH_NAME, PROJ_ID);
  if(key == -1) {
    perror("ftok");
    return 1;
  }

  printf("key: %x\n", key);

  int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0644);

  if(shmid == -1) {
    perror("shmget");
    return 2;
  }

  printf("shmid: %d\n", shmid);
  sleep(5);


  char* start = (char*)shmat(shmid, NULL, 0);
  if(start == (void*)-1) {
    perror("shmat");
    return 3;
  }
  printf("shmat success...\n");
  sleep(5);
  while(1) {
    printf("%s\n", start);
    sleep(1);
  }


  shmdt(start);
  sleep(5);
  shmctl(shmid, IPC_RMID, NULL);
  return 0;
}
  • client.c
#include "common.h"
int main(){
  
  key_t key = ftok(PATH_NAME, PROJ_ID);
  if(key == -1) {
    perror("ftok");
    return 1;
  }

  int shmid = shmget(key, SIZE, IPC_CREAT);
  if(shmid == -1) {
    perror("shmget");
    return 2;
  }

  sleep(5);
  char* start = (char*)shmat(shmid, NULL, 0);
  if(start == (void*)-1) {
    perror("shmat");
    return 3;
  }
  sleep(5);


  char ch = 'A';
  while(ch<='Z') {
    start[ch-'A'] = ch;
    ch++;
    sleep(1);
  }

  shmdt(start);
  printf("%x\n", key);
  return 0;
} 
  • Makefile
CC=gcc
all: server client
server: server.c
	$(CC) -o $@ $^
client: client.c
	$(CC) -o $@ $^
.PHONY: clean
clean: 
	rm -f server client

由于server是死循环打印,最后被迫手动释放共享内存:
在这里插入图片描述

ipcrm -m 327680

5.注意点/重申

一、共享内存特性

共享内存的生命周期随OS
共享内存不提供任何同步与互斥的操作,双方彼此独立
共享内存是所有的进程间通信中,速度最快的
创建共享内存的大小建议是4KB的整数倍,否则系统会按照4KB对齐

二、key vs shmid

key:是一个用户层生成的唯一键值,核心作用是为了区分唯一性,不能用了进行IPC资源的操作
shmid:是一个系统给我们返回的IPC资源标识符,用来操作IPC资源

  • 你怎么保证多个进程看到的是同一块共享内存呢?
    通过Key来进行唯一性区分
  • 如何保证A、B使用同一个Key呢?
    key_t ftok(const char* pathname, int proj_id)

三、系统接口

在系统中,查看ipc的命令为ipcs

所有ipc资源都是随内核的,不会因为进程退出而自动释放 ,除非进程退出的时候调用接口释放之/OS重启

指令释放ipc

ipcrm -m shmid

四、套路

// 创建key

key_t ftok(pathname, proj_id);

// 创建共享内存

int shmid = shmget(key, SIZE, IPC_CREAT  | IPC_EXCL | 0644);

// 共享内存挂接进程

char* start = shmat(shmid, NULL, 0);

// 解除挂接

shmdt(start);

// 释放

shmctl(shmid, IPC_RMID, NULL);
  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DanteIoVeYou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值