首页 > 系统相关 >进程间通信-POSIX 共享内存

进程间通信-POSIX 共享内存

时间:2024-03-07 20:11:22浏览次数:29  
标签:include 函数 int 间通信 POSIX 内存 共享内存 shm

POSIX 共享内存

POSIX 共享内存是一种在 Linux 系统上使用的共享内存机制,它允许多个进程可以访问同一个内存区域,从而实现进程间的数据共享。共享内存是可用IPC机制中最快的,使用共享内存不必频繁拷贝数据。但也需要注意,由于共享内存段中的数据可以被多个进程同时访问,因此需要在程序设计中考虑好数据同步和互斥机制,以避免出现数据竞争和不一致的情况。

共享内存使用的基本步骤:

  • 通过 shm_open() 函数创建了共享内存区域,此时会在 /dev/shm/ 创建共享内存文件。
  • 通过 ftruncate() 函数改变共享内存的大小,一般设置为页大小 sysconf(_SC_PAGE_SIZE) 的整数倍。
  • 通过 mmap() 函数将创建的共享内存文件映射到内存。
  • 通过 munmap() 卸载共享内存。
  • 通过 shm_unlink() 删除内存共享文件。

下面分别介绍这些函数。

创建共享内存-shm_open() 函数

shm_open() 函数是用于创建或打开一个共享内存对象,该函数定义如下:

#include <sys/mman.h>

int shm_open(const char *name, int oflag, mode_t mode);

参数说明

  • name:指定共享内存对象的名称,其命名规则类似于文件系统中的文件名,不同进程可以通过相同的名字来访问同一个共享内存对象。
  • oflag:指定打开方式。
  • mode:创建文件时的权限。

返回值

  • 如果函数执行成功,返回一个文件描述符,可以用于后续操作共享内存对象。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

共享内存对象通过IPC名字描述,当成功创建共享内存对象,系统会以文件形式将其保存在 /dev/shm 目录下。

更改文件大小-ftruncate() 函数

ftruncate() 函数用于更改文件大小,该函数定义如下:

#include <unistd.h>

int ftruncate(int fd, off_t length);

参数说明

  • fd:一个已经打开的文件描述符,用于指定需要更改大小的文件。
  • length:指定文件应当调整到的新大小,单位是字节。

返回值

  • 如果函数执行成功,返回值为0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

如果文件在调整大小后比原来更大,文件的数据将会被扩展,多出的部分以0填充。如果文件在调整大小后比原来更小,多出的部分将会被删除。

在调整文件大小时,尽量选择当前系统页大小的整数倍,可以通过 sysconf(_SC_PAGE_SIZE) 获取当前系统的页大小。

内存映射-mmap()函数

mmap() 函数用于创建内存映射区域,该函数定义如下:

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, 
                       int flags, int fd, off_t offset);

参数说明

  • addr:指定希望内存映射区域的起始地址,通常设为 NULL,由系统自动选择合适的地址。
  • length:指定需要映射的内存区域的长度,单位是字节。
  • prot:用于设置内存区域的保护权限,不能与文件的打开模式冲突。可以是如下值的组合:
    • PROT_NONE:内存区域不可访问。
    • PROT_WRITE:内存区域可以被写入。
    • PROT_READ:内存区域可以被读取。
    • PROT_EXEC:内存区域可以被执行。
  • flags:指定映射对象的类型。下面列出一些常用项:
    • MAP_SHARED:与文件进行共享映射,可以实现多个进程之间共享数据的操作。对映射区域的修改会反映到文件中,同样文件的修改也会反映到映射区域中。
    • MAP_PRIVATE:创建一个私有的映射副本,进程之间不共享数据。对映射区域的修改不会反映到文件中,也不会影响其他映射该文件的进程。
    • MAP_LOCKED:映射区域会被锁定在物理内存中,防止页面被交换出去,保证内存访问速度,但可能会导致内存资源消耗较大。
  • fd:已打开文件的文件描述符,用于与内存映射区域关联。
  • offset:文件中的偏移量,指定文件的起始映射位置。

返回值

  • 如果函数执行成功,返回一个指向映射区域的指针。
  • 如果发生错误,返回 MAP_FAILED(-1) 。可以通过 errno 变量来获取具体的错误信息。

映射方式如下:

数据同步-msync()函数

如果指定了 MAP_SHARED 标志,Linux内核会保持内存映射文件与内存映射区的同步,但这种同步可能不会立即生效,此时,可以使用 msync() 函数来确保数据已经同步完成。该函数定义如下:

#include <sys/mman.h>

int msync(void *addr, size_t length, int flags);

参数说明

  • addr:指向共享内存区域的指针。
  • length:需要同步的共享内存区域的长度。
  • flags:用来指定额外的行为。有如下取值:
    • MS_SYNC:强制将修改同步到存储设备,数据量大时,可能会导致阻塞。
    • MS_ASYNC:将修改排入写队列,不会等待写操作完成。
    • MS_INVALIDATE:标记共享内存区域已经无效,使下次访问该区域时重新从底层存储器加载数据。

返回值

  • 如果函数执行成功,函数返回 0。
  • 如果函数执行失败,函数返回 -1,可以通过 errno 变量来获取具体的错误信息。

注意事项

  • msync() 函数的调用可能会比较耗时,因此需要考虑性能问题。
  • msync() 函数只适用于共享内存,对于普通的内存操作并不适用。

卸载共享内存-munmap()函数

munmap() 函数用于取消指定的地址空间内存映射。该函数定义如下:

#include <sys/mman.h>

int munmap(void *addr, size_t length);

参数说明

  • addr:要取消映射的内存区域的起始地址。是由 mmap() 函数返回的地址。
  • length:要取消映射的内存区域的长度。

返回值

  • 如果函数执行成功,函数返回 0。
  • 如果函数执行失败,函数返回 -1,可以通过 errno 变量来获取具体的错误信息。

注意事项

  • 只能取消由 mmap() 函数创建的内存映射区域,否则会导致未定义行为。
  • 要确保取消映射的地址和长度是有效的,否则会导致程序崩溃或内存泄漏。
  • 取消映射后,原先映射的内存区域就会被释放,程序不应再访问这部分内存。

删除共享内存文件-shm_unlink()函数

shm_unlink()函数用于删除共享内存文件,后续其他进程将无法通过这个名称打开该共享内存对象。该函数定义如下:

#include <sys/mman.h>

int shm_unlink(const char *name);

参数说明

  • name:要删除的 POSIX 共享内存对象的名称。

返回值

  • 如果函数执行成功,函数返回 0。
  • 如果函数执行失败,函数返回 -1,可以通过 errno 变量来获取具体的错误信息。

用例

数据同步

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/mman.h>
 4 #include<unistd.h>
 5 #include<sys/stat.h>
 6 
 7 int main(int argc, char** argv)
 8 {
 9     size_t mem_size = sysconf(_SC_PAGE_SIZE) * 2;
10     int fd;
11 
12 AGAIN:
13     fd = shm_open("/my_shm", O_CREAT | O_RDWR | O_EXCL, 0666);
14     if(fd == -1)
15     {
16         if(errno == EEXIST)
17         {
18             shm_unlink("/my_shm");
19             goto AGAIN;
20         }
21         perror("shm_open");
22         return -1;
23     }
24 
25     int ret = ftruncate(fd, mem_size);
26     if(ret == -1)
27     {
28         perror("ftruncate");
29         return -1;
30     }
31 
32     void *ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
33     if(ptr == MAP_FAILED)
34     {
35         perror("mmap");
36         return -1;
37     }
38 
39     int i = 0;
40     sprintf(ptr, "Data%d", i);
41     while(1);
42         
43     close(fd);
44     munmap(ptr, mem_size);
45     return 0;
46 }

输出:

$ cat /dev/shm/my_shm
Data1

当指定 MAP_SHARED 后,对指针 ptr 的操作,都会同步至 my_shm 文件中,数据是以明文形式存储,可以被其他进程读取。

如果将 MAP_SHARED 改为 MAP_PRIVATE,执行结果如下:

$ cat /dev/shm/my_shm
                //无数据

简单用例

读端代码如下:

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/mman.h>
 4 #include<unistd.h>
 5 #include<sys/stat.h>
 6 #include<errno.h>
 7 
 8 int main(int argc, char** argv)
 9 {
10     size_t mem_size = sysconf(_SC_PAGE_SIZE) * 2;
11     int fd = shm_open("/my_shm", O_CREAT | O_TRUNC | O_RDWR, 0666);
12     if (fd == -1 && errno != EEXIST)
13     {
14         perror("shm_open");
15         return -1;
16     }
17 
18     int ret = ftruncate(fd, mem_size);
19     if(ret == -1)
20     {
21         perror("ftruncate");
22         return -1;
23     }
24 
25     void *ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
26     if(ptr == MAP_FAILED)
27     {
28         perror("mmap");
29         return -1;
30     }
31 
32     while (1)
33     {
34         printf("read data : %s\n", (char *)ptr);
35         sleep(1);
36     }
37 
38     close(fd);
39 
40     return 0;
41 }

写端代码如下:

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/mman.h>
 4 #include<unistd.h>
 5 #include<errno.h>
 6 #include<sys/stat.h>
 7 
 8 int main(int argc, char** argv)
 9 {
10     size_t mem_size = sysconf(_SC_PAGE_SIZE) * 2;
11     int fd;
12     AGAIN:
13         fd = shm_open("/my_shm", O_CREAT | O_TRUNC | O_RDWR, 0666);
14         if (fd == -1)
15         {
16             if (errno == EEXIST)
17             {
18                 goto AGAIN;
19             }
20             perror("shm_open");
21             return -1;
22     }
23 
24     int ret = ftruncate(fd, mem_size);
25     if(ret == -1)
26     {
27         perror("ftruncate");
28         return -1;
29     }
30 
31     void *ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
32     if(ptr == MAP_FAILED)
33     {
34         perror("mmap");
35         return -1;
36     }
37 
38     int i = 0;
39     while(1)
40     {
41         sprintf(ptr, "Data%d", i++);
42         printf("write data : %s\n", (char*)ptr);
43         sleep(1);
44     }
45 
46     close(fd);
47     munmap(ptr, mem_size);
48 
49     return 0;
50 }

 

标签:include,函数,int,间通信,POSIX,内存,共享内存,shm
From: https://www.cnblogs.com/BroccoliFighter/p/18059660

相关文章

  • C# 使用共享内存通信
    //SharedMemoryusing(MemoryMappedFilemmf=MemoryMappedFile.CreateOrOpen("sharedmem",500)){ objectobj=newObject(); ManualResetEventmyevent=newManualResetEvent(false); //写入线程每500ms发送一个消息,并发出一次event Threadth_writer=newThre......
  • 进程间通信-信号
    信号信号(signal)机制是Linux系统中最为古老的进程之间的通信机制。Linux信号也可以称为软中断,是在软件层次上对中断机制的一种模拟。在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,进程不需要通过任何操作等待......
  • 2024-02-27-物联网系统编程(7- 共享内存)
    7.共享内存7.1共享内存概述​共享内存允许两个或者多个进程共享给定的区域共享内存的特点共享内存是进程间共享数据的一种最快的方法;一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。使用共享内存要注意的是多个进程之......
  • 关于POSIX定义的宏S_ISLINK(),S_ISREG()的使用
    摘自:https://forum.ubuntu.org.cn/viewtopic.php?t=380854我在学习linuxC系统编程,书上有个源代码可以实现自己的ls命令,不过在查错的过程中这个问题卡了我很久#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>#include<sys/stat.h>#include<......
  • 《UNIX网络编程 卷2:进程间通信(第2版)》PDF
    内容简介《UNIX网络编程.卷2:进程间通信(第2版)》是一部UNIX网络编程的经典之作!进程间通信(IPC)几乎是所有Unix程序性能的关键,理解IPC也是理解如何开发不同主机间网络应用程序的必要条件。《UNIX网络编程.卷2:进程间通信(第2版)》从对PosixIPC和SystemVIPC的内部结构开始讨论,全面......
  • Linux进程间通信_共享内存和消息队列
    本文对SystemV标准的共享内存和消息队列这两种进程间通信方式进行讨论,涉及原理、系统调用接口以及相关的内核数据结构,并给出相关示例代码。SystemV共享内存基本原理进程间通信必须要让不同的进程看到同一份内存资源,因为进程具有独立性,所以这份内存资源是操作系统提供的,接口是由......
  • Linux进程通信-POSIX IPC
    前言LinuxPOSIXIPC的可移植性是不如SystemVIPC的,但是我们只用Linux,并且内核版本高于2.6.6的话就不存在该问题了。也因为POSIXIPC出现的比较晚,借鉴了systemVIPC的长处,规避其短处,使得POSIXIPC的接口更易用。进程间通信的手段很多,除了消息队列、信号量、共享内存,还有信号、so......
  • 深入浅出Java多线程(五):线程间通信
    引言大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第五篇内容:线程间通信。大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!!在现代编程实践中,多线程技术是提高程序并发性能、优化系统资源利用率的关键手段。Java作为主流的多线程支持语言,不仅提供了丰富的......
  • 共享内存 ---进程间通讯 解决死锁和数据损坏的问题 同时可以解决数据不同步的问题
    usingSystem;usingSystem.IO.MemoryMappedFiles;usingSystem.Text;usingSystem.Threading;classWriteProcess{staticvoidMain(){using(MemoryMappedFilemmf=MemoryMappedFile.CreateOrOpen("my_shared_memory",1024)){......
  • 进程间通信是什么?Linux进程间通信有几种方式?
    Linux进程间通信有几种方式?所谓进程间通信,就是在不同进程之间传播或交换信息,Linux支持多种进程间通信机制,常见的方式如下:进程间通信(IPC,Interprocesscommunication)是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。这使......