Linux 内核开发 27 POSIX共享内存

ops/2024/10/15 18:50:37/
Linux 内核开发 27 POSIX共享内存
1.定义

支持 POSIX 共享内存,linux 内核使用的是通过一个名为tmpfs的特殊文件系统来实现内存共享,并且将文件系统挂载在rootfs的/dev/shm上。
这种实现与linux 文件系统api 相互一致,所以每个文件都有inode 与名称与之对应。使用posix 应用开发者来说,更具灵活性(相比systemv 内存共享模式)。映射实现由tmpfs文件系统处理。

2.相关接口定义


shm_open() 是 POSIX 共享内存的 API 函数之一,用于创建或打开 POSIX 共享内存对象。以下是关于 shm_open() 函数的详细说明:

函数原型
#include <sys/mman.h>

int shm_open(const char *name, int oflag, mode_t mode);
参数说明
name:共享内存对象的名称。在 POSIX 共享内存中,共享内存对象以名称标识。如果要创建新的共享内存对象,需要提供一个名称;如果要打开现有的共享内存对象,也需要提供相同的名称。
oflag:打开标志,指定了对共享内存对象的操作行为。可以是以下值的位掩码组合:

  • O_RDONLY:只读模式打开共享内存对象。
  • O_RDWR:读写模式打开共享内存对象。
  • O_CREAT:如果共享内存对象不存在,则创建它。如果同时指定了 O_CREAT,还需要提供 mode 参数来设置共享内存对象的权限。

O_EXCL:与 O_CREAT 一起使用,表示如果共享内存对象已存在,则返回错误。
mode:权限掩码,仅在指定了 O_CREAT 标志时才有效,用于设置新创建的共享内存对象的权限。在 Unix 系统中,通常使用八进制表示权限,如 0644。


mmap() 是 POSIX 共享内存的 API 函数之一,用于将文件或共享内存对象映射到进程的地址空间,从而实现对文件或共享内存的读写操作。以下是关于 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_READ:映射区域可读取。
  • PROT_WRITE:映射区域可写入。
  • PROT_EXEC:映射区域可执行。

flags:标志,指定了映射区域的其他特性,可以是以下值的位掩码组合:

  • MAP_SHARED:共享映射,多个进程可以修改映射区域。
  • MAP_PRIVATE:私有映射,对映射区域的修改不会影响原文件或其他进程。

fd:文件描述符,指定要映射的文件或共享内存对象的文件描述符。通常由 open()、shm_open() 或类似函数打开。
offset:文件偏移量,指定文件中的起始位置。对于共享内存对象通常为 0。
返回值

  • 如果成功,返回值是映射后内存区域的起始地址。
  • 如果失败,返回值是 MAP_FAILED,表示发生了错误。

错误处理
在失败时,可以通过检查 errno 来确定具体的错误原因。可能的错误包括但不限于:

  • EACCES:权限不足,无法访问文件或共享内存对象。
  • EINVAL:指定的参数无效。
  • ENOMEM:内存不足,无法分配足够的内存空间。


shm_unlink() 是 POSIX 共享内存的 API 函数之一,用于删除命名的共享内存对象。以下是关于 shm_unlink() 函数的详细说明:

函数原型
#include <sys/mman.h>

int shm_unlink(const char *name);
参数说明

  • name:共享内存对象的名称。要删除的共享内存对象由其名称标识。

返回值

  • 如果成功,返回值为 0。
  • 如果失败,返回值为 -1,并设置 errno 表示错误类型。

错误处理
在失败时,可以通过检查 errno 来确定具体的错误原因。可能的错误包括但不限于:

ENOENT:指定的共享内存对象不存在。


取消内存映射需要使用 munmap() 函数。以下是关于 munmap() 函数的描述:

函数原型
#include <sys/mman.h>

int munmap(void *addr, size_t length);
参数说明

  • addr:映射后的起始地址,即调用 mmap() 返回的映射区域的起始地址。
  • length:映射的长度,与调用 mmap() 时指定的长度相同。

返回值

  • 如果成功,返回值为 0。
  • 如果失败,返回值为 -1,并设置 errno 表示错误类型。
  • 错误处理

在失败时,可以通过检查 errno 来确定具体的错误原因。可能的错误包括但不限于:

  • EINVAL:指定的参数无效。
  • ENOMEM:内存不足,无法释放映射区域的内存空间。

3.使用案例分析代码


编写写内存共享demo


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>#define MAXSIZE 1024*4   /*共享内存的大小,建议设置成内存页的整数倍*/#define FILENAME "shm.test"int main()
{/* 创建共享对象,可以查看/dev/shm目录 */int fd = shm_open(FILENAME, O_CREAT | O_TRUNC | O_RDWR, 0777);if (fd == -1) {perror("open failed:");exit(1);}/* 调整大小 */if (ftruncate(fd, MAXSIZE) == -1) {perror("ftruncate failed:");exit(1);}/* 获取属性 */struct stat buf;if (fstat(fd, &buf) == -1) {perror("fstat failed:");exit(1);}printf("the shm object size is %ld\n", buf.st_size);/* 建立映射关系 */char *ptr = (char*)mmap(NULL, MAXSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (ptr == MAP_FAILED) {perror("mmap failed:");exit(1);}printf("mmap %s success\n", FILENAME);close(fd); /* 关闭套接字 *//* 写入数据 */char *content = "hello world";strncpy(ptr, content, strlen(content));// why sleep?sleep(30);return 0;
}

编写读内存共享demo

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>#define FILENAME "shm.test"int main()
{/* 创建共享对象,可以查看/dev/shm目录 */int fd = shm_open(FILENAME, O_RDONLY, 0);if (fd == -1) {perror("open failed:");exit(1);}/* 获取属性 */struct stat buf;if (fstat(fd, &buf) == -1) {perror("fstat failed:");exit(1);}printf("the shm object size is %ld\n", buf.st_size);/* 建立映射关系 */char *ptr = (char*)mmap(NULL, buf.st_size, PROT_READ, MAP_SHARED, fd, 0);if (ptr == MAP_FAILED) {perror("mmap failed:");exit(1);}printf("mmap %s success\n", FILENAME);close(fd); /* 关闭套接字 */printf("the read msg is:%s\n", ptr);sleep(30);return 0;
}

编译时注意需要增加 -lrt 选项,这样编译器就会在链接时搜索 shm_open 函数所在的库。

如果你已经包含了 -lrt 选项,但仍然遇到此问题,请确保你正确包含了头文件 sys/mman.h 并且没有拼写错误。

gcc -o write write.c -lrtgcc -o read read.c -lrt

执行结果:

以上代码在以下环境编译成功过。

peach@peach:~/posix-demo$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.6 LTS
Release:        20.04
Codename:       focal


 

4.总结

在使用 POSIX 共享内存时,有一些需要注意的重要点:

  • 文件描述符: POSIX 共享内存是使用文件描述符(fd)来标识共享内存区域的,因此在操作共享内存之前,需要先使用 shm_open() 函数打开一个共享内存对象,并获取文件描述符。
  • 权限控制: 与其他共享内存机制不同,POSIX 共享内存允许对共享内存对象设置权限,即允许控制哪些进程可以访问共享内存。在调用 shm_open() 时可以设置权限参数,以实现这种控制。
  • 映射: 要在进程的地址空间中映射共享内存对象,可以使用 mmap() 函数。需要注意的是,映射到进程地址空间时要指定正确的权限,例如 PROT_READ、PROT_WRITE 等。
  • 取消映射: 使用 munmap() 函数取消内存映射,并释放该内存区域。
  • 同步机制: 使用 POSIX 共享内存时,需要确保在不同进程之间正确同步共享内存的访问,以避免竞争条件和数据不一致性问题。通常可以使用信号量或互斥锁等同步机制来实现。
  • 异常处理: 在使用共享内存时,需要注意处理可能出现的异常情况,例如共享内存对象打开失败、映射内存错误等,避免出现程序崩溃或数据丢失。
  • 清理资源: 在不需要共享内存时,应该正确地取消内存映射,并关闭共享内存对象,以释放资源并避免资源泄漏。
  • 跨平台兼容性: POSIX 共享内存是基于 POSIX 标准的实现,因此在不同的操作系统上可能会有一些差异。在编写跨平台程序时要格外注意这些细节,确保代码的可移植性和兼容性。

POSIX 共享内存提供了一种高效的共享内存机制,但在使用时需要特别注意上述点,以确保共享内存的正确和可靠操作。


http://www.ppmy.cn/ops/42609.html

相关文章

SpringBoot(八)之JdbcTemplate

SpringBoot&#xff08;八&#xff09;之JdbcTemplate 文章目录 SpringBoot&#xff08;八&#xff09;之JdbcTemplate1.添加依赖项&#xff1a;2. 配置数据库连接3.创建表信息4. 创建数据模型5. 创建 Repository6.测试,创建TestController spring-boot-starter-jdbc 是 Spring…

每天一个数据分析题(三百三十五)

下图表中&#xff0c;适用于展示连续型数据的数据分布情况的是&#xff08;&#xff09; A. 条形图 B. 饼图 C. 直方图 D. 箱线图 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处获取答案

深度学习之基于YoloV5钢材微小缺陷检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与目标 在钢材生产过程中&#xff0c;由于各种因素&#xff0c;钢材表面可能会出现微小缺陷&#xff…

视频批量剪辑神器大揭秘:一键删减片头片尾,高效打造精彩视频内容!

在数字化时代的浪潮中&#xff0c;视频已经成为人们传递信息、分享生活的重要载体。无论是制作一部精美的宣传片&#xff0c;还是剪辑一段有趣的短视频&#xff0c;视频时长都是至关重要的因素。然而&#xff0c;很多视频创作者在调整视频时长时遇到了困难&#xff0c;耗费了大…

YOLOv5/v7 引入 RepVGG 重参数化模块

YOLOv5/v7 中引入 RepVGG 重参数化模块 1. 介绍 RepVGG 是由 Megvii Research 团队于 2021 年提出的深度卷积神经网络架构&#xff0c;它通过重参数化 VGGNet 架构&#xff0c;显著提高了模型的性能和效率。RepVGG 架构在 YOLOv5 和 YOLOv7 等目标检测模型中得到了广泛应用&a…

Java18新特性

1 Java 18 引入了一些新特性和改进&#xff0c;旨在提高开发效率、性能和语言的易用性。以下是其中几个值得注意的新特性&#xff1a; 默认使用 UTF-8 (JEP 400): Java 18 默认字符集现在是 UTF-8&#xff0c;这简化了文本处理并避免了以前因地区设置不同而可能导致的乱码问题…

【产品经理】输出

引言&#xff1a;        在最近频繁的产品管理职位面试中&#xff0c;我深刻体会到了作为产品经理需要的不仅仅是对市场和技术的敏锐洞察&#xff0c;更多的是在复杂多变的环境中&#xff0c;如何运用沟通、领导力和决策能力来引导产品从概念走向市场。这一系列博客将分享…

ts 学习笔记

:void 代表没有返回值 一般用于函数 1顶级类型 any unknown(不可以赋值给其他类型&#xff0c;只能赋值给自身或者any,没有办法读任何属性&#xff0c;方法也不可以调用) 2 Object 3 Number String Boolean 4 number string boolean 5 1 ‘’a’ false 6 never interface // in…