Linux进程间通信(下)之共享内存实践

简介: Linux进程间通信(下)之共享内存实践

上节和上上节我们分享了Linux进程间通信的管道、消息队列、信号以及信号量的基本原理和实践,文章如下:


Linux进程间通信(上)之管道、消息队列实践


Linux进程间通信(中)之信号、信号量实践


这节我们就来分享一下Linux的最后一种进程间通信的方式:共享内存。

1、什么是共享内存

共享内存就是两个不相关的进程之间可以直接访问同一段内存,共享内存在两个正在运行的进程之间共享和传递数据起到了非常有效的方式。在不同的进程之间共享的内存通常安排为同一段物理内存,进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以直接访问共享内存中的地址。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程;其实就是映射一段能够被其它内存所访问到的内存,这段内存由一个进程创建,但是多个进程都可以去访问。共享内存是最快的IPC方式,它是通过其它通信方式的效率不足而专门设计的。往往都是和其它通信机制配合使用,来实现进程间的同步和通信。


共享内存的使用和信号量其实也是差不多的,都是使用接口的形式,共享内存的接口比信号量的接口更加的简单,我们一起去了解下共享内存的使用。


共享内存函数由shmget、shmat、shmdt、shmctl四个函数组成。我们下面来分析每一个函数的用法。

1.1、创建共享内存

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

第一个参数是共享内存段的命名,shmget成功时返回一个关于key相关的标识符,用于后续的共享内存函数。当调用失败返回-1。其它进程也可以通过shmget函数返回值访问同一个共享内存。第二个参数是指定共享内存的容量;第三个shmflg是一个权限标志,它的作用和open和mode函数都是相同的,当共享内存不存在的时候则通过IPC_CREAT来创建。共享内存的权限标准和文件读写的权限一样。

1.2、启动对共享内存的访问

void *shmat(int shm_id, const void *shm_addr, int shmflg);

当我们第一次创建完共享内存时,它还不能被任何进程访问,shmat函数就是用来启动对共享内存的访问,并把共享内存连接到当前进程的地址空间。


shm_id是由shmget函数返回的共享内存标识;shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。最后一个参数是标志位通常都是0。调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1。

1.3、共享内存从当前内存中分离

int shmdt(const void *shmaddr);

这个函数只是从共享内存中分离而不是删除,这一点要分清楚,对于初学者而言这里很容易掉坑,使共享内存在当前进程中不可再用。


参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1。

1.4、控制共享内存

int shmctl(int shm_id, int command, struct shmid_ds *buf);

第一个参数是shaget函数返回的共享内存标识符;command参数是要采取的操作,它由 IPC_STAT、IPC_SET和IPC_RMID组成,分别IPC_STAT代表把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值;IPC_SET代表如果进程有足够的权限,就可以把共享内存的当前关联值设置为shmid_ds结构中给出的值;IPC_RMID代表删除共享内存段。第三个参数buf代表一个结构指针,它指向共享内存的模式或访问权限的结构。


shmid_ds结构至少包括以下成员:

struct shmid_ds  
{  
  uid_t shm_perm.uid;
  uid_t shm_perm.gid;
  mode_t shm_perm.mode;
};

2、共享内存案例

shm_snd.c

#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main()
{
        int shmid;
        char *shmptr;   
        //创建共享内存
        shmid = shmget(0x66, SHM_SIZE, IPC_CREAT|0666);
        //创建失败
        if(shmid < 0)
        {               
            perror("shmget");
            return -1 ;
        }
        //对共享内存的访问
        shmptr = shmat(shmid, 0, 0);
         if (shmptr == (void *)-1)
        {
            perror("shmat");
            return -2 ;
        }
        // 往共享内存写数据
        strcpy(shmptr, "shmat write ok");
        shmdt(shmptr);
        return 0 ;
}

shm_rcv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main()
{
    int shmid;
    char *shmptr;
    shmid = shmget(0x66, SHM_SIZE, IPC_CREAT|0666);
    if(shmid < 0)
    {
        perror("shmget");
        return -1 ;
    }
    shmptr = shmat(shmid, 0, 0);
    if (shmptr == (void *)-1)
    {
        perror("shmat");
        return -2 ;
    }
    // 从共享内存读数据
    printf("read:%s\n", shmptr);
    shmdt(shmptr);
    return 0 ;
}

运行结果:


先分别编译shm_snd.c和shmrcv.c这两个程序,生成shmrcv和shmsnd这两个可执行程序。

640.png

接下来,首先执行shmsnd,会得到以下结果:

640.png

什么都没有?共享内存创建成功了吗?当然是成功了,可以通过ipcs –m命令查看:

640.jpg

如图上图所示,nattch项下的数字为0那个就是刚刚使用shmsnd这个可执行程序创建的一段共享内存。当然,我们还往共享内存发了shmat write ok这个字符串,下面运行shmrcv这个程序,看看是否能把写进共享内存的数据读出来。

640.png

成功读出。同样的,也可以删除共享内存,如何删除?也一样有两种方法。

(1)使用ipcrm –m shmid可以删除共享内存

如上图,我们已经知道0x66的shmid为1835021,所以只要执行ipcrm –m 1835021命令即可删除,如下图所示,成功删除。

640.png

(2)使用shmctl 函数写入IPC_RMID指令删除共享内存

shmrm.c

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(void)
{
        int shmid ;
        //同样,首先先打开共享内存
        shmid = shmget(0x66 , 0 , 0);
        if(-1 == shmid)
        {
                perror("open  shmkey 0x66 fail");
                return -1 ;
        }
        //成功的话,向shmctl写入参数,IPC_RMID表示立刻删除,后面的参数被忽略,为0
        int ret ;
        //写入的是参数
        ret = shmctl(shmid , IPC_RMID , NULL);
        if(ret < 0)
        {
                perror("remove shm fail");
                return -2 ;
        }
        printf("remove key:%d success ... \n" , 0x66);
        return 0 ;
}

运行结果:

640.png

往期精彩

Linux进程间通信(中)之信号、信号量实践


Linux进程间通信(上)之管道、消息队列实践


【Linux系统编程】IO标准缓冲区


替代传统串口屏的Yoxios了解一下!

目录
相关文章
|
3天前
|
存储 缓存 安全
Java内存模型深度解析:从理论到实践####
【10月更文挑战第21天】 本文深入探讨了Java内存模型(JMM)的核心概念与底层机制,通过剖析其设计原理、内存可见性问题及其解决方案,结合具体代码示例,帮助读者构建对JMM的全面理解。不同于传统的摘要概述,我们将直接以故事化手法引入,让读者在轻松的情境中领略JMM的精髓。 ####
19 6
|
15天前
|
关系型数据库 MySQL Linux
Linux环境下MySQL数据库自动定时备份实践
数据库备份是确保数据安全的重要措施。在Linux环境下,实现MySQL数据库的自动定时备份可以通过多种方式完成。本文将介绍如何使用`cron`定时任务和`mysqldump`工具来实现MySQL数据库的每日自动备份。
37 3
|
16天前
|
存储 监控 Java
深入理解计算机内存管理:优化策略与实践
深入理解计算机内存管理:优化策略与实践
|
22天前
|
消息中间件 存储 Linux
|
1月前
|
监控 Linux 云计算
Linux操作系统在云计算环境中的实践与优化###
【10月更文挑战第16天】 本文探讨了Linux操作系统在云计算环境中的应用实践,重点分析了其在稳定性、安全性和高效性方面的优势。通过具体案例,阐述了Linux如何支持虚拟化技术、实现资源高效分配以及与其他开源技术的无缝集成。文章还提供了针对Linux系统在云计算中的优化建议,包括内核参数调整、文件系统选择和性能监控工具的应用,旨在帮助读者更好地理解和应用Linux于云计算场景。 ###
42 3
|
1月前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
32 0
Linux c/c++之IPC进程间通信
|
1月前
|
Linux C++
Linux c/c++进程间通信(1)
这篇文章介绍了Linux下C/C++进程间通信的几种方式,包括普通文件、文件映射虚拟内存、管道通信(FIFO),并提供了示例代码和标准输入输出设备的应用。
30 0
Linux c/c++进程间通信(1)
|
1月前
|
Ubuntu Linux
Linux实践|设置静态 IP 地址
Linux实践|设置静态 IP 地址
63 0
Linux实践|设置静态 IP 地址
|
1月前
|
SQL 安全 算法
ChatGPT高效提问—prompt实践(漏洞风险分析-重构建议-识别内存泄漏)
ChatGPT高效提问—prompt实践(漏洞风险分析-重构建议-识别内存泄漏)
|
1月前
|
程序员 编译器 数据处理
【C语言】深度解析:动态内存管理的机制与实践
【C语言】深度解析:动态内存管理的机制与实践
下一篇
无影云桌面