System V方案 — 共享内存

news2024/9/21 3:22:34

目录

  • System V方案 — 详述共享内存
    • 共享内存
      • 共享内存的原理
      • 共享内存数据结构
      • 共享内存函数
        • 实例
    • 消息队列
      • 消息队列数据结构
      • 消息队列函数
        • 创建
        • 删除
    • 信号量
      • 信号量数据结构
      • 信号量函数
        • 创建
        • 删除
      • 进程互斥
    • 总结

System V方案 — 详述共享内存

SystemV标准的进程间通信方式,是前人在OS层面专门为进程通信设计的一个方案。由于需要给用户提供功能使用,而OS又不相信任何用户,所以此时采用系统调用。

因此,System V进程间通信,一定会存在专门用来通信的接口(system call)。

总的来说,System V是一个由人们定制的,在同一个主机内的进程间通信方案(System V方案)。

我们知道,进程间通信的本质是,先让不同的进程看到同一份资源。为此,System V提供的主流方案有三个,分别为:

  • 共享内存
  • 消息队列(有点落伍)
  • 信号量

其中,共享内存和消息队列以传送数据为目的,信号量以实现进程间同步或互斥为母的。

共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到
内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据

可以通过接下来的部分来深入理解这句话。

共享内存的原理

  1. 通过某种调用,在内存中创建一份内存空间。
  2. 通过某种调用,让参与通信的多个进程“挂接”到这份新开辟的内存空间上。

image-20230518210159716

此时就让不同进程看到了同一份资源,这就叫做共享内存。

OS内可能存在多份共享内存,因此需要对这些不同的共享内存进行管理。

为了保证两个或者多个进程,看到的是同一个共享内存,共享内存一定要有一定的标识唯一性的ID,方便让不同的进程能识别同一个共享内存资源!

这个“ID”应该在哪里呢?应该在描述共享内存的数据结构中。

共享内存数据结构

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 */
}

共享内存函数

  1. shmget

    功能:用来创建共享内存
    原型:
      int shmget(key_t key, size_t size, int shmflg);
    参数:
      key:这个共享内存段名字
      size:共享内存大小 (共享内存在内核中申请的基本单位是页,内存页(4KB)
      shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
    返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
    

image-20230519130134374

  1. shmat函数

    功能:将共享内存段连接到进程地址空间
    原型:
      void *shmat(int shmid, const void *shmaddr, int shmflg);
    参数:
      shmid: 共享内存标识
      shmaddr:指定连接的地址
      shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
    返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1
    
    //说明
    shmaddr为NULL,核心自动选择一个地址
    shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
    shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。
    公式:shmaddr-(shmaddr % SHMLBA)
    shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
    
  2. shmdt函数

    功能:将共享内存段与当前进程脱离
    原型:
      int shmdt(const void *shmaddr);
    参数:
      shmaddr: 由shmat所返回的指针
    返回值:成功返回0;失败返回-1
    注意:将共享内存段与当前进程脱离不等于删除共享内存段
    
  3. shmctl函数

    功能:用于控制共享内存
    原型:
      int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    参数:
      shmid:由shmget返回的共享内存标识码
      cmd:将要采取的动作(有三个可取值)
      buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
    返回值:成功返回0;失败返回-1
    
命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

实例

接下来通过实例代码了解一下这些函数或接口,先实现以下代码

  • 共享内存的创建与删除

comm.h

#pragma once
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>

#define PATH_NAME "./"
#define PROJ_ID 0x6666
#define SIZE 4097

server.c

#include "comm.h"
int main(){
  key_t key = ftok(PATH_NAME, PROJ_ID);
  if(key < 0){
    perror("ftok");
    return 1;
  }
  //创建共享内存
  int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL);//创建全新的shm,如果和系统已经存在ID冲突,我们出错返回
  if(shmid < 0){
    perror("shmget");
    return 2;
  }
  printf("key: %u, shmid: %d\n", key, shmid);
  sleep(10);
  shmctl(shmid, IPC_RMID, NULL); //属性设为NULL
  return 0;
}

client.c

#include "comm.h"
int main(){
  key_t key = ftok(PATH_NAME, PROJ_ID);
  if(key < 0){
    perror("ftok");
    return 1;
  }
  printf("%u\n", key);
  return 0;
}
  • 如图,执行完创建共享内存的程序后,该进程结束。
    image-20230519233954186

  • 通过ipcs查看后,可以发现,进程结束,该进程创建的共享内存并未被释放,依旧存在。
    image-20230519233540138

  • 这是因为systemV的IPC资源,生命周期是随内核的!只能通过,程序员显示的释放(命令,system call等)或者OS重启释放。

    • 如下使用ipcrm删除(面对用户层面),再次查看已经被释放:
      image-20230519233802730

    • 直接在源代码加入删除语句(开发者层面),通过shmctl

      image-20230520000008939

  • 由上述示例也可以看出**keyshmid的区别**

    • key:只是用来在系统层面进行标识唯一性的,不能用来管理shm
    • shmid:是OS给用户返回的id,用来在用户层进行shm管理
  • perm为权限,可以设置

    image-20230520002305908

  • 通过共享内存实现控制打印

comm.h

#pragma once
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>

#define PATH_NAME "./"
#define PROJ_ID 0x6666
#define SIZE 4097

server.c

#include "comm.h"
int main(){
  key_t key = ftok(PATH_NAME, PROJ_ID);
  if(key < 0){
    perror("ftok");
    return 1;
  }
  //创建共享内存
  int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0666);//创建全新的shm,如果和系统已经存在ID冲突,我们出错返回
  if(shmid < 0){
    perror("shmget");
    return 2;
  }
  printf("key: %u, shmid: %d\n", key, shmid);
  //sleep(10);

  char *mem =(char*)shmat(shmid, NULL, 0); //因为shmat返回值为void*,所以此处要做强转
  printf("attaches shm success\n");
  //sleep(15);

  //此处即为后面要进行的通信逻辑
  while(1){
    sleep(2);
    //这里有没有调用类似pipe or fifo中的read这样的接口呢?
    //没有,所以共享内存一旦建立好并映射进自己进程的地址空间,该进程就可以直接看到该共享内存,
    //就如图malloc的空间一般,不需要任何系统调用接口!
    //也因此,共享内存是所有的进程间通信中速度最快的!
    printf("%s\n", mem); // server 任务共享内存里面放的是一个长字符串
  }
  
  shmdt(mem);
  printf("detaches shm success\n"); 
  //sleep(5);
  shmctl(shmid, IPC_RMID, NULL); //属性设为NULL
  printf("key: 0X%x, shmid: %d -> shm delete success\n", key, shmid);

  //sleep(10);
  return 0;
}

client.c

#include "comm.h"
int main(){
  key_t key = ftok(PATH_NAME, PROJ_ID);
  if(key < 0){
    perror("ftok");
    return 1;
  }
  printf("%u\n", key);
  //client这里只需获取即可
  int shmid = shmget(key, SIZE, IPC_CREAT);
  if(shmid < 0){
    perror("shmget");
    return 1;
  }
  char *mem = (char*)shmat(shmid, NULL, 0);
  //sleep(5);
  printf("client process attaches success!\n");
  
  //这个地方就是我们要通信的区域
  char c = 'A';
  while(c <= 'Z'){
    mem[c-'A'] = c;
    c++;
    mem[c-'A'] = 0;
    sleep(2);
  }
  shmdt(mem);
  //sleep(5);
  printf("client process detaches success\n");
  return 0;
}

image-20230520160141138

  • 运行效果:

    image-20230520154629199

  • ctrl+c终止进程后,再次重启会发现如下现象,这是因为共享内存已存在,而上一个进程并未进行到释放该共享内存的部分,此时可手动删除再重启即可

    image-20230520154924839

    image-20230520155333170

消息队列

消息队列数据结构

struct msqid_ds {
    struct ipc_perm msg_perm;     /* Ownership and permissions */
    time_t          msg_stime;    /* Time of last msgsnd(2) */
    time_t          msg_rtime;    /* Time of last msgrcv(2) */
    time_t          msg_ctime;    /* Time of last change */
    unsigned long   __msg_cbytes; /* Current number of bytes in queue (nonstandard) */
    msgqnum_t       msg_qnum;     /* Current number of messages in queue */
    msglen_t        msg_qbytes;   /* Maximum number of bytes allowed in queue */
    pid_t           msg_lspid;    /* PID of last msgsnd(2) */
    pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
};

//The ipc_perm structure is  defined  as  follows  (the  highlighted  fields  are settable  using
IPC_SET):

struct ipc_perm {
    key_t          __key;       /* Key supplied to msgget(2) */
    uid_t          uid;         /* Effective UID of owner */
    gid_t          gid;         /* Effective GID of owner */
    uid_t          cuid;        /* Effective UID of creator */
    gid_t          cgid;        /* Effective GID of creator */
    unsigned short mode;        /* Permissions */
    unsigned short __seq;       /* Sequence number */
};

消息队列函数

创建

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);

删除

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
  • 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
  • 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
  • 特性方面:IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核

信号量

信号量主要用于同步和互斥。

信号量数据结构

//The semid_ds data structure is defined in <sys/sem.h> as follows:

struct semid_ds {
    struct ipc_perm sem_perm;  /* Ownership and permissions */
    time_t          sem_otime; /* Last semop time */
    time_t          sem_ctime; /* Last change time */
    unsigned long   sem_nsems; /* No. of semaphores in set */
};

//The ipc_perm structure is  defined  as  follows  (the  highlighted  fields  are  settable  using
IPC_SET):

struct ipc_perm {
    key_t          __key; /* Key supplied to semget(2) */
    uid_t          uid;   /* Effective UID of owner */
    gid_t          gid;   /* Effective GID of owner */
    uid_t          cuid;  /* Effective UID of creator */
    gid_t          cgid;  /* Effective GID of creator */
    unsigned short mode;  /* Permissions */
    unsigned short __seq; /* Sequence number */
};

信号量函数

创建

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

删除

int semctl(int semid, int semnum, int cmd, ...);

进程互斥

  • 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥
  • 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
  • 在进程中涉及到互斥资源的程序段叫临界区
  • 特性方面:IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核

总结

综上所述,我们可以发现,这三种通信方式的数据结构很相似。接口相似,并且数据结构的第一个数据类型是完全一样的(struct ipc_perm)!这是由于,在内核中,所有的ipc资源都是通过数组组织起来的。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/547999.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【react 全家桶】react-Hook(上)

本人大二学生一枚&#xff0c;热爱前端&#xff0c;欢迎来交流学习哦&#xff0c;一起来学习吧。 <专栏推荐> &#x1f525;&#xff1a;js专栏 &#x1f525;&#xff1a;vue专栏 &#x1f525;&#xff1a;react专栏 文章目录 14【react-Hook &#xff08;上&#x…

计算机网络基础知识(六)—— 什么是HTTP协议?你听我说

文章目录 01 | 基本概念02 | 工作原理 && 特点03 | URI && URL04 | 消息结构05 | 请求方法06 | http响应头信息07 | 状态码08 | HTTP的常见安全机制09 | HTTP的缓存机制10 | HTTP/2 && HTTP/3新特性11 | 面试中常见问题 超文本传输协议&#xff08;Hyp…

【前端知识】Cookie, Session,Token和JWT的发展及区别(四)

【前端知识】Cookie, Session,Token和JWT的发展及区别&#xff08;四&#xff09; 9. JWT9.1 JWT的背景及定义&#xff08;1&#xff09;JWT的字面理解&#xff08;2&#xff09;JWT与传统Token的区别 9.2 JWT的组成&#xff08;1&#xff09; Header&#xff08;头部&#xff…

【UDS】诊断故障代码老化机制

文章目录 简介相关术语1. 老化计数器 Ageing counter2. 诊断故障代码已老去 DTC aged3. 已老去计数器 Aged counter4. 操作循环 Operation cycle5. 诊断故障代码老化机制 DTC aging mechanism 总结 ->返回总目录<- 简介 诊断故障代码&#xff08;DTC&#xff09;一旦生…

【IDEA使用指南】使用Hibernate框架的Java项目,如何找到并打开 “Import Database Schema”窗口?

【IDEA使用指南】使用Hibernate框架的Java项目&#xff0c;如何找到并打开 “Import Database Schema”窗口&#xff1f; 背景&#xff1a; 使用 Hibernate 框架时&#xff0c;假如在 “Import Database Schema” 窗口&#xff08;如下图所示&#xff09;时&#xff0c;点击了…

day06_Java中的流程控制语句

流程控制 简单来讲所谓流程就是完成一件事情的多个步骤组合起来就叫做一个流程。在一个程序执行的过程中&#xff0c;各条语句的执行顺序对程序的结果是有直接影响的。我们必须清楚每条语句的执行流程。而且&#xff0c;很多时候要通过控制语句的执行顺序来实现我们想要的功能…

.Net6 使用aspose.cells23.5.0破译

一、测试代码 internal class Program { static void Main(string[] args) { WorkbookDesigner wb new WorkbookDesigner(new Workbook()); var style new CellsFactory().CreateStyle(); style.Borders.SetColor(C…

路径规划算法:基于缎蓝园丁鸟算法的路径规划算法- 附代码

路径规划算法&#xff1a;基于缎蓝园丁鸟优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于缎蓝园丁鸟优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能…

交换式以太网的诞生

电路交换&#xff0c;终端(电话)独占端线路自然而然&#xff0c;天经地义&#xff0c;可计算机收发的是数据包(即数据分组)&#xff0c;当多台终端接入到同一个共享介质的网络&#xff0c;所有终端可 “同时” 收发数据&#xff0c;一起统计复用网络&#xff0c;多台终端如何协…

Linux常用命令——htop命令

在线Linux命令查询工具 htop [非内部命令]一个互动的进程查看器&#xff0c;可以动态观察系统进程状况。 补充说明 htop命令 是Linux系统中的一个互动的进程查看器&#xff0c;一个文本模式的应用程序(在控制台或者X终端中)&#xff0c;需要ncurses。 与Linux传统的top相比…

【5.20】五、安全测试——概念与漏洞

目录 5.1 安全测试概述 5.1.1 什么是安全测试 5.1.2 安全测试的基本原则 5.2 常见的安全漏洞 5.2.1 SQL注入 5.2.2 XSS跨站脚本攻击 5.2.3 CSRF攻击 软件安全测试是软件测试的重要研究领域&#xff0c;它是保证软件能够安全使用的最主要手段&#xff0c;做好软件安全测试…

pg事务:multixact

什么是multixact&#xff1f; 在对同一行加锁时&#xff0c;元组上关联的事务ID可能有多个&#xff0c;pg将多个事务ID组合起来用一个MultiXactID来管理。TransactionId和MultiXactID是多对一的关系 multixactID跟TransactionId一样&#xff0c;也是32位&#xff0c;同样有wra…

Seata AT 模式理论学习及部分源码解析 | Spring Cloud 52

理论部分来自Seata官网&#xff1a;http://seata.io/zh-cn/docs/dev/mode/at-mode.html 一、前提 基于支持本地 ACID 事务的关系型数据库。Java 应用&#xff0c;通过 JDBC 访问数据库。 二、整体机制 两阶段提交协议的演变&#xff1a; 一阶段&#xff1a;业务数据和回滚日…

【SpringCloud组件——Ribbon(负载均衡)】

一、Ribbon主要作用在哪一环节&#xff1f; 流程讲解&#xff1a; 案例依然采用Eureka章节提供的案例&#xff0c;orderService根据服务名称发起请求&#xff0c;请求传达至Ribbon&#xff0c;此时Ribbon从Eureka中心拉取userService服务列表&#xff0c;Ribbon根据负载均衡算法…

组合预测模型 | ARIMA-WOA-CNN-LSTM时间序列预测(Python)

组合预测模型 | ARIMA-WOA-CNN-LSTM时间序列预测&#xff08;Python&#xff09; 目录 组合预测模型 | ARIMA-WOA-CNN-LSTM时间序列预测&#xff08;Python&#xff09;预测结果基本介绍程序设计参考资料 预测结果 基本介绍 ARIMA-WOA-CNN-LSTM是一种结合了传统时间序列模型和深…

ChatGPT应用场景巡航之广告文案

此文为ChatGPT应用场景巡航第二篇&#xff1a;广告文案。 写出成功的文案&#xff0c;需要专业的技术水准&#xff0c;如果加以辅助工具&#xff0c;那会更加如虎添翼&#xff0c;事半功倍&#xff0c;本文会给大家介绍一下广告文案的写作技巧和辅助工具的使用。 01 — 指导原…

8-《性能优化》

8-《性能优化》 1 启动优化1.1.冷启动耗时统计&#xff1f;1.2.TraceView和System Trace1.3.优雅获取方法耗时1.4.启动速度优化小技巧1.5.启动优化之异步初始化1.6.启动优化之异步初始化最优解—启动器1.7.延迟初始化1.8.其它方案1.9.启动优化之模拟面试 2 布局优化3 线程优化3…

基于Zynq的雷达10Gbps高速PCIE数据采集卡方案(一)总体设计

2.1 引言 本课题是来源于雷达辐射源识别项目&#xff0c;需要对雷达辐射源中频信号进行采集传输 和存储。本章基于项目需求&#xff0c;介绍采集卡的总体设计方案。采集卡设计包括硬件设计 和软件设计。首先对采集卡的性能和指标进行分析&#xff0c;接着提出硬件的总体设计…

OPCUA 聚合服务器和历史数据服务器

前言 开放自动化是一个热门话题&#xff0c;自动化XML&#xff08;AutomationML&#xff09;&#xff0c;基于信息模型的通信协议&#xff08;OPC UA&#xff09;和工业4.0 管理壳&#xff08;ASS&#xff09; 可谓是开放自动化的三套件。三者相互交叉&#xff0c;相互引用&…