【Linux】System V共享内存

前言

目录

  • 共享内存的数据结构
  • 共享内存函数
  • 命令行查看共享内存
  • 实验:server与client通信
  • 注意点

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

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

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

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

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

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

【Linux】System V共享内存_第1张图片

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:
【Linux】System V共享内存_第2张图片

  • 生成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
【Linux】System V共享内存_第3张图片

3.命令行查看共享内存

ipcs -m

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

4.实验:server与client通信

实验现象
【Linux】System V共享内存_第5张图片

  • common.h
#include 
#include 
#include 
#include 
#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是死循环打印,最后被迫手动释放共享内存:
【Linux】System V共享内存_第6张图片

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);

你可能感兴趣的:(Linux运维,linux,unix,操作系统)