当前位置: 首页 > news >正文

Linux 中System V IPC的共享内存

1. 概念介绍

System V IPC(Inter-Process Communication)是一组在UNIX系统中用于进程间通信的机制,包括共享内存、消息队列和信号量。这些机制由System V内核提供,并且它们的存在不依赖于创建它们的进程,而是由内核管理,直到显式删除。由于消息队列和信号量并不常用,故而以下主要介绍共享内存。

2. 共享内存 shm

共享内存允许不同进程共享一段由操作系统亲自开辟的物理内存,进程可以通过读写这段内存来交换数据。共享内存是最快的IPC方式,因为它减少了复制数据的需要,如通过管道交换数据时需要经过从内存复制到缓冲区中。

基本原理如上图,共享内存会被进程的页表直接映射到自己的进程地址空间的共享区,从而通过进程地址空间直接对内存进行操作,实现资源的共享与交换。

共享内存函数

shmget函数

功能:用来创建共享内存
返回值:成功返回一个非负整数,即该共享内存段的标识码 shmid ;失败返回 -1

参数:

  • key: 一个整数键值,用于唯一标识共享内存段,以创建或获取

我们可以看到 key 的类型为 key_t,实际上也是一个整数,不过要保证其数值的唯一性,要获取该参数 key 我们需要使用 ftok 函数,它会根据一个文件路径和一个项目标识符(proj_id)通过一系列算法生成一个几乎唯一的键值。

  • pathname 是一个指向存在的文件路径的指针,实际上可以随便写。
  • proj_id 是一个整数,通常是一个字符常量,其低8位被用于生成键值,实际上可以随便写。

key 是System V的唯一标识符,注意不是shm的,shmid 是 shm 的唯一标识符。

  • size: 共享内存大小,以字节为单位

注意:共享内存以4 kb为基本单位开辟内存,也就是4096 byte,哪怕只申请了1 byte的内存,实际上还是会开辟4096 byte大小的空间。因此开辟shm的时候,这个参数最好设置为4096的倍数。

  • shmflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。

其中最为常用的shmflg如下:

  • IPC_CREAT:如果共享内存段不存在,则创建一个新的共享内存段,存在则返回 shmid。
  • IPC_EXCL:与IPC_CREAT一起使用时,确保创建新的共享内存段,如果共享内存段已存在则失败。
  • 权限位:shmflg的低9位用于设置共享内存段的权限,与文件系统的权限位相同。
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main()
{key_t key=ftok("/home/lbk/lesson17/test.cpp",8888);int shmid=shmget(key,4096,IPC_CREAT|IPC_EXCL|0666);return 0;
}

第一个参数 key:我们通过ftok获得该唯一的共享内存的system V标识符key;
第二个参数为4096:即开辟的共享内存大小为4096 byte;
第三个参数为IPC_CREAT | IPC_EXCL | 0666:如果当前的key不存在,则创建对应的共享内存,该共享内存的初始权限为0666,如果存在,则创建失败。

通过 ipcs指令可以看当前所有的system V的总体情况:

也可以通过 ipcs -m命令只看共享内存的情况:

前面我们已经提到共享内存的存在不依赖于创建它们的进程,而是由内核管理,直到显式删除。即进程已经结束了,但是进程创建的共享内存还存在,如果想要删除一个共享内存,我们可以通过ipcrm -m shmid 命令。

shmat函数

共享内存是被直接映射到进程地址空间的共享区的,进程可以通过访问进程地址空间来访问共享内存,而从内存映射到进程地址空间我们就需要shmat函数。

功能:将共享内存段连接到进程地址空间
返回值:成功返回一个指针,指向共享内存第一个节;失败返回 -1
参数
  • shmid: 由 shmget 函数返回的共享内存段的标识符。
  • shmaddr: 一个指向进程地址空间中的位置的指针,用于指定共享内存段附加的起始地址。通常,这个参数设置为 NULL,让系统自动选择一个合适的地址。
  • shmflg: 一个标志位,可以包含 SHM_RDONLY 来指定以只读方式附加共享内存,或者 0 以允许读写。

shmdt函数

当一个进程不再需要访问共享内存时,可以调用 shmdt 来解除共享内存与该进程地址空间的关联。这个函数不会删除共享内存,只是使其对当前进程不可用。

功能:将共享内存段与当前进程脱离
返回值:成功返回 0 ;失败返回 -1
参数:
shmaddr: shmat 所返回的指针

shmctl函数

功能:用于控制System V共享内存段的系统调用。它可以执行多种操作,包括获取共享内存段的状态信息、修改共享内存段的属性、以及删除共享内存段。
返回值:成功返回 0 ;失败返回 -1
参数:
  • shmid: shmget返回的共享内存标识码
  • cmd: 命令参数,指定了要对共享内存段执行的操作(有三个可取值)

IPC_STAT:获取共享内存段的状态信息
IPC_SET:设置共享内存段的某些属性
IPC_RMID:删除共享内存段

  • buf: 一个指向 shmid_ds 结构的指针,用于存储或接收共享内存段的信息。如果 cmd 是IPC_STAT 或 IPC_SET, 则 buf 不为 NULL;如果 cmd 是 IPC_RMID,则 buf 可以为 NULL

shmid_ds 结构体是与System V共享内存段相关联的数据结构,它包含了共享内存段的各种状态信息和属性。这个结构体在 <sys/shm.h> 头文件中定义,并且在内核中用于维护每个共享内存段的信息。

struct shmid_ds {struct ipc_perm shm_perm;    /* Ownership and permissions */size_t          shm_segsz;   /* Size of segment (bytes) */time_t          shm_atime;   /* Last attach time */time_t          shm_dtime;   /* Last detach time */time_t          shm_ctime;   /* Last change time */pid_t           shm_cpid;    /* PID of creator */pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */shmatt_t        shm_nattch;  /* No. of current attaches */...
};
使用示例:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int main()
{key_t key = ftok("/home/lbk/lesson17/test.cpp", 8888);int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0666);if (shmid == -1){perror("shmget");exit(-1);}struct shmid_ds shm;shmctl(shmid, IPC_STAT, &shm);cout << "shm_nattch:" << shm.shm_nattch << endl;cout << "shm_segsz:" << shm.shm_segsz << endl;shmctl(shmid, IPC_RMID, NULL);return 0;
}

进程间通信示例

comm.hpp

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <iostream>
using namespace std;#define PATH_NAME "/home/lbk/lesson17/comm.hpp"
#define PROJECT_ID 0x8888
#define SHM_SIZE 4096
key_t Getkey()
{return ftok(PATH_NAME, PROJECT_ID);
}
int Creatshm()
{int shmid = shmget(Getkey(), SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);if (shmid == -1){cout << "shmget error" << endl;return -1;}return shmid;
}
int Getshm()
{int shmid = shmget(Getkey(), SHM_SIZE, IPC_CREAT);if (shmid == -1){cout << "shmget error" << endl;return -1;}return shmid;
}

server.cpp

#include "comm.hpp"
int main()
{int shmid = Creatshm();char *buf = (char *)shmat(shmid, NULL, 0);cout << "Client start" << endl;while (true){cout << "Please Enter@";cin >> buf;}shmctl(shmid, IPC_RMID, NULL);return 0;
}

client.cpp

#include "comm.hpp"
int main()
{int shmid = Getshm();char *buf = (char *)shmat(shmid, NULL, 0);while (true){cout << "client receive:" << buf << endl;sleep(1);}return 0;
}

makefile

.PHONY:all
all:client.exe server.exe
client.exe:client.cppg++ -o $@ $^
server.exe:server.cppg++ -o $@ $^
.PHONY:clean
clean:rm -f client.exe server.exe

特点

  • 高效性:共享内存允许直接在内存中交换数据,避免了数据拷贝的开销,因此在进程间通信中速度非常快。

  • 同步问题:由于多个进程可以同时访问共享内存,因此需要同步机制来避免数据竞争和一致性问题。通常,进程会使用信号量或互斥锁来同步对共享内存的访问。

  • 系统资源:共享内存不需要操作系统维护额外的通信队列或管道,它直接使用进程的地址空间。

在使用共享内存时,需要注意的是,共享内存的内容在系统重启后不会持久化,因为它存储在物理内存中,而不是磁盘上。此外,共享内存的管理通常涉及一系列系统调用,如 shmget 创建共享内存段,shmat 将共享内存段附加到进程的地址空间,shmdt 分离共享内存段,以及 shmctl 控制共享内存段的属性和状态.


http://www.mrgr.cn/news/26375.html

相关文章:

  • 深度学习速通系列:命名实体识别
  • Docker概述
  • 【MySQL】MySQL和Workbench版本兼容问题
  • Linux下的系统接口(实时更新)
  • GPT-4与ChatGPT:人工智能对话的新时代【含国内可用gpt】
  • OPENAI的 o1非常强-可是也被网友们玩坏了
  • 【刷题】Day 3--错误的集合
  • 在 React 中掌握 useImperativeHandle(使用 TypeScript)
  • Python数据分析与可视化基础教程
  • linux-L9.linux中对文件 按照时间排序 显示100 个
  • CentOs 入门必备基础知识详细讲解
  • Python 入门教程(3)基础知识 | 3.1、基础语法
  • C++——多态
  • Delphi 的 RSA 库 LockBox
  • 用Matlab求解绘制2D散点(x y)数据的最小外接矩形
  • RTC、ADC
  • 红外接收并解码驱动C语言
  • 认识原码反码补码
  • Java8函数式接口全攻略
  • OpenAI发布o1预览模型:推理能力更强可达理科博士生水准