草庐IT

Linux进程通信 | 共享内存

Wayne_hzw 2023-03-28 原文

一、共享内存是什么

在Linux系统中,共享内存是一种IPC(进程间通信)方式,它可以让多个进程在物理内存中共享一段内存区域。

这种共享内存区域被映射到多个进程的虚拟地址空间中,使得多个进程可以直接访问同一段物理内存区域中的数据,从而实现进程间的高速数据交换和通信。

二、共享内存的原理

共享内存基于内核的支持。在共享内存中,内核维护了一块物理内存区域,并将其映射到多个进程的虚拟地址空间中。每个进程都可以使用指针来访问共享内存区域中的数据,就像它们访问自己的内存一样。

三、共享内存的使用方法

相关函数介绍

shmget函数

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

用于创建或打开一个共享内存区段,具体参数如下:

参数 类型 说明
key key_t 共享内存区段的关键字,用于在多个进程间标识同一个共享内存区段。
size size_t 共享内存区段的大小,以字节为单位。
shmflg int 共享内存区段的访问权限和行为属性。

函数返回值为共享内存区段的标识符 shmid,用于标识已创建或已打开的共享内存区段。

shmat函数

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

用于将共享内存区段连接到当前进程的地址空间,具体参数如下:

参数 类型 说明
shmid int 共享内存区段的标识符,用于标识已创建或已打开的共享内存区段。
shmaddr const void* 共享内存区段连接到当前进程地址空间的起始地址,如果为 NULL,则由系统自动选择一个地址。
shmflg int 标志参数,指定共享内存区段的访问权限和行为属性。

函数返回值为共享内存区段连接到当前进程地址空间的起始地址,即指向共享内存区段的指针。

shmdt函数

int shmdt(const void *shmaddr);

用于断开进程与共享内存区段的连接,具体参数如下:

参数 类型 说明
shmaddr const void* 共享内存区段连接到当前进程地址空间的起始地址。

函数返回值为 0 表示成功,-1 表示失败。

shmctl函数

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

用于控制共享内存区段的行为,如删除、获取、设置共享内存区段的属性等,具体参数如下:

参数 类型 说明
shmid int 共享内存区段的标识符,用于标识已创建或已打开的共享内存区段。
cmd int 控制命令,指定对共享内存区段的操作类型。
buf struct shmid_ds* 指向共享内存区段属性结构体的指针,用于获取或设置共享内存区段的属性。

常用的cmd参数包括:

  • IPC_STAT:获取共享内存的状态信息,并将该信息存储在buf参数指向的结构体中。
  • IPC_SET:设置共享内存的状态信息,buf参数指向要设置的新值。
  • IPC_RMID:删除共享内存。

函数返回值为操作成功返回 0,失败返回 -1。

实例演示

以下是一个示例代码,其中一个程序用于写入共享内存,另一个程序用于读取共享内存。

写入程序

/* write.c */

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

#define SHM_SIZE 1024

int main()
{
    int shmid;
    char *shmaddr;
    char write_buf[SHM_SIZE];

    // 创建共享内存段
    shmid = shmget((key_t)1234, SHM_SIZE, 0666|IPC_CREAT);
    if (shmid == -1) {
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }

    // 将共享内存段连接到当前进程
    shmaddr = (char*)shmat(shmid, 0, 0);
    if (shmaddr == (void*)-1) {
        perror("shmat failed");
        exit(EXIT_FAILURE);
    }

    // 从标准输入读取数据并将其写入共享内存
    while(1) {
        fgets(write_buf, SHM_SIZE, stdin);
        strncpy(shmaddr, write_buf, SHM_SIZE);
        if (strncmp(write_buf, "exit", 4) == 0) {
            break;
        }
    }

    // 断开共享内存连接
    if (shmdt(shmaddr) == -1) {
        perror("shmdt failed");
        exit(EXIT_FAILURE);
    }

    // 删除共享内存段
    if (shmctl(shmid, IPC_RMID, 0) == -1) {
        perror("shmctl failed");
        exit(EXIT_FAILURE);
    }
    
    printf("write exit\n");

    return 0;
}

读取程序

/* read.c */

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

#define SHM_SIZE 1024

int main()
{
    int shmid;
    char *shmaddr;
    char read_buf[SHM_SIZE];

    // 获取共享内存段
    shmid = shmget((key_t)1234, SHM_SIZE, 0666|IPC_CREAT);
    if (shmid == -1) {
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }

    // 将共享内存段连接到当前进程
    shmaddr = (char*)shmat(shmid, 0, 0);
    if (shmaddr == (void*)-1) {
        perror("shmat failed");
        exit(EXIT_FAILURE);
    }

    // 从共享内存读取数据并输出到标准输出
    while(1) {
        strncpy(read_buf, shmaddr, SHM_SIZE);
        printf("Received message: %s\n", read_buf);
        if (strncmp(read_buf, "exit", 4) == 0) {
            printf("Received exit\n");
            break;
        }
        sleep(1);
    }

    // 断开共享内存连接
    if (shmdt(shmaddr) == -1) {
        perror("shmdt failed");
        exit(EXIT_FAILURE);
    }

    printf("read exit\n");
    
    return 0;
}
  • 编译这两个程序
gcc -o write write.c
gcc -o read read.c

在一个终端窗口中运行write, 输入消息,在另一个终端窗口中运行read,查看消息。

要退出程序,则在运行write的终端窗口中键入"exit",效果如下:


四、 共享内存的注意事项

  • 使用共享内存需要注意以下几点:
  1. 同步问题:由于多个进程可以同时访问共享内存,因此必须要使用同步机制来保证数据的一致性和正确性。
  2. 内存泄漏:如果一个进程崩溃或者没有及时解除共享内存映射,就有可能导致内存泄漏的问题。
  3. 安全问题:共享内存是多个进程共享的,因此必须要注意数据的安全性和隐私性,避免敏感数据泄露。

五、 共享内存的使用技巧

  • 以下是一些共享内存的使用技巧:
  1. 分配内存时使用 shmget() 系统调用中的 IPC_PRIVATE 标记,可以确保共享内存的键值在系统中是唯一的,避免冲突和安全问题。
  2. 在读写共享内存之前,使用信号量或互斥锁等同步机制来保证数据的一致性和正确性。
  3. 在使用共享内存时,可以将共享内存区域按照固定大小进行分块,避免多个进程同时访问同一块内存区域的冲突。

小结

共享内存是一种高效、灵活、方便的进程间通信方式,可以用于在多个进程之间共享大量数据。

使用共享内存需要注意同步、安全和内存泄漏等问题,可以使用信号量、互斥锁等同步机制来保证数据的正确性。

以上,如果觉得对你有帮助,点个赞再走吧,这样@知微之见也有更新下去的动力!

也欢迎私信我,一起交流!

有关Linux进程通信 | 共享内存的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  3. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  4. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  5. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  6. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  7. ruby - 无法在 Ruby 中将 ffmpeg 作为子进程运行 - 2

    我正在尝试使用以下代码通过将ffmpeg实用程序作为子进程运行并获取其输出并解析它来确定视频分辨率:IO.popen'ffmpeg-i'+path_to_filedo|ffmpegIO|#myparsegoeshereend...但是ffmpeg输出仍然连接到标准输出并且ffmepgIO.readlines是空的。ffmpeg实用程序是否需要一些特殊处理?或者还有其他方法可以获得ffmpeg输出吗?我在WinXP和FedoraLinux下测试了这段代码-结果是一样的。 最佳答案 要跟进mouviciel的评论,您需要使用类似pope

  8. Ruby 守护进程导致 ActiveRecord 记录器 IOError - 2

    我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame

  9. 键删除后 ruby​​ 哈希内存泄漏 - 2

    你好,我无法成功如何在散列中删除key后释放内存。当我从哈希中删除键时,内存不会释放,也不会在手动调用GC.start后释放。当从Hash中删除键并且这些对象在某处泄漏时,这是预期的行为还是GC不释放内存?如何在Ruby中删除Hash中的键并在内存中取消分配它?例子:irb(main):001:0>`ps-orss=-p#{Process.pid}`.to_i=>4748irb(main):002:0>a={}=>{}irb(main):003:0>1000000.times{|i|a[i]="test#{i}"}=>1000000irb(main):004:0>`ps-orss=-p

  10. ruby - 在 ruby​​ 中生成一个进程,捕获 stdout,stderr,获取退出状态 - 2

    我想从ruby​​rake脚本运行一个可执行文件,比如foo.exe我希望将foo.exe的STDOUT和STDERR输出直接写入我正在运行rake任务的控制台.当进程完成时,我想将退出代码捕获到一个变量中。我如何实现这一目标?我一直在玩backticks、process.spawn、system但我无法获得我想要的所有行为,只有部分更新:我在Windows上,在标准命令提示符下,而不是cygwin 最佳答案 system获取您想要的STDOUT行为。它还返回true作为零退出代码,这可能很有用。$?填充了有关最后一次system调

随机推荐