Linux应用编程---8.共享内存

news2024/11/18 14:37:07

Linux应用编程—8.共享内存

​ 共享内存是进程之间通讯的方式。大概原理是先申请一块共享内存,然后通过“映射”,映射到进程中。进程中读写这块被映射过来的内存,共享内存也会随之改变,同理其它进程也能做相同的操作。所以,两个不同的进程通过共享内存实现了通讯。

8.1 创建共享内存

​ 创建共享内存,使用到的库函数是:shmget(),是:share memory get的缩写。在Linux编程手册中查看这个函数。

NAME
       shmget - allocates a System V shared memory segment

SYNOPSIS
       #include <sys/ipc.h>
       #include <sys/shm.h>

       int shmget(key_t key, size_t size, int shmflg);

​ shmge()函数,用来分配一个“System v”的共享内存段。使用时需要包含头文件:sys/ipc.h与sys/shm.h。函数原型是:int shmget(key_t key, size_t size, int shmflg);需要传入3个参数,返回值是int类型的。

DESCRIPTION
shmget()  returns  the identifier of the System V shared memory segment associated with the value of the argument key.  It may be  used  either to  obtain the identifier of a previously created shared memory segment(when shmflg is zero and key does not have the value  IPC_PRIVATE), or to create a new set.

​ shmget()函数返回与参数key的值相关联的System v共享内存段的标识符。它即可用于获取先前创建的共享内存段的标识符,当shmflg为0且key没有值IPC_PRIVATE时,也可以用于创建一个新的共享内存段。

A  new  shared  memory  segment,  with  size equal to the value of size rounded up to a multiple of PAGE_SIZE, is created if key has the  value IPC_PRIVATE  or  key isn't IPC_PRIVATE, no shared memory segment corre‐sponding to key exists, and IPC_CREAT is specified in shmflg.

​ 如果key的值为IPC_PRIVATE或key不是IPC_PRIVATE,不存在与key对应的共享内存段,并且在shmflg中指定了IPC_CREAT,则创建一个新的共享内存段,其大小等于size的值的四舍五入到PAGE_SIZE的整数倍。

If shmflg specifies both IPC_CREAT and IPC_EXCL  and  a  shared  memory segment  already  exists for key, then shmget() fails with errno set to EEXIST.  (This is analogous to the effect of the combination O_CREAT | O_EXCL for open(2).)

​ 如果shmflg同时指定了IPC_CREAT和IPC_EXCL,并且key的共享内存段已经存在,那么shmget()将失败,errno设置为EEXIST。(这类似于open(2)的组合O_CREAT | O_EXCL的效果。)

​ shmflg的取值可以是以下几种:

IPC_CREAT   Create  a  new  segment.   If  this  flag is not used, then shmget() will find the  segment  associated  with  key  and check  to see if the user has permission to access the segment.
IPC_EXCL    This flag is used with IPC_CREAT to ensure that  this  call creates  the  segment.   If the segment already exists, the call fails.
SHM_HUGETLB  (since Linux 2.6) Allocate the segment using "huge  pages."   See  the  Linux kernel  source  file  Documentation/admin-guide/mm/hugetlbpage.rst for further information.

​ 创建一个新的段。如果没有使用此标志,那么shmget()将查找与key相关联的段并检查用户是否有访问该段的权限。此标志与IPC_CREAT一起使用,以确保该调用创建了段。如果该段已经存在,则调用失败。使用“大页面”分配段。参见Linux内核源文件Documentation/admin-guide/mm/hugetlbpage。RST查询进一步信息。

​ shmget()函数返回值:

RETURN VALUE
On success, a valid shared memory identifier is returned.  On error, -1 is returned, and errno is set to indicate the error.

​ 函数调用成功,返回一个有效的共享内存标识符,失败则返回-1。

8.2 shmat()、shmdt()函数详情

NAME
       shmat, shmdt - System V shared memory operations
SYNOPSIS
       #include <sys/types.h>
       #include <sys/shm.h>

       void *shmat(int shmid, const void *shmaddr, int shmflg);

       int shmdt(const void *shmaddr);
shmat()
       shmat()  attaches the System V shared memory segment identified by shmid to
       the address space of the calling process.  The attaching address is  speci‐
       fied by shmaddr with one of the following criteria:

       *  If  shmaddr is NULL, the system chooses a suitable (unused) page-aligned
          address to attach the segment.

       *  If shmaddr isn't NULL and SHM_RND is specified in shmflg, the attach oc‐
          curs  at the address equal to shmaddr rounded down to the nearest multi‐
          ple of SHMLBA.

       *  Otherwise, shmaddr must be a page-aligned address at  which  the  attach
          occurs.

       In  addition to SHM_RND, the following flags may be specified in the shmflg
       bit-mask argument:

​ shmat()函数与shmdt()函数是对System v下的共享内存的操作。

​ shmat()函数:

shmat()  attaches the System V shared memory segment identified by shmid to the address space of the calling process.  The attaching address is  specified by shmaddr with one of the following criteria:
If  shmaddr is NULL, the system chooses a suitable (unused) page-aligned address to attach the segment.If shmaddr isn't NULL and SHM_RND is specified in shmflg, the attach occurs  at the address equal to shmaddr rounded down to the nearest multiple of SHMLBA. Otherwise, shmaddr must be a page-aligned address at  which  the  attachoccurs.

​ 该函数通过shmid标识的System v下的共享内存段附加到调用进程的地址空间。附加地址由shmadr指定,使用以下条件之一:如果shmaddr地址为空,系统选择一个合适的页对其地址附加到段。如果地址不为空并且shmflg标志位被指定,则在等于shmaddr四舍五入到SHMLBA最接近倍数的地址处进行附加。否则,shmaddr地址必须是一个与页面对其的地址,在该地址上执行附加操作。

​ shmat()函数返回值:

On success, shmat() returns the address of the attached shared memory  segment;  on  error, (void *) -1 is returned, and errno is set to indicate the cause of the error.

​ 函数调用成功,返回附加共享内存段的首地址,失败,则返回-1.

​ shmdt()函数:

shmdt() detaches the shared memory segment located at the address specified
       by shmaddr from the address space of the calling  process.   The  to-be-de‐
       tached  segment  must be currently attached with shmaddr equal to the value
       returned by the attaching shmat() call.

       On a successful shmdt()  call,  the  system  updates  the  members  of  the
       shmid_ds structure associated with the shared memory segment as follows:

              shm_dtime is set to the current time.

              shm_lpid is set to the process-ID of the calling process.

              shm_nattch  is  decremented by one.  If it becomes 0 and the segment
              is marked for deletion, the segment is deleted.

​ shmdt()用来断开当前进程与共享内存的连接。分Shmdt()离位于指定地址的共享内存段通过shmaddr从调用进程的地址空间。限于检测所连接的段当前必须附加shmadr等于该值由附加的shmat()调用返回。在成功调用shmdt()时,系统更新Shmid_ds结构与共享内存段关联如下:Shm_dtime设置为当前时间。shm_lpid设置为调用进程的进程id。Shm_nattch减1。如果它变成0,这个线段标记为删除,则删除该段。

​ shmdt()函数返回值:

On success, shmdt() returns 0; on error -1 is returned, and errno is set to
       indicate the cause of the error.

​ 函数调用成功,返回0,失败,则返回-1.

8.2 使用共享内存在父子进程之间进行通讯

​ 父进程往共享内存映射到自己进程中的内存写入一串字符串,子进程访问共享内存映射到自己进程中的内存,将该字符串打印出来。

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

char msg[] = "I love VAE!";

int main(void)
{
        pid_t pid;
        int shmid;

        shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT);
        if(-1 == shmid)
                perror("shmget");

        pid = fork();

        if(pid > 0)			// Parent process
        {
                char * p_addr;

                p_addr = shmat(shmid, NULL, 0);

                memset(p_addr, '\0', sizeof(msg));
                memcpy(p_addr, msg, sizeof(msg));

                shmdt(p_addr);

                waitpid(pid, NULL, 0);
        }
        else if(pid == 0)
        {
                char * c_addr;

                c_addr = shmat(shmid, NULL, 0);

                printf("Waits 3 seconds:\n");

                sleep(3);

                printf("Data from parent process by shm is %s.\n", c_addr);
        }
        else
        {
                perror("fork.");
        }

        return 0;
}

​ 运行结果:

root@ubuntu:/home/sgk/Documents/Linux_Program/shared_memory# gcc shared_memory_0.c 
root@ubuntu:/home/sgk/Documents/Linux_Program/shared_memory# ./a.out
Waits 3 seconds:
Data from parent process by shm is I love VAE!.

8.3 共享内存用于非亲缘关系进程之间通讯

​ 共享内存创建函数shmget()的原型是:int shmget(key_t key, size_t size, int shmflag),其中参数key的值是相关联的System v共享内存段的标识符。如果已经创建了key值对应的共享内存段它们则对应了起来。非亲缘关系的进程之间通过共享内存通讯,最重要的一点是:这个共享内存段的标识符是相同的 !!!

​ 建立两个.c文件,分别是:write_shm.c与read_shm.c,在write_shm.c中,使用shmget()创建一个指定key值的共享内存段,然后使用函数shmat()与进程建立连接,并得到该进程下被映射内存的首地址,然后写入数据,最后调用shmdt()断开与共享内存的连接。在read_shm.c中,先同样使用shmget()函数创建共享内存,但是此时传入的key值是之前已经创建好的key值,如果该键值对应的共享内存段已经存在,则联系到一起。同样调用shmat()函数将该key值下的共享内存段映射到本进程,读取出里面的数据并打印,最后使用shmdt()断开共享内存连接。

​ write_shm.c:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>

#define KEY_0 0514

char msg[] = "I love vae!";

int main(void)
{
        int shmid;
        char *p1_addr;

        shmid = shmget(KEY_0, 1024, IPC_CREAT);

        if(-1 == shmid)
                perror("shmget.");

        p1_addr = shmat(shmid, NULL, 0);

        memset(p1_addr, '\0', sizeof(msg));
        memcpy(p1_addr, msg, sizeof(msg));

        printf("Wrote data to shared memory done.\n");

        shmdt(p1_addr);

        return 0;
}

​ read_shm.c:

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

#define KEY_0 0514

int main(void)
{
        int shmid;
        char *p2_addr;

        shmid = shmget(KEY_0, 1024, IPC_CREAT);

        if(-1 == shmid)
                perror("shmget.");

        p2_addr = shmat(shmid, NULL, 0);

        printf("Shared memory's data is: %s\n", p2_addr);

        shmdt(p2_addr);

        return 0;
}

​ 分别编译出可执行文件read与write。

​ 运行结果:

image-20221127104824504

图1 运行结果

​ 如果共享内存的标识符KEY_0不相同,将write_shm.c中的KEY_0保持0514,将read_shm.c中的KEY_0改写为0528。运行结果如下:

image-20221127105537131

图2 运行结果

​ 根据结果可知:这两个进程通讯失败。如果共享内存标识符不同,内核分别在两个进程下创建了新的共享内存。

8.4 shmctl()函数详情

	查看Linux编程手册下关于shmctl()函数定义与使用方法。
NAME
       shmctl - System V shared memory control

SYNOPSIS
       #include <sys/ipc.h>
       #include <sys/shm.h>

       int shmctl(int shmid, int cmd, struct shmid_ds *buf);

​ shmctl()控制System v共享内存段。需要包含两个头文件,函数原型是:int shmctl(int shmid, int cmd, struct shmid_ds *buf);

DESCRIPTION
shmctl()  performs  the control operation specified by cmd on the System V shared memory segment whose identifier is given in shmid.

​ shmctl()函数在shmid中给出标识符的System v共享内存段上执行由cmd指定的控制操作。

​ cmd有效参数如下:

Valid values for cmd are:

       IPC_STAT  Copy information from the kernel data  structure  associated
                 with  shmid  into  the shmid_ds structure pointed to by buf.
                 The caller must have read permission on  the  shared  memory
                 segment.

       IPC_SET   Write  the  values of some members of the shmid_ds structure
                 pointed to by buf to the kernel  data  structure  associated
                 with this shared memory segment, updating also its shm_ctime
                 member.  The following fields can be changed:  shm_perm.uid,
                 shm_perm.gid,   and   (the  least  significant  9  bits  of)
                 shm_perm.mode.  The effective UID  of  the  calling  process
                 must    match    the   owner   (shm_perm.uid)   or   creator
                 (shm_perm.cuid) of the shared memory segment, or the  caller
                 must be privileged.

       IPC_RMID  Mark the segment to be destroyed.  The segment will actually
                 be destroyed only after the last process detaches it  (i.e.,
                 when  the  shm_nattch  member  of  the  associated structure
                 shmid_ds is zero).  The caller must be the owner or  creator
                 of  the  segment, or be privileged.  The buf argument is ig‐
                 nored.

                 If a segment has been marked for destruction, then the (non‐
                 standard)  SHM_DEST  flag  of the shm_perm.mode field in the
                 associated data structure retrieved by IPC_STAT will be set.

                 The caller must ensure that  a  segment  is  eventually  de‐
                 stroyed;  otherwise  its pages that were faulted in will re‐
                 main in memory or swap.

                 See also the description of /proc/sys/kernel/shm_rmid_forced
                 in proc(5).

​ cmd的有效值为:

IPC_STAT从相关的内核数据结构中复制信息将shmid放入buf所指向的shmid_ds结构中。调用者必须对共享内存具有读权限段。

写shmid_ds结构的一些成员的值由buf指向相关的内核数据结构使用这个共享内存段,也更新它的shm_ctime成员。以下字段可以更改:shm_perm.uid,shm_perm。Gid,和(最小有效9位)shm_perm.mode。调用进程的有效UID必须匹配所有者(shm_perm.uid)或创建者(shm_perm.cuid)的共享内存段,或调用者必须有特权。

ipc_rmd标识要销毁的段。这部分实际上只有在最后一个进程分离它(即,当关联结构的shm_nattch成员Shmid_ds为零)。调用者必须是所有者或创建者的部分,或享有特权。buf的论点很重要也没有。

如果一段已被标记为要销毁,则(非‐shm_perm的SHM_DEST标志。的模式字段将设置由IPC_STAT检索的关联数据结构。

呼叫者必须确保一个区段最终被解除毁掉了;否则,有错误的页面将重新制作主内存或交换区。

参见/proc/sys/kernel/shm_rmid_forced的描述在proc(5)。

​ 修改write_shm.c代码,在写结束后,使用shmctl()函数,使用IPC_RMID指令销毁,查看效果。

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>

#define KEY_0 0514

char msg[] = "I love vae!";

int main(void)
{
        int shmid;
        char *p1_addr;

        shmid = shmget(KEY_0, 1024, IPC_CREAT);

        if(-1 == shmid)
                perror("shmget.");

        p1_addr = shmat(shmid, NULL, 0);

        memset(p1_addr, '\0', sizeof(msg));
        memcpy(p1_addr, msg, sizeof(msg));

        printf("Wrote data to shared memory done.\n");

        shmdt(p1_addr);

        shmctl(shmid, IPC_RMID, NULL);

        return 0;
}

​ 分别编译出可执行文件:read与write,然后执行。

运行结果:

image-20221127112006397

图3 运行结果

​ 当调用shmctl()销毁共享内存段后,是彻底销毁了。其它进程读取失败。

8.5 总结

​ 共享内存是进程之间通讯的方式之一,建立共享内存大概分为几步:(1)使用shmget()创建共享内存段;(2)使用shmat()将共享内存段与当前进程绑定;(3)使用shmdt()函数将共享内存与进程断开连接;(4)如需彻底销毁共享内存段,调用shmctl(),传入IPC_RMID。
在这里插入图片描述

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

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

相关文章

dubbo源码实践-protocol层例子

1 概述本文提供了基于protocol层的一个客户端、服务端代码例子。从dubbo 2.7的架构图上可以看到protocol层上在Remoting层之上的&#xff0c;个人理解Protocol层不在讨论客户端和服务端的概念了&#xff0c;开始讨论服务提供者和服务消费者的概念了。参考上一篇dubbo源码实践-p…

基于ngxin一个http模板

1.初始化 static char * ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);static ngx_command_t ngx_http_mytest_commands[] {{ngx_string("mytest"),NGX_HTTP_MAIN_CONF | N…

分布式基础篇2——分布式组件(谷粒商城)

一、SpringCloud Alibaba1、简介2、为什么使用3、版本选择4、依赖选择二、SpringCloud Alibaba 组件1、Nacos作为注册中心2、OpenFeign3、Nacos作为配置中心namespaceData IDGroup同时加载多个配置文件三、Spring Cloud1、GateWay简介三大核心部分网关的使用视频来源: 【Java项…

爬虫学习+实战

爬虫 概念&#xff1a; 网络爬虫&#xff1a;就是模拟客户端发送请求&#xff0c;获取响应数据&#xff0c;一种按照一定的规则&#xff0c;自动地抓取万维网上的信息的程序或者脚本 爬虫分类: 通用爬虫&#xff1a;抓取系统中重要的组成部分。抓取的是一整张页面数据聚焦爬…

I2C总线驱动

一. I2C背景知识 SOC芯片平台的外设分为&#xff1a; 一级外设&#xff1a;外设控制器集成在SOC芯片内部二级外设&#xff1a;外设控制器由另一块芯片负责&#xff0c;通过一些通讯总线与SOC芯片相连 Inter-Integrated Circuit&#xff1a; 字面意思是用于“集成电路之间”的…

SELECT COUNT(*) 会造成全表扫描?回去等通知吧

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/T…

CPU_并行(多线程)不同高性能旋转图片

并行(多线程)不同高性能旋转图片 代码 ImageStuff.h struct ImgProp {int Hpixels;int Vpixels;unsigned char HeaderInfo[54];unsigned long int Hbytes; };struct Pixel {unsigned char R;unsigned char G;unsigned char B; };unsigned char** CreateBlankBMP(); unsigned…

Java中>>,>>=,<<,<<=运算符

今天在刷LeetCode的时候遇到了一个运算符<<&#xff0c;对这个运算符的意思有点模糊&#xff0c;然后便开始面向百度学习&#xff0c;但是发现&#xff0c;很多篇帖子表达的意思太文章化&#xff0c;不够通俗易懂&#xff0c;于是打算写下这篇帖子&#xff0c;让大家能够…

工作笔记——微信支付开发相关知识整理

在最近的工作中&#xff0c;引入了微信小程序支付&#xff0c;在开发过程中积累和整理了一些技术知识&#xff0c;现将其整理如下 目录 一、概念认识 &#xff08;一&#xff09;术语介绍 &#xff08;二&#xff09;名词解释 &#xff08;四&#xff09;对接微信支付接口规…

Win10安卓子系统安装教程

Win10安卓子系统安装教程必要安装文件下载和安装子系统安装方法方法一&#xff1a;安装 WSA PacMan方法二&#xff1a;安装 APK安装程序必要安装文件下载和安装 win10安卓子系统和win11子系统的安装一样&#xff0c;都必须要安装适用于 Android ™的 Windows 子系统设置的软件…

Java设计模式中行为型模式是什么/模板方式又是什么,编程怎么运用

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 6.行为型模式 6.1 概述 6.1.1 特点 用于描述程序在运行时复杂的流程控制&#xff0c;即描述多个类或对象之间怎么相互协作共同完成单个对象都无法单独完成任务涉…

分布式基础篇3——前端开发基础知识(谷粒商城)

前端技术对比一、ES61、简介2、什么是 JavaScript3、ES6新特性3.1 let3.2 const3.3 解构表达式3.4 字符串扩展3.5 函数优化3.6 对象优化3.7 map 和 reduce3.8 Promise3.9 模块化二、Vue1、MVVM 思想2、Vue 简介3、Vue 入门案例4、Vue 指令插值表达式v-text、v-htmlv-bindv-mode…

【CANN训练营第三季】基于昇腾PyTorch框架的模型训练调优

文章目录性能分析工具PyTorch Profiling性能分析工具CANN Profiling结业考核1、使用Pytorch实现LeNet网络的minist手写数字识别。2、采用课程中学习到的手工或者自动迁移方式&#xff0c;将上述脚本迁移到昇腾npu上&#xff0c;单机单卡&#xff0c;提供迁移脚本&#xff0c;突…

YOLOv5视觉AI库安装

打开YOLOv5开源仓库: https://github.com/ultralytics/yolov5/blob/master/README.zh-CN.md下载源码:安装 : pip install -r requirements.txt完成安装目标检测推理可通过PyTorch Hub加载YOLOv5检测模型检测图像并返回数据帧使用YOLOv5要先安装opencv-python和pandas库安装open…

C#,图像二值化(18)——全局阈值的模糊集理论算法(Huang Thresholding)与源程序

1 模糊集理论模糊集理论,也称为模糊集合论,或简单地称为模糊集,1965年美国学者扎德在数学上创立了一种描述模糊现象的方法—模糊集合论。这种方法把待考察的对象及反映它的模糊概念作为一定的模糊集合&#xff0c;建立适当的隶属函数&#xff0c;通过模糊集合的有关运算和变换&…

arduino - pinMode参数1的确定 - 以arduino nano every核心板为例

文章目录arduino - pinMode参数1的确定 - 以arduino nano every核心板为例概述笔记pins_arduino.hABX00028-datasheet.pdf简单的辨认管脚号就照ABX00028-datasheet.pdf来ENDarduino - pinMode参数1的确定 - 以arduino nano every核心板为例 概述 arduino nano every的核心板使…

我的交易抽象思路分享

这几天我老是抛出一些问题给老师们&#xff0c;都是故意而为之&#xff0c;因为我靠这种方式自己引导自己很多年&#xff1b; 比如&#xff1a;龙头真的存在么&#xff1f;为何前几天它还不是龙头&#xff0c;怎么今天就是了&#xff1f; 再如&#xff1a;交易模式和交易系统…

微信小程序解密encryptedData报错pad block corrupted

前要&#xff1a; 今天调试一下微信授权登录的时候老是第一次报错解密失败pad block corrupted&#xff0c;第二次授权的时候正常&#xff0c;因为第一次已经获取到手机号码&#xff01; 后端代码&#xff1a; public static JSONObject getUserInfo(String encryptedData, S…

微信自动回复软件

软件介绍 软件名称&#xff1a; 微信超级管家 适用平台&#xff1a; windows 是否免费&#xff1a; 完全免费 病毒检测&#xff1a; 火绒安全检测通过 流氓检测&#xff1a; 无广告、无弹窗、无其他流氓行为 软件大小&#xff1a; 183M 这个软件依赖的是本地微信客户端&#x…

C++ 初始化列表详解

目录 1.什么是初始化列表 2.什么时候需要使用初始化列表&#xff1f; 3.初始化列表的效率 4.初始化列表的初始化顺序 1.什么是初始化列表 class A { public:A(int value):m_dada(value){}private:int m_dada; }; 如上图&#xff0c;红色圈起来的部分&#xff0c;就是构造函…