【并发程序设计】12.内存映射

12.内存映射

使一个磁盘文件与内存中的一个缓冲区相映射,进程可以像访问普通内存一样对文件进行访问,不必再调用read,write,更加高效。

用到的函数

mmap函数

  1. 原型

    #include <sys/mman.h>
    void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
    
  2. 功能:创建共享内存映射

  3. 参数

    • addr:指定要映射的内存地址,一般设置为 NULL 让操作系统自动选择合适的内存地址。

    • length:必须>0。映射地址空间的字节数,它从被映射文件开头 offset 个字节开始算起。

    • prot:指定共享内存的访问权限。可取如下几个值的可选:

      • PROT_READ(可读)

      • PROT_WRITE(可写)

      • PROT_EXEC(可执行)

      • PROT_NONE(不可访问)

    • flags:由以下几个常值指定:

      1. MAP_SHARED(共享映射)

      2. MAP_PRIVATE(私有映射)

      3. MAP_FIXED(表示必须使用 start 参数作为开始地址,如果失败不进行修正)

      4. MAP_ANONYMOUS(匿名映射,用于血缘关系进程间通信)

        其中,MAP_SHARED , MAP_PRIVATE必选其一,而 MAP_FIXED 则不推荐使用。

    • fd:表示要映射的文件句柄。如果匿名映射写-1。

    • offset:表示映射文件的偏移量,一般设置为 0 表示从文件头部开始映射。

  4. 返回值

    • 成功返回创建的映射区首地址
    • 失败返回MAP_FAILED,设置errno值

lseek函数

  1. 原型

    #include <sys/types.h>
    #include <unistd.h>
    off_t lseek(int fd, off_t offset, int whence);
    
  2. 功能:用于在文件中定位文件指针的位置

  3. 参数

    • fd:文件描述符,通常是通过openfopen函数获得的。

    • offset:相对于whence的偏移量,以字节为单位。

    • whence:表示参考点的位置,可以是以下值之一:

      :表示参考点的位置,可以是以下值之一:

      • SEEK_SET:文件开头
      • SEEK_CUR:文件当前位置
      • SEEK_END:文件结尾
  4. 返回值

    • 成功时,返回新的文件指针位置。
    • 失败时,返回-1,并设置errno

示例1

利用内存映射实现两个无亲缘关系的线程间的通信

write.c

#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
	void *addr;
	int fd;
	fd = open("test",O_RDWR);
	if(fd<0)
	{
		perror("open");
		return 0;
	}

	//获取文件大小
	int fileSize = lseek(fd, 0, SEEK_END);
	lseek(fd, 0, SEEK_SET); // 将文件指针重置到文件开头

	//映射文件到内存
	addr = mmap(NULL,2000,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	if(addr == MAP_FAILED)
	{
		perror("mmap");
		close(fd);
		return 0;
	}

	//写入文件
	int i = 0;
	printf("%d\n",fileSize);
	while(i < 2000)
	{
		printf("%d\n",i);
		memcpy(addr+i,"5",1);
		sleep(1);
		i++;
	}
	
	return 0;
}

read.c

#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
	void *addr;
	int fd;
	fd = open("test",O_RDWR);
	if(fd<0)
	{
		perror("open");
		return 0;
	}

	//获取文件大小
	int fileSize = lseek(fd, 0, SEEK_END);
	lseek(fd, 0, SEEK_SET); // 将文件指针重置到文件开头

	//映射文件到内存
	addr = mmap(NULL,2000,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	if(addr == MAP_FAILED)
	{
		perror("mmap");
		close(fd);
		return 0;
	}

	//读取
	int i = 0;
	printf("%d\n",fileSize);
	while(i < 2000)
	{
		printf("%s\n",(char *)addr);
		sleep(1);
	}
	
	return 0;
}

示例2匿名映射

利用内存映射匿名映射实现两个亲缘线程间的通信

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

int main(){

    void *addr;
    
    //匿名映射
    addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    if(addr == MAP_FAILED)
    {
        perror("mmap");
        return 0;
    }

    //创建子进程
    pid_t pid;
    pid = fork();
    if(pid<0)
    {
        perror("fork");
        return 0;
    }

    //父进程
    else if(pid>0)
    {
        memcpy(addr,"1234567890",10);
        wait(NULL);//回收子进程
    }

    //子进程
    else 
    {
        sleep(1);
        printf("read father val=%s\n",(char *)addr);
    }
    munmap(addr,2048);//释放内存
}

注意事项

  1. 创建映射区的过程中,隐含着一次对映射文件的读操作,将文件内容读取到映射区。

  2. 当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护),如果不满足报非法参数(Invalid argument)错误。

    当MAP_PRIVATE时候,mmap中的权限是对内存的限制,只需要文件有读权限即可,操作只在内存有效,不会写到物理磁盘,且不能在进程间共享。

  3. 映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭。

  4. 用于映射的文件大小必须>0,当映射文件大小为0时,指定非0大小创建映射区,访问映射地址会报总线错误,指定0大小创建映射区,报非法参数错误(Invalid argument)

  5. 文件偏移量必须为0或者4K的整数倍(不是会报非法参数Invalid argument错误)

  6. 映射大小可以大于文件大小,但只能访问文件page的内存地址,否则报总线错误 ,超出映射的内存大小报段错误

    在这里插入图片描述

  7. 映射内存大小为4K的整数倍,若申请映射大小不为4K的整数倍,则会自动向上补齐。例如申请2k,实际可以操作的内存大小为4K。

System V共享内存

  • 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
  • 共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活
  • 由于多个进程可同时访问共享内存,因此需要同步和互斥机制配合使用

共享内存使用步骤:

  1. 生成key
  2. 创建/打开共享内存
  3. 映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
  4. 读写共享内存
  5. 撤销共享内存映射
  6. 删除共享内存对象

在这里插入图片描述

ftok函数

  1. 原型

    #include <sys/types.h>
    #include <sys/ipc.h>
    key_t **ftok**(const char *pathname, int proj_id);
    
  2. 功能:生成一个唯一的与文件相关的键值(key),这个键值通常用于IPC(进程间通信)中,比如创建共享内存或信号量等。

  3. 参数

    • pathname:必须是存在的且可访问的文件路径。它可以是一个普通文件或者目录,但该路径下的某个文件需要具有相应的权限,以便其他进程可以通过该路径访问到相同的键值。
    • proj_id:是一个8位的整数(即范围在0到255之间)。它与文件的索引节点号结合生成最终的键值。
  4. 返回值

    • 如果函数执行成功,会返回一个key_t类型的键值
    • 如果执行失败,则返回-1
  5. 注意

    • 确保pathname指定的文件存在且可访问。

    • proj_id的值虽然作为int类型传递,但实际上只有8个比特被使用,因此其取值范围是0到255。

    • 生成的键值是由proj_id的后8个比特、文件所在设备的最后两位和文件的索引节点号的最后四位组成的

shmget函数

  1. 原型

    #include <sys/shm.h>
    int shmget(key_t key, size_t size, int shmflg);
    
  2. 功能:获取或创建一个共享内存段,并返回一个共享内存标识符。

  3. 参数

    • key:共享内存的键值,可以是0(IPC_PRIVATE)以创建一个新的共享内存对象,或者是大于0的32位整数。如果shmflg中包含了IPC_CREAT标志,且提供的键值已经存在,则会返回现有的共享内存标识符。
    • size:指定需要共享的内存容量,以字节为单位。
    • shmflg:权限标志,与open函数的mode参数类似。如果希望在键值指定的共享内存不存在时创建它,可以与IPC_CREAT进行或操作。
  4. 返回值

    • 如果成功,shmget函数返回一个共享内存标识符,该标识符可以在后续的共享内存相关操作中使用,如shmatshmdtshmctl等。
    • 如果失败,返回-1。
  5. 注意

    • 不同进程可以通过shmget返回的共享内存标识符访问同一块共享内存。
    • 当写数据到共享内存时,需要注意同步问题,即在进程间访问共享内存时要加锁,以防止数据竞争。
    • 在使用共享内存时,应确保结构体中的缓冲区已经声明了足够的大小,而不是仅使用一个指针并在需要时通过malloc分配内存,因为这样分配的地址其他进程无法访问

ipcs 命令

  1. 格式ipcs [options]
  2. 功能ipcs 命令用于提供系统中进程间通信设施的信息,包括消息队列、共享内存段和信号量。
  3. 参数
    • -a--all:显示所有类型的 IPC 资源信息。
    • -b--broad:显示 IPC 资源的宽泛信息,包括最大允许的尺寸等。
    • -m--shmems:仅显示共享内存段的信息。
    • -q--queues:仅显示消息队列的信息。
    • -s--semaphores:仅显示信号量的信息。
    • -i <id>--identifier <id>:显示特定 IPC 资源 ID 的详细信息。
    • -l--limits:显示系统限制信息。
    • -u--summary:显示 IPC 资源的摘要信息。
    • -p--pid:显示创建 IPC 资源的进程 ID。
    • -t--time:显示最后操作 IPC 资源的时间。
    • -c--creator:显示 IPC 资源的创建者信息。
    • -h--human:以人类可读的格式显示大小。
    • -v--version:显示 ipcs 命令的版本信息。
    • -V--help:显示帮助信息。
  4. 示例
    • ipcs -a:显示所有类型的 IPC 资源信息。
    • ipcs -m:仅显示共享内存段的信息。
    • ipcs -q:仅显示消息队列的信息。
    • ipcs -s:仅显示信号量的信息。
    • ipcs -u:显示 IPC 资源的摘要信息。
    • ipcs -i 1234:显示 ID 为 1234 的 IPC 资源的详细信息。

shmat函数

  1. 原型

    #include <sys/shm.h>
    void *shmat(int shmid, const void *shmaddr, int shmflg);
    
  2. 功能:将创建好的共享内存段连接到某个进程,并指定内存空间

  3. 参数

    • shmid:是通过shmget函数返回的共享内存标识符,它唯一标识了一个共享内存段。
    • shmaddr:是一个可选参数,用于指定共享内存映射到进程地址空间中的起始地址。如果传递NULL,则由系统自动选择一个地址进行映射。
    • shmflg:是一组标志位,用于控制共享内存的访问权限和其他属性。常见的标志包括SHM_RDONLY(只读访问)和SHM_WRONLY(只写访问)。
  4. 返回值

    • 成功,返回一个指向共享内存段在进程地址空间中映射的起始位置的指针。这个指针可以用于访问共享内存中的数据。
    • 失败,返回-1

shmdt函数

  1. 原型

    #include <sys/shm.h>
    int shmdt(const void *shmaddr);
    
  2. 功能:从进程的地址空间中分离已经附加的共享内存段

  3. 参数shmaddr是一个指向共享内存段的指针,这个指针通常是通过shmat函数返回的。

  4. 返回值

    • 成功,shmdt函数返回0
    • 失败,返回 -1,并设置 errno 以指示错误原因。

shmctl函数

  1. 原型

    #include <sys/shm.h>
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    
  2. 功能:控制共享内存段,包括删除和修改权限等操作

  3. 参数

    • shmid:是通过shmget函数返回的共享内存标识符,唯一标识了一个共享内存段。
    • cmd:是控制命令,用于指定要执行的操作
      • IPC_STAT(获取共享内存的状态)
      • IPC_SET(设置共享内存的状态)
      • IPC_RMID(删除共享内存段)
    • buf:是一个指向shmid_ds结构体的指针,用于存储或接收共享内存的状态信息。在某些命令下,如IPC_STAT和IPC_SET,需要提供这个参数。
  4. 返回值

    • 成功,shmctl函数返回0;
    • 出错,返回 -1,并设置 errno 以指示错误原因

示例

创建和使用共享内存

write.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>

int main() {
    key_t key;  // 定义一个键值变量
    int shmid;  // 定义一个共享内存标识符变量
    char *buf;  // 定义一个指向共享内存的指针变量

    key = ftok("keytest", 100);  // 生成一个唯一的键值
    if (key < 0) 
    {
        perror("ftok");  
        return 0;
    }
    printf("key=%x\n", key);  // 打印生成的键值

    shmid = shmget(key, 512, IPC_CREAT | 0666);  //创建并获取共享内存段
    if (shmid < 0) 
    {
        perror("shmget");  
        return 0;
    }

    printf("shmid=%d\n", shmid);  // 打印共享内存标识符

    buf = shmat(shmid, NULL, 0);  // 将共享内存段附加到进程的地址空间中
    if (buf < 0) 
    {
        perror("shmat");  
        return 0;
    }

    strcpy(buf, "hello world");  // 将字符串"hello world"复制到共享内存中
}

read.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>

int main() {
    key_t key;  // 定义一个键值变量
    int shmid;  // 定义一个共享内存标识符变量
    char *buf;  // 定义一个指向共享内存的指针变量

    key = ftok("keytest", 100);  // 生成一个唯一的键值
    if (key < 0) 
    {
        perror("ftok");  
        return 0;
    }
    printf("key=%x\n", key);  // 打印生成的键值

    shmid = shmget(key, 512, 0666);  // 获取共享内存段
    if (shmid < 0) 
    {
        perror("shmget");  
        return 0;
    }

    printf("shmid=%d\n", shmid);  // 打印共享内存标识符

    buf = shmat(shmid, NULL, 0);  // 将共享内存段附加到进程的地址空间中
    if (buf < 0) 
    {
        perror("shmat");  
        return 0;
    }

    printf("read = %s\n",buf);
      
    if(shmdt(buf) == -1)//共享内存段从进程的地址空间中分离
        perror("shmdt");
    if(shmctl(shmid, IPC_RMID, NULL) == -1)//删除共享内存段
        perror("shmctl");
    
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/665279.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

销售数据分析怎么做?看这一篇就够了

销售数据分析&#xff0c;简而言之&#xff0c;就是通过对销售数据的收集、整理、分析和解释&#xff0c;发现其中的规律和趋势&#xff0c;从而为销售策略的制定提供有力的数据支持。在当今数据驱动的时代&#xff0c;销售数据分析已经不再是专业数据分析师的专属技能&#xf…

【项目管理知识】项目质量管理措施

1、持续改进&#xff08;PDCA&#xff09; 戴明循环或称PDCA循环、PDSA循环。戴明循环的研究起源于20世纪20年代&#xff0c;先是有着“统计质量控制之父”之称的著名的统计学家沃特阿曼德休哈特&#xff08;Walter A. Shewhart&#xff09;在当时引入了“计划-执行-检查&…

去噪扩散概率模型在现代技术中的应用:图像生成、音频处理到药物发现

去噪扩散概率模型&#xff08;DDPMs&#xff09;是一种先进的生成模型&#xff0c;它通过模拟数据的噪声化和去噪过程&#xff0c;展现出多方面的优势。DDPMs能够生成高质量的数据样本&#xff0c;这在图像合成、音频生成等领域尤为重要。它们在数据去噪方面表现出色&#xff0…

结构体相关习题的补充

结构体相关习题的补充 题目1&#xff1a; 如有以下代码&#xff1a; struct student {int num;char name[32];float score; }stu;则下面的叙述不正确的是&#xff1a;( ) A.struct 是结构体类型的关键字 B.struct student 是用户定义的结构体类型 C.num, score 都是结构体…

带文字的短视频:成都鼎茂宏升文化传媒公司

带文字的短视频&#xff1a;视觉与文字的交织艺术 在信息爆炸的时代&#xff0c;短视频以其直观、生动的视觉呈现方式&#xff0c;迅速成为人们获取信息、娱乐休闲的重要渠道。然而&#xff0c;随着人们对内容深度和质量要求的提升&#xff0c;成都鼎茂宏升文化传媒公司单纯的…

一文搞懂分布式事务-Saga

Saga定义 Saga模式是一种分布式事务处理模式&#xff0c;用于保证分布式系统中的一系列操作要么全部成功执行&#xff0c;要么全部回滚&#xff0c;以实现一致性的目标。它采用了长事务的概念&#xff0c;将原子操作拆分为多个子事务&#xff0c;并通过补偿机制保证整个事务的…

知识图谱抽取实战

相关代码见文末 1.知识图谱应用场景 知识图谱是一种先进的数据组织形式,它通过图数据结构来表示实体(如人、地点、概念)及其之间的复杂关系,便于机器理解和处理。这种结构化知识库允许高效的信息检索、推理和知识发现,尤其适用于处理高度关联且需要深度理解的领域,如医学…

深入了解diffusion model

diffusion model是如何运作的 会输入当时noise的严重程度&#xff0c;根据我们的输入来确定在第几个step&#xff0c;并做出不同的回应。 Denoise模组内部实际做的事情 产生一张图片和产生noise难度是不一样的&#xff0c;若denoise 模块产生一只带噪声的猫说明这个模块已经会…

【考试100】安全员B证《建设工程安全生产技术》单选题

​ 题库来源&#xff1a;考试100 【考试100】安全员B证《建设工程安全生产技术》单选题 1&#xff0e;在悬空部位作业时&#xff0c;操作人员应&#xff08; &#xff09; A.遵守操作规定 B.进行安全技术交底 C.戴好安全帽 D.系好安全带 【考试100答案】&#xff1a;D…

华为云的云主机安装的linux系统不能使用yum下载软件包、程序、组件等

目录 一、背景介绍 二、问题描述 1、尝试使用yum安装traceroute 2、更换yum源 3、使用curl命令访问百度&#xff0c;测试网络 三、问题分析和解决 1、修改网卡设置 &#xff08;1&#xff09;ifconfig查看网卡信息 &#xff08;2&#xff09;添加DNS 2、修改/etc/res…

268 基于matlab的模拟双滑块连杆机构运动

基于matlab的模拟双滑块连杆机构运动&#xff0c;并绘制运动动画&#xff0c;连杆轨迹可视化输出&#xff0c;并输出杆件质心轨迹、角速度、速度变化曲线。可定义杆长、滑块速度&#xff0c;滑块初始位置等参数。程序已调通&#xff0c;可直接运行。 268 双滑块连杆机构运动 连…

【LINUX】LINUX基础(目录结构、基本权限、基本命令)

文章目录 LINUX的目录结构LINUX的基本权限LINUX基本命令 LINUX的目录结构 /&#xff1a;表示根目录bin&#xff1a;存放二进制可执行文件(命令ls、cat、mkdir等)boot&#xff1a;存放系统引导文件dev&#xff1a;存放设备文件etc&#xff1a;存放系统配置文件home&#xff1a;…

纯js仿淘宝多图片封面图插件模板/带视频,带放大镜,带前后端完整代码PHP

功能预览,他依赖jq插件,请自已引入 类似这样 <script type"text/javascript" src"/Application/Admin/Static/js/jquery-2.0.3.min.js"></script>一,前端模板代码 <!--多图功能--><style> charset "utf-8"; .wrap_imgs…

打造高效上传体验:基于Kotlin的Android快速上传框架

1. 引言 在Android开发中&#xff0c;文件上传操作常常面临各种挑战&#xff0c;为此我开源了一个高效、易用的快速上传框架&#xff0c;助力开发者轻松实现文件上传功能。 GitHub项目地址: 点我 2. 框架特点概述 纯Kotlin编写&#xff1a;简洁、现代的编程语言。MVVM架构&a…

利用依赖结构矩阵管理架构债务

本文讨论了如何利用依赖结构矩阵&#xff08;DSM&#xff0c;Dependency Structure Matrix&#xff09;管理和识别架构债务&#xff0c;并通过示例应用展示了这一过程。原文: Managing Architecture Debt with Dependency Structure Matrix Vlado Paunovic Unsplash 技术债务&a…

Django ORM入门指南:从概念到实践,掌握模型创建、迁移与视图操作

系列文章目录 Django入门全攻略&#xff1a;从零搭建你的第一个Web项目Django ORM入门指南&#xff1a;从概念到实践&#xff0c;掌握模型创建、迁移与视图操作[Django ORM实战&#xff1a;模型字段与元选项配置&#xff0c;以及链式过滤与QF查询详解]还在写0.0… 文章目录 系…

中国青年汽车研发仿真建模大赛“汽车系统建模仿真与科学计算赛道”,邀您来赛!

近日&#xff0c;由中国青年创业就业基金会、国家新能源汽车技术创新中心共同主办&#xff0c;中国汽车研发软件产业创新联盟承办、苏州同元软控信息技术有限公司&#xff08;简称“同元软控”&#xff09;等企业协办的首届中国青年汽车研发仿真建模大赛启动仪式在北京经开区拉…

精选免费在线工具与资源推荐20240531

精选免费在线工具与资源推荐 引言 在互联网高速发展的今天&#xff0c;我们身处一个信息爆炸的时代。为了更好地应对工作和学习中的挑战&#xff0c;我们时常需要借助各种工具和资源来提高效率。幸运的是&#xff0c;网络上存在着大量免费且高效的在线工具和资源&#xff0c;…

【算法】模拟算法——Z字形变换(medium)

题解&#xff1a;模拟算法——Z字形变换(medium) 目录 1.题目2.题解3.参考代码4.总结 1.题目 题目链接&#xff1a;LINK 2.题解 利用模拟&#xff0c;来解决问题。 首先创建出一个O(numRows*n)的数组来&#xff0c;并按照题目要求把每个字符按顺序填进去。 这里以numRows…

【运维项目经历|026】Redis智能集群构建与性能优化工程

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…