Linux进阶-ipc共享内存

目录

共享内存

shmget():创建或获取共享内存

shmat():映射

shmdt():解除映射

shmctl():获取或设置属性

sem.h文件

sem.c文件

shm.c文件

Makefile文件

执行过程


共享内存

共享内存:将内存进行共享,它允许多个不相关的进程访问同一个逻辑内存,直接将一块裸露的内存放在需要数据传输的进程前,供进程使用。

因此,共享内存是效率最高的一种IPC通信机制,可以在多个进程间共享和传递数据,进程间需要共享的数据被放在共享内存区域所有需要访问该共享内存的进程都要把该共享区域映射到本进程的地址空间中,因此所有进程都可访问共享内存的地址。

但是,共享内存需要进程自己去维护,如同步、互斥等。如进程1在读取共享内存的数据时,进程2却修改了共享内存的数据,这会导致数据混乱。因此共享内存属于临界资源,在某一时刻只能有一个进程对其操作(读写)。共享内存一般不能单独使用,而是配合信号量、互斥锁等协调机制,让各个进程在高效交换数据时,不会发生数据践踏、破坏等行为。

共享内存思想:进程间虚拟内存空间本来相互独立,不能相互访问,但是可以通过某种方式使得相同的一块物理内存多次映射到不同的进程虚拟空间中,相当于多个进程的虚拟内存空间部分重叠在一起。共享内存少了拷贝的操作,减少了系统开销,因此效率极高

shmget():创建或获取共享内存

shmget()函数会创建或获取一个共享内存对象,并返回共享内存标识符。

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
/*
key:共享内存的标识符IPC_PRIVATE:创建一块新的共享内存0:当shmflg参数设置了IPC_PRIVATE标志,则将创建一块新的共享内存大于0的32位整数:根据shmflg参数来确定操作
size:要创建共享内存的大小,所有的内存分配操作都是以页为单位的,所有即使只申请一个字节的内存,内存也会分配一页。
shmflg:表示创建的共享内存的模式标志参数。标志 | modeIPC_CREAT:如果内核中不存在关键字与key相等的共享内存,则新建一个共享内存;如果存在则返回此共享内存的标识符。IPC_EXCL:如果内核中不存在关键字与key相等的共享内存,则新建一个共享内存;如果存在则报错SHM_HUGETLB:使用“大页面”来分配共享内存,所谓的“大页面”指的是内核为了提高程序性能,对内存实行分页管理时,采用比默认尺寸(4KB)更大的分页,以减少缺页中断。 Linux 内核支持以 2MB 作为物理页面分页的基本单位。SHM_NORESERVE:不在交换分区中为这块共享内存保留空间。
返回值:共享内存的ID。
*/

当调用shmget()函数失败时将产生错误代码:

        EACCES:指定的消息队列已存在,但调用进程没有权限访问它

        EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志

        EINVAL:创建共享内存是参数size小于SHMMIN或大于SHMMAX

        ENFILE:已达到系统范围内打开文件总数的限制

        ENOENT:给定的key不存在任何共享内存,并且未指定IPC_CREAT

        ENOMEM:内存不足,无法为共享内存分配内存

shmat():映射

shmat()函数是把共享内存区域对象映射到调用进程的地址空间。

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
/*
shmid:共享内存ID
shmaddr:如果不为NULL,则系统会根据shmaddr来选择一个合适的内存区域;如果为NULL,则系统会自动选择一个合适的虚拟内存空间地址去映射共享内存
shmflg:SHM_RDONLY:以只读方式映射共享内存SHM_REMAP:重新映射,此时shmaddr不能为NULLNULLSHM:自动选择比shmaddr小的最大页对齐地址
返回值:共享内存的起始地址。
*/

共享内存的映射需注意:

共享内存只能以只读或可读写方式映射,无法以只写方式映射。

shmaddr参数一般设置为NULL,让系统自动地寻找合适的地址。当不为NULL时,SHMFLG必须设置为SHM_RND,系统将会选择比shmaddr小而又最大的页对齐地址(即为SHMLBA的整数倍)作为共享内存区域的起始地址。如果没有设置SHM_RND,那么shmaddr必须是严格的页对齐地址。

shmdt():解除映射

shmdt()函数是解除进程和共享内存间的映射。

#include <sys/types.h>
#include <sys/shm.h>
void shmdt(const void *shmaddr);
/*
shmaddr:映射的共享内存的起始地址
返回值:执行成功:0执行失败:-1,并将错误原因存于errno
*/

注意:该函数并不删除所指定的共享内存区,而只是将先前用shmat()函数映射好的共享内存脱离当前进程,共享内存还是存在于物理内存中。

shmctl():获取或设置属性

shmctl()函数用于获取或设置共享内存的相关属性。

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
/*
shmid:共享内存标识符
cmd:IPC_STAT:获取属性信息,放置到 buf 中。IPC_SET:设置属性信息为 buf 指向的内容。IPC_RMID:删除该共享内存。IPC_INFO:获得关于共享内存的系统限制值信息。SHM_INFO:获得系统为共享内存消耗的资源信息。SHM_STAT:与 IPC_STAT 具有相同的功能,但 shmid 为该 SHM 在内核中记录所有 SHM 信息的数组的下标,因此通过迭代所有的下标可以获得系统中所有 SHM 的相关信息。SHM_LOCK:禁止系统将该 SHM 交换至 swap 分区。SHM_UNLOCK:允许系统将该 SHM 交换至 swap 分区。
buf:共享内存属性信息结构体指针,设置或者获取信息都通过该结构体
*/

一个SHM被交换至swap分区后如果被设置了SHM_LOCK,那么任何访问这个SHM的进程都将会遇到页错误。进程可以通过IPC_STAT后得到的mode来检测SHM_LOCKED信息。

struct shmid_ds {struct ipc_perm shm_perm;     /* 所有权和权限 */size_t shm_segsz;             /* 共享内存尺寸(字节) */time_t shm_atime;             /* 最后一次映射时间 */time_t shm_dtime;             /* 最后一个解除映射时间 */time_t shm_ctime;             /* 最后一次状态修改时间 */pid_t shm_cpid;               /* 创建者 PID */pid_t shm_lpid;               /* 后一次映射或解除映射者 PID */shmatt_t shm_nattch;          /* 映射该 SHM 的进程个数 */...
};struct ipc_perm {key_t __key;           /* 该共享内存的键值 key */uid_t uid;             /* 所有者的有效 UID */gid_t gid;             /* 所有者的有效 GID */uid_t cuid;            /* 创建者的有效 UID */gid_t cgid;            /* 创建者的有效 GID */unsigned short mode;   /* 读写权限 + SHM_DEST + SHM_LOCKED 标记 */unsigned short __seq;  /* 序列号 */
};

sem.h文件

#ifndef __SEM_H
#define __SEM_H#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>int init_sem(int sem_id, int init_value);
int del_sem(int sem_id);
int sem_p(int sem_id);
int sem_v(int sem_id);#endif

sem.c文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>union semun
{int val;                //供semctl()函数cmd = SETVAL时使用struct semid_ds *buf;   //供semctl()汉斯cmd = IPC_STAT|IPC_SET时使用
};/* 初始化信号量 */
int init_sem(int sem_id, int init_value)
{union semun sem_union;sem_union.val = init_value;/* 信号量ID,信号量编号,命令,联合体 */if( semctl( sem_id, 0, SETVAL, sem_union) == -1 ){printf("Initialize semaphore error!\n");return -1;}else{printf("Initialize semaphore!\n");}
}/* 删除信号量 */
int del_sem(int sem_id)
{union semun sem_union;/* 信号量ID,信号量编号,命令,联合体 */if( semctl( sem_id, 0, IPC_RMID, sem_union) == -1 ){printf("Delete semaphore error!\n");return -1;}else{printf("Delete semaphore!\n");}
}/* p操作 */
int sem_p(int sem_id)
{struct sembuf sops;sops.sem_num = 0;               //信号量的编号:0~nsems-1sops.sem_op  = -1;              //表示p操作sops.sem_flg = SEM_UNDO;        //系统自动释放在系统中残留的信号量/* 信号量ID,结构体,信号量数量 */if( semop( sem_id, &sops, 1) == -1 ){perror("p operation error!\n");return -1;}else{printf("p operation successful!\n");return 0;}
}/* v操作 */
int sem_v(int sem_id)
{struct sembuf sops;sops.sem_num = 0;               //信号量的编号:0~nsems-1sops.sem_op  = 1;               //表示v操作sops.sem_flg = SEM_UNDO;        //系统自动释放在系统中残留的信号量/* 信号量ID,结构体,信号量数量 */if( semop( sem_id, &sops, 1) == -1 ){perror("v operation error!\n");return -1;}else{printf("v operation successful!\n");return 0;}
}

shm.c文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "sem.h"int main(int argc, char** argv)
{int sem_id, shm_id;char *addr;pid_t pid;/* 创建信号量:键值,数量,模式 */sem_id = semget((key_t)0x1100, 1, IPC_CREAT|0666);/* 创建共享内存:键值,共享内存大小,模式 */shm_id = shmget((key_t)0x1111, 1024, IPC_CREAT|0666);/* 初始化信号量 */init_sem(sem_id, 0);if( ( pid = fork() ) == -1){perror("rork error!\n");}else if(pid == 0)      //子进程{printf("Child process will wait for some seconds...\n");sleep(3);/* 映射共享内存,映射地址系统自动分配 */if( ( addr = shmat(shm_id, NULL, 0) ) == (void *)-1 ){printf("child shmat error!\n");exit(-1);}memcpy(addr, "hello couvrir", 14);printf("the child process is running...\n");sem_v(sem_id);}else                   //父进程{sem_p(sem_id);printf("the father process is running...\n");/* 映射共享内存,映射地址系统自动分配 */if( ( addr = shmat(shm_id, NULL, 0) ) == (void *)-1 ){printf("father shmat error!\n");exit(-1);}printf("share memory string:%s\n", addr);/*解除共享内存映射*/shmdt(addr);sem_v(sem_id);del_sem(sem_id);}exit(0);
}

Makefile文件

照旧

执行过程

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

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

相关文章

小程序中如何使用自定义组件应用及搭建个人中心布局

一&#xff0c;自定义组件 从小程序基础库版本 1.6.3 开始&#xff0c;小程序支持简洁的组件化编程。所有自定义组件相关特性都需要基础库版本 1.6.3 或更高。 开发者可以将页面内的功能模块抽象成自定义组件&#xff0c;以便在不同的页面中重复使用&#xff1b;也可以将复杂的…

02、MySQL-------主从复制

目录 七、MySql主从复制启动主从复制&#xff1a;原理&#xff1a;实现&#xff1a;1、创建节点2、创建数据库3、主从配置1、主节点2、从节点 4、测试&#xff1a;5、问题&#xff1a;1、uuid修改2、service_id3、读写不同步方法1&#xff1a;方法2&#xff1a; 七、MySql主从复…

pip install huggingface_hub时报错

pip install huggingface_hub时报错&#xff1a; 可以尝试&#xff1a;pip install --upgrade huggingface_hub 进行安装 方法参考了&#xff1a;https://blog.csdn.net/m0_72295867/article/details/132060750

HTTP基础

HTTP请求报文格式 HTTP 的请求报文分为三个部分 请求行&#xff08;Request Line&#xff09;、请求头&#xff08;Request Header&#xff09;和请求体&#xff08;Request Body&#xff09;。请求体是HTTP请求的核心&#xff0c;其中包含了需要上传服务器的数据。常见的请求…

macOS查端口占用进程

java开发人员&#xff0c;端口冲突的问题基本都遇到过吧&#xff01;以下的日志是否熟悉&#xff1a; *************************** APPLICATION FAILED TO START ***************************Description:The Tomcat connector configured to listen on port 8084 failed to …

小程序技术在信创操作系统中的应用趋势:适配能力有哪些?

小程序技术在信创操作系统中的应用前景非常广阔&#xff0c;但也面临着一些挑战和问题。开发者需要积极应对这些挑战和问题&#xff0c;为信创操作系统的发展和推广做出贡献。同时&#xff0c;开发者也需要关注小程序技术在信创操作系统中的应用趋势&#xff0c;积极探索新的应…

【Django 03】QuerySet 和 Instance应用

1. DRF QuerySet 和 Instance功能概述 1.1 QuerySet 从数据库中查询结果存放的集合称为 QuerySet。 Django ORM用到三个类&#xff1a;Manager、QuerySet、Model。每个Model都有一个默认的 manager实例&#xff0c;名为objects。Django的ORM通过Mode的objects属性提供各种数据…

Java 常用类(包装类)

目录 八大Wrapper类包装类的分类 装箱和拆箱包装类和基本数据类型之间的转换常见面试题 包装类方法包装类型和String类型的相互转换包装类常用方法&#xff08;以Integer类和Character类为例&#xff09;Integer类和Character类的常用方法 Integer创建机制&#xff08;面试题&a…

划词搜索IP插件

插件背景 浏览器插件可以让用户根据个人工作及日常需求来定制浏览器的功能和界面。当用户在网页上看到一些IP地址时&#xff0c;或许会好奇它们的来源和归属。传统的做法是&#xff0c;用户需要复制这个IP地址&#xff0c;然后跳转到埃文科技旗下的http://IPUU.net网站进行查询…

基于Java的考研信息查询系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

速卖通测评补单的条件和步骤

速卖通测评补单对于卖家来说是一种低成本、高回报的推广营销方式&#xff0c;可以提高商品的流量、转化率、关键词质量分和链接权重。但是如果处理不当&#xff0c;可能会对店铺产生负面影响。下面是实现自养号给自己店铺测评的条件和步骤&#xff1a; 1. 稳定的测评环境系统 …

Docker是一个流行的容器化平台,用于构建、部署和运行应用程序。

文章目录 Web应用程序数据库服务器微服务应用开发环境持续集成和持续部署 (CI/CD)应用程序依赖项云原生应用程序研究和教育 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&#xff1a;…

react配置 axios

配置步骤&#xff08;基本配置&#xff09;&#xff1a; 1.安装 axios cnpm install axios --save2.src/utils 新建一个 request.js文件(没有utils就新建一个目录然后再建一个request.js) 3.request代码如下&#xff1a; 这个是最简单的配置了&#xff0c;你可以根据自己的需…

链表的概念+MySingleList的实现

文章目录 链表一、 链表的概念1.概念2. 结构 二、MySingleList的实现1 .定义内部类2 .创建链表3. 遍历链表并打印4.查找单链表中是否包含关键字key5.得到链表的长度6.头插法7. 尾插法8.任意位置插入8.删除结点清空 链表 顺序存储&#xff1a;顺序表/ArrayList 优点&#xff1…

【数据库范式】实际案例分析

前言 在日常业务研发过程中&#xff0c;我们常常需要与数据库表打交道。设计范式是数据表设计的基本原则&#xff0c;对于数据表的设计范式&#xff0c;我们特别容易忽略它的存在。很多时候&#xff0c;当数据库运行了一段时间之后&#xff0c;我们才发现数据表设计上有问题。然…

linux常见命令-文件目录类

9.4 文件目录类 &#xff08;1&#xff09;pwd 指令:显示当前工作目录的绝对路径 &#xff08;2&#xff09;Is指令:查看当前目录的所有内容信息 基本语法&#xff1a; ls [选项,可选多个] [目录或是文件] 常用选项:-a:显示当前目录所有的文件和目录&#xff0c;包括隐藏的…

钢铁异常分类 few-shot 问题 小陈读paper 钢铁2

很清爽的 abstract 给出链接 前面的背景意义 其实 是通用的 这里替大家 整理一吓吓 1 缺陷分类在钢铁表面缺陷检测中 有 意义。 2 大多数缺陷分类模型都是基于完全监督的学习&#xff0c; 这需要大量带有图像标签的训练数据。 在工业场景中收集有缺陷的图像是非常困难…

C++入门(c++历史篇)

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 重点 1. 什么是C2. C的发展…

RTOS(6)任务管理

任务状态理论 我们是怎么实现&#xff0c;两个同优先级的任务之间交替执行的呢&#xff1f; 任务切换的基础&#xff1a;tick中断&#xff01; tick为1ms一个周期&#xff0c;可以通过修改时钟配置修改&#xff1b; running&#xff1a;正在进行的任务3为running&#xff…

`SQL`编写判断是否为工作日函数编写

SQL编写判断是否为工作日函数编写 最近的自己在写一些功能,遇到了对于工作日的判断,我就看了看sql,来吧!~(最近就是好疲惫) 我们一起看看(针对ORACLE) 1.声明: CREATE OR REPLACE PACKAGE GZYW_2109_1214.PKG_FUN_GETDAY_HDAY AS /** * 通过节假日代码获取指定的日期[查找基…