linux 共享内存操作(shm_open、mmap、编译链接库:-lz -lrt -lm -lc都是什么库)

linux 共享内存操作(shm_open)

一、背景

在linux系统开发当中,时常需要在多个进程之间交换数据,在多个进程之间交换数据,有很多方法,但最高效的方法莫过于共享内存。

共享内存并不是从某一进程拥有的内存中划分出来的;进程的内存总是私有的。共享内存是从系统的空闲内存池中分配的,希望访问它的每个进程连接它。这个连接过程称为映射,它给共享内存段分配每个进程的地址空间中的本地地址。

linux共享内存是通过tmpfs文件系统来实现的,tmpfs文件系的目录为/dev/shm,/dev/shm是驻留在内存 RAM 当中的,因此读写速度与读写内存速度一样,/dev/shm的容量默认尺寸为系统内存大小的一半大小,使用df -h命令可以看到。

root@i-3t537lcq:~# free -h
              total        used        free      shared  buff/cache   available
Mem:          1.9Gi       226Mi       175Mi        20Mi       1.5Gi       1.5Gi
Swap:         2.0Gi        62Mi       1.9Gi
root@i-3t537lcq:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            952M     0  952M   0% /dev
tmpfs           199M  1.4M  198M   1% /run
/dev/vda2        50G   13G   35G  27% /
tmpfs           994M   16K  994M   1% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           994M     0  994M   0% /sys/fs/cgroup
tmpfs           199M     0  199M   0% /run/user/0
/dev/loop2       64M   64M     0 100% /snap/core20/1778
/dev/loop7       50M   50M     0 100% /snap/snapd/17950
/dev/loop8      138M  138M     0 100% /snap/lxd/24239
/dev/loop4      145M  145M     0 100% /snap/lxd/24323
/dev/loop5       56M   56M     0 100% /snap/core18/2679
/dev/loop3       64M   64M     0 100% /snap/core20/1822
/dev/loop1       50M   50M     0 100% /snap/snapd/18357
/dev/loop9       56M   56M     0 100% /snap/core18/2697
root@i-3t537lcq:~#

实际上它并不会真正的占用这块内存,如果/dev/shm/下没有任何文件,它占用的内存实际上就是0字节,仅在使用shm_open文件时,/dev/shm才会真正占用内存。

二、函数使用说明

Linux的共享内存提供了进程之间共享数据的一种方法,可以避免进程之间的数据复制。Linux的共享内存API有两套,即POSIX共享内存和System V共享内存。POSIX共享内存其实是基于mmap实现的。

共享内存一般基本操作流程如下:

  1. 使用shm_open来新建或者打开一个共享内存
  2. 如果是创建一个新的共享内存对象,那么调用ftruncate设置共享内存对象的最大大小
  3. 使用mmap将共享内存映射到进程的地址空间中,然后操作共享内存
  4. 操作完毕后,使用munmap解除映射。解除映射后,本进程就不能再访问共享内存,但共享内存对象依然还是存在的
  5. 调用close关闭shm_open返回的文件描述符。如果进程修改了共享内存,那么在close之前可一调用fsync来同步。
  6. 如果没有任何进程再访问共享内存对象,可以调用shm_unlink来删除共享内存对象

在Linux系统使用共享内存,POSIX 为创建、映射、同步和取消共享内存段提供五个入口点:

int shm_open(const char *name, int oflag, mode_t mode);
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);
int shm_unlink(const char *name);
int ftruncate(int fd, off_t length);
  • shm_open():创建共享内存段或连接到现有的已命名内存段。这个系统调用返回一个文件描述符。
    shm_open最主要的操作也是默认的操作就是在/dev/shm/下面,建立一个文件。
  • mmap():把共享内存段映射到进程的内存。这个系统调用需要 shm_open() 返回的文件描述符,它返回指向内存的指针。
  • shm_unlink():根据(shm_open() 返回的)文件描述符,删除共享内存段。实际上,这个内存段直到访问它的所有进程都退出时才会删除,这与在 UNIX 中删除文件很相似。但是,调用 shm_unlink() (通常由原来创建共享内存段的进程调用)之后,其他进程就无法访问这个内存段了。
  • munmap():作用与 mmap() 相反。
  • msync():用来让共享内存段与文件系统同步 — 当把文件映射到内存时
  • ftruncate(改变文件大小)
    参数fd为已打开的文件描述词,而且必须是以写入模式打开的文件。
    ftruncate函数是用来截断指定文件的一个系统调用,它可以改变指定文件的大小.当指定的文件的大小比原先的大时,那么多余的部分将会以0填充.
    如果原来的文件大小比参数length大,则超过的部分会被删去。

文件名字是用户自己输入的。另外一定要用ftruncate把文件大小于设置为共享内存大小。

shm_open

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

功能说明:shm_open 用于创建或者打开共享内存文件。shm_open 也许仅仅是系统函数open的一个包装,不同之处就是shm_open操作的文件一定是位于tmpfs文件系统里的,常见的Linux发布版的tmpfs文件系统的存放目录就是/dev/shm

用shm_open 创建的文件,如果不调用此函数(shm_unlink)删除,会一直存在于/dev/shm目录里,直到操作系统重启或者调用linux命令rm来删除为止。

返回值:成功返回fd>0, 失败返回fd<0

参数说明:

name:要打开或创建的共享内存文件名,由于shm_open 打开或操作的文件都是位于/dev/shm目录的,因此name不能带路径,例如:/var/myshare 这样的名称是错误的,而 myshare 是正确的,因为 myshare 不带任何路径。如果你一定要在name添加路径,那么,请在/dev/shm目录里创建一个目录,例如,如果你想创建一个 bill/myshare 的共享内存文件,那么请先在/dev/shm目录里创建 bill这个子目录,由于不同厂家发布的linux系统的tmpfs的位置也许不是/dev/shm,因此带路径的名称也许在别的环境下打开不成功。

oflag:打开的文件操作属性:O_CREAT、O_RDWR、O_EXCL的按位或运算组合

mode:文件共享模式,例如 0777

代码验证demo

yum install gcc

ftruncate(改变文件大小)

ftruncate(改变文件大小)
定义函数 int ftruncate(int fd,off_t length);

  • 函数说明
    ftruncate()会将参数fd指定的文件大小改为参数length指定的大小。
    参数fd为已打开的文件描述词,而且必须是以写入模式打开的文件。
    如果原来的文件大小比参数length大,则超过的部分会被删去。

  • 返回值
    执行成功则返回0,失败返回-1,错误原因存于errno。

  • 错误代码
    EBADF 参数fd文件描述词为无效的或该文件已关闭。
    EINVAL 参数fd 为一socket 并非文件,或是该文件并非以写入模式打开。

mmap共享内存

Linux的共享内存提供了进程之间共享数据的一种方法,可以避免进程之间的数据复制。Linux的共享内存API有两套,即POSIX共享内存和System V共享内存。POSIX共享内存其实是基于mmap实现的。

   #include <sys/mman.h>
 
   void *mmap(void *addr, size_t length, int prot, int flags,
              int fd, off_t offset);
   int munmap(void *addr, size_t length);

mmap在调用进程的地址空间中映射一段内存。当参数文件描述符fd为-1时,并在flags中指定MAP_ANONYMOUS,就会生成匿名映射,这种内存映射没有后台文件,可在fork的父子继承中共享(子进程调用execl后不再共享),这也是常用的共享内存手段。当fd参数为某特定文件时,就产生文件映射,不同进程映射统一文件可实现映射区域内存共享的目的。进程对共享内存的操作会在msync调用或者munmap调用后同步到后台文件。

三、示例代码

创建内存共享文件并写入数据

shm_write.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>

char buf[10];
char *ptr;

int main()
{
    
    
    int fd;
    fd = shm_open("shm_region", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fd < 0)
    {
    
    
        printf("error open shm_region\n");
        return 0;
    }
    ftruncate(fd, 10);
    ptr = mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED)
    {
    
    
        printf("error map\n");
        return 0;
    }
    *ptr = 0x12;
    return 0;
}

编译:

gcc -lrt -o  shm_write  shm_write.c

使用hexdump读取共享内存内容:

hexdump /dev/shm/shm_region

打开内存共享文件读数据

shm_read.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
char buf[10];
char *ptr;
int main()
{
    
    
    int fd;
    fd = shm_open("shm_region", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fd < 0)
    {
    
    
        printf("error open shm_region\n");
        return 0;
    }
    ftruncate(fd, 10);
    ptr = mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED)
    {
    
    
        printf("error map\n");
        return 0;
    }
    while (*ptr != 0x12)
        ;
    printf("ptr : %d\n", *ptr);
    return 0;
}

编译:

gcc -lrt -o  shm_read  shm_read.c

执行结果:

[root@dev c]# ./shm_read
ptr : 18
[root@dev c]#

四、问题总结

shm_write.c:(.text+0x18): undefined reference to `shm_open’

问题描述:
执行编译,文件内容包含shm_open方法

 gcc -o shm_write  shm_write.c

报错:shm_write.c:(.text+0x18): undefined reference to `shm_open’

问题分析:
没有找到函数定义,需要 指定动态链接库。

man shm_open

解决方法:
编译参数添加 -lrt

gcc -lrt -o  shm_write  shm_write.c

编译链接库:-lz -lrt -lm -lc都是什么库

-lz 压缩库(Z)
-lrt 实时库(real time):shm_open系列
-lm 数学库(math)
-lc 标准C库(C lib)
-dl ,是显式加载动态库的动态函数库

五、参考

linux 共享内存 shm_open ,mmap的正确使用
原文链接:https://blog.csdn.net/ababab12345/article/details/102931841
Linux编程之共享内存
参考URL: https://blog.csdn.net/rangzh/article/details/112002554

猜你喜欢

转载自blog.csdn.net/inthat/article/details/129137559