Linux下的系统编程——共享存储映射(十)

前言:

mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。

目录

 一.文件间进程通信:

 二、存储映射I/O:

三、mmap函数: 

1.创建共享内存映射:

2.释放映射区:

*3.使用注意事项:

*4.mmap函数的保险调用方式:

5.mmap父子进程通信:

6.无血缘关系进程间 mmap 通信:                  


 一.文件间进程通信:

打开的文件是内核中的一块缓冲区。多个无血缘关系的进程,可以同时访问该文件。

两个完全独立没有血缘关系的进程文件之间也可以完成进程间的通信

test1.c 先执行,将数据写入文件test.txt 

 

/** 先执行,将数据写入文件test.txt*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>#define N 5int main(void)
{char buf[1024];char *str = "--------------secesuss-------------\n";int ret;int fd = open("test.txt", O_RDWR|O_TRUNC|O_CREAT, 0664);//直接打开文件写入数据write(fd, str, strlen(str));printf("test1 write into test.txt finish\n");sleep(N);lseek(fd, 0, SEEK_SET);ret = read(fd, buf, sizeof(buf));ret = write(STDOUT_FILENO, buf, ret);if (ret == -1) {perror("write second error");exit(1);}close(fd);return 0;
}

test2.c   后执行,尝试读取另外一个进程写入文件的内容

/** 后执行,尝试读取另外一个进程写入文件的内容*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>int main(void)
{char buf[1024];char *str = "----------test2 write secesuss--------\n";int ret;sleep(2);   //睡眠2秒,保证test1将数据写入test.txt文件int fd = open("test.txt", O_RDWR);//尝试读取test.txt文件中test1写入的数据ret = read(fd, buf, sizeof(buf));   //将读到的数据打印至屏幕write(STDOUT_FILENO, buf, ret);//写入数据到文件test.txt中, 未修改读写位置write(fd, str, strlen(str));printf("test2 read/write finish\n");close(fd);return 0;
}

 

 二、存储映射I/O:

        存储映射I/o(Memory-mapped l/o)使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用read和write函数的情况下,使用地址(指针)完成l/o操作。
        

        使用这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过 mmap函数来实现。

三、mmap函数: 

1.创建共享内存映射:

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_READ|PROT_WRITE

        flags:   标注共享内存的共享属性

        MAP_SHARED、MAP_PRIVATE

        fd:          用于创建共享内存映射区的那个文件的文件描述符。

        offset: 默认0,表示映射文件全部。偏移位置。需是 4k 的整数倍

返回值:

        成功:映射区的首地址。

        失败:MAP_FAILED (void*(-1)), errno

2.释放映射区:

int munmap(void *addr, size_t length);        

    addr:mmap 的返回值

    length:大小

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/mman.h>
#include <fcntl.h>void sys_err(const char *str)
{perror(str);exit(1);
}int main(int argc,char *argv[])
{char *p = NULL;int fd;//打开或者创建tetsmap文件fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);if(fd == -1){sys_err("open error");}/*两个函数等价于ftruncate()函数lseek(fd,10,SEEK_END);write(fd,"\0",1);*/ftruncate(fd,20);int len = lseek(fd,0,SEEK_END);p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(p == MAP_FAILED){sys_err("mmap error");}//使用p对文件进行读写操作strcpy(p,"hello mmap\n");printf("-------%s\n",p);//写操作int ret = munmap(p,len);if(ret == -1){sys_err("munmap error");}return 0;
}

*3.使用注意事项:

语法
open()方法语法格式如下:os.open(file, flags[, mode]);参数
file – 要打开的文件flags – 该参数可以是以下选项,多个使用 “|” 隔开:O_RDONLY: 以只读的方式打开
O_WRONLY: 以只写的方式打开
O_RDWR : 以读写的方式打开
O_NONBLOCK: 打开时不阻塞
O_APPEND: 以追加的方式打开
O_CREAT: 创建并打开一个新文件
O_TRUNC: 打开一个文件并截断它的长度为零(必须有写权限)
O_EXCL: 如果指定的文件存在,返回错误
O_SHLOCK: 自动获取共享锁
O_EXLOCK: 自动获取独立锁
O_DIRECT: 消除或减少缓存效果
O_FSYNC : 同步写入
O_NOFOLLOW: 不追踪软链接1)PROT_READ:表示内存段内的内容可写;2)PROT_WRITE:表示内存段内的内容可读;3)PROT_EXEC:表示内存段中的内容可执行;4)PROT_NONE:表示内存段中的内容根本没法访问。

  (1). 用于创建映射区的文件大小为 0,实际指定非0大小创建映射区,出 “总线错误”

	fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);  //O_RDWR : 以读写的方式打开if(fd == -1){sys_err("open error");}//ftruncate(fd,20);//int len = lseek(fd,0,SEEK_END);int len = 20;//PROT_READ:表示内存段内的内容可写;   PROT_WRITE:表示内存段内的内容可读p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);      //可读可写

 

    (2) 用于创建映射区的文件大小为 0,实际制定0大小创建映射区, 出 “无效参数”

	fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);    //O_RDWR : 以读写的方式打开if(fd == -1){sys_err("open error");}//ftruncate(fd,20);//int len = lseek(fd,0,SEEK_END);int len = 0;//PROT_READ:表示内存段内的内容可写;   PROT_WRITE:表示内存段内的内容可读p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);      //可读可写

   

    (3). 用于创建映射区的文件读写属性为,只读。映射区属性为 读、写。 出 “无效参数”。

	fd = open("testmap",O_RDONLY|O_CREAT|O_TRUNC,0644);    //O_RDONLY: 以只读的方式打开if(fd == -1){sys_err("open error");}ftruncate(fd,20);int len = lseek(fd,0,SEEK_END);//int len = 0;//PROT_READ:表示内存段内的内容可写;   PROT_WRITE:表示内存段内的内容可读p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);      //可读可写

    (4). 创建映射区,需要read权限。当访问权限指定为 “共享”MAP_SHARED是, mmap的读写权限,应该 <=文件的open权限。    只写不行。

	//fd = open("testmap",O_RDONLY|O_CREAT|O_TRUNC,0644);fd = open("testmap",O_WRONLY);    //文件的只写if(fd == -1){sys_err("open error");}ftruncate(fd,20);int len = lseek(fd,0,SEEK_END);//int len = 0;p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,0);  //文件只写

 

	fd = open("testmap",O_RDWR);    //文件的读写

 

注意:当文件是读写的时候可以写,当文件是只读的时候可以是只读。当文件是只写的时候不能写 

    (5). 文件描述符fd,在mmap创建映射区完成即可关闭。后续访问文件,用地址访问。

	//fd = open("testmap",O_RDONLY|O_CREAT|O_TRUNC,0644);fd = open("testmap",O_RDWR);if(fd == -1){sys_err("open error");}ftruncate(fd,20);int len = lseek(fd,0,SEEK_END);//int len = 0;p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,0);//文件只写if(p == MAP_FAILED){sys_err("mmap error");}close(fd);    //创建映射区完成即可关闭

  (6). offset 必须是 4096的整数倍。(MMU 映射的最小单位 4k

p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,1000);

p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,4096);

 

    (7). 对申请的映射区内存,不能越界访问。 

p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,0);.......strcpy(p+len+4096*4,"hello mmap\n");

  

    (8). munmap用于释放的地址,必须是mmap申请返回的地址。

p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,0);.......strcpy(p++,"hello mmap\n");

  

 解决方法:

 char *temp = p;strcpy(temp++,"hello mmap\n");

  

    (9). 映射区访问权限为 “私有”MAP_PRIVATE, 对内存所做的所有修改,只在内存有效,不会反应到物理磁盘上。

	int fdfd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);//fd = open("testmap",O_RDWR);if(fd == -1){sys_err("open error");}ftruncate(fd,20);int len = lseek(fd,0,SEEK_END);//int len = 0;p = mmap(NULL,len,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd,0);//可读可写

  

    (10).映射区访问权限为 “私有”MAP_PRIVATE, 需要open文件时,有读权限用于创建映射区即可。

    int fd;//int fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);fd = open("testmap",O_RDONLY);    //O_RDONLY: 以只读的方式打开if(fd == -1){sys_err("open error");}//ftruncate(fd,20);int len = lseek(fd,0,SEEK_END);//int len = 0;p = mmap(NULL,len,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd,0);//可读可写if(p == MAP_FAILED){sys_err("mmap error");}

*4.mmap函数的保险调用方式:

    1. fd = open("文件名", O_RDWR);

    2. mmap(NULL, 有效文件大小PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

5.mmap父子进程通信:

        父子等有血缘关系的进程之间也可以通过 mmap建立的映射区来完成数据通信。但相应的要在创建映射区的时候指定对应的标志位参数flags:

        MAP_PRIVATE:(私有映射)父子进程各自独占映射区;

        MAP_SHARED:(共享映射)父子进程共享映射区;
 

父进程创建映射区,然后fork子进程,子进程修改映射区内容,而后,父进程读取映射区内容,查验是否共享。

思路:

父进程先创建映射区。 open( O_RDWR) mmap( MAP_SHARED );

 指定 MAP_SHARED 权限

 fork() 创建子进程。

 一个进程读, 另外一个进程写

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>//全局变量
int var = 100;int main(void)
{int *p;pid_t pid;int fd;fd = open("temp",O_RDWR|O_CREAT|O_TRUNC,0644);if(fd < 0){perror("open error");exit(1);}unlink("temp");ftruncate(fd,4);p = (int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);    //MAP_SHARED共享映射//p = (int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);   //MAP_PRIVATE私有映射if(p == MAP_FAILED){        //注意:不是p == NULLperror("mapp error");exit(1);}close(fd);           //映射区建立完毕,即可关闭文件pid = fork();        //创建子进程if(pid == 0){        //子进程*p = 2000;       //写共享内存var = 1000;printf("child *p = %d,var = %d\n",*p,var);}else if(pid > 0){    //父进程sleep(1);printf("parent *p = %d,var = %d\n",*p,var);    //读共享内存wait(NULL);int ret = munmap(p,4);    //释放映射区if(ret == -1){perror("munmap error");exit(1);}}return 0;
}

//p = (int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);    //MAP_SHARED共享映射p = (int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);   //MAP_PRIVATE私有映射

 

6.无血缘关系进程间 mmap 通信:                  

    两个进程 打开同一个文件,创建映射区。

    指定flags 为 MAP_SHARED。

    一个进程写入,另外一个进程读出。

   【注意】:无血缘关系进程间通信。

                                mmap:数据可以重复读取。

                                fifo:数据只能一次读取。

匿名映射:只能用于血缘关系进程间通信

p = (int *)mmap(NULL, 40,PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

写端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>struct student {int id;char name[256];int age;
};void sys_err(const char *str)
{perror(str);exit(1);
}int main(int argc, char *argv[])
{struct student stu = {1, "xiaoming", 18};struct student *p;int fd; fd = open("test_map", O_RDWR|O_CREAT|O_TRUNC, 0664);
//    fd = open("test_map", O_RDWR);if (fd == -1)sys_err("open error");ftruncate(fd, sizeof(stu));p = mmap(NULL, sizeof(stu), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if (p == MAP_FAILED)sys_err("mmap error");close(fd);while (1) {memcpy(p, &stu, sizeof(stu));stu.id++;sleep(2);}munmap(p, sizeof(stu));return 0;
}

读端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>struct student {int id;char name[256];int age;
};void sys_err(const char *str)
{perror(str);exit(1);
}int main(int argc, char *argv[])
{struct student stu;struct student *p;int fd; fd = open("test_map", O_RDONLY);if (fd == -1)sys_err("open error");p = mmap(NULL, sizeof(stu), PROT_READ, MAP_SHARED, fd, 0);if (p == MAP_FAILED)sys_err("mmap error");close(fd);while (1) {printf("id= %d, name=%s, age=%d\n", p->id, p->name, p->age);sleep(1);}munmap(p, sizeof(stu));return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

车船边缘网关是如何给车辆船只定位的?

随着智能交通系统的不断发展&#xff0c;车路协同成为了重要的研究方向之一。而AI边缘计算网关在这个领域中发挥着至关重要的作用。本文将重点介绍AI边缘计算网关在车路协同中的应用&#xff0c;并强调其中的重点词汇或短语。 首先&#xff0c;什么是AI边缘计算网关&#xff1…

使用命令行创建仓库

如果你还没有任何代码&#xff0c;可以通过命令行工具创建一个全新的Git仓库并初始化到本项目仓库中。 git clone https://e.coding.net/***/neurosens.git cd neurosens echo "# neurosens" >> README.md git add README.md git commit -m "first commi…

2023年09月编程语言流行度排名

点击查看最新编程语言流行度排名&#xff08;每月更新&#xff09; 2023年09月编程语言流行度排名 编程语言流行度排名是通过分析在谷歌上搜索语言教程的频率而创建的 一门语言教程被搜索的次数越多&#xff0c;大家就会认为该语言越受欢迎。这是一个领先指标。原始数据来自…

解决windows下git操作提示用户名密码错误的问题

当代码从一个平台切换到另一个平台的时候&#xff0c;需要做两步操作&#xff0c;第一步就是更新git的仓库地址&#xff0c;在项目的.git/config文件里面修改&#xff0c;这一步做完之后&#xff0c;就可以推送代码到新的仓库了&#xff0c;这里就是重点来了。 一般第一次推动代…

27.方向标

题目 描述 一位木匠收到了一个木制指示牌的订单。每块木板必须与前一块垂直对齐&#xff0c;要么与前一个箭头的基部对齐&#xff0c;要么与相反的一侧对齐&#xff0c;在那里用特制的螺钉固定。两块木板必须重叠。木匠将设计师发送的草图编码成了一个整数序列&#xff0c;但…

lv3 嵌入式开发-7 linux shell脚本编程(分支语句、循环语句)

目录 1 分支语句 2 多路分支语句 3 for的用法 4 while的用法 5 循环控制语句 6 练习 1 分支语句 语法结构: if 表达式then 命令表fi 如果表达式为真, 则执行命令表中的命令; 否则退出if语句, 即执行fi后面的语句。 if和fi是条件语句的语句括号, 必须成对使用; …

C++,day0907

#include <iostream>using namespace std; struct stu { private:int num; private:double score[32];public:void setNum(){cout <<"请输入学生人数:";cin >>num;}void input(){cout<<"请输入学生的成绩:"<<endl;for(int i…

ARM DIY(九)陀螺仪调试

前言 今天调试六轴陀螺仪 MPU6050 硬件 硬件很简单&#xff0c;使用 I2C 接口&#xff0c;并且没有使用中断引脚。 焊接上 MPU6050 芯片和上拉电阻、滤波电容。 检测 MPU6050 是挂在 I2C-0 上的&#xff0c;I2C-0 控制器的驱动已 OK&#xff0c;所以直接使用 I2C-0 检测 …

Linux与shell命令行学习

文章目录 走进shell基本的bash shell命令2.1 遍历目录 cd2.2 查看文件和目录列表 ls2.3 创建文件 touch2.4 复制文件 cp2.5 自动补全 tab2.6 链接文件 ln2.7 文件重命名 mv2.8 删除文件 rm2.9 创建目录 mkdir2.10 删除目录 rmdir2.11 查看文件类型 file2.12 查看整个文件 cat、…

ABAP BAPI_ACC_DOCUMENT_POST 中 EXTENSION1的用法

BAPI_ACC_DOCUMENT_POST 在过账会计凭证时候&#xff0c;经常会发现一些标准字段在参数中并没有 可以通过CMOD/SMOD增强出口--》ACBAPI01--》EXIT_SAPLACC4_001--》ZXACCU15 示例代码&#xff1a; DATA: wa_extension TYPE bapiextc,it_extension TYPE STANDARD TABLE OF ba…

软件工程概述

软件工程概述 软件工程指的是应用计算机科学、数学及管理科学等原理&#xff0c;以工程化的原则和方法来解决软件问题的工程&#xff0c;目的是提高软件生产效率、提高软件质量、降低软件成本。 1. 计算机软件 计算机软件指的是计算机系统中的程序及其文档。程序是计算任务的…

圆圈加数字的css

方式一 .circle { width: 50px; height: 50px; border-radius: 50%; background-color: #f00; color: #fff; text-align: center; line-height: 50px; } .circle::before { content: attr(data-number); display: block; } <div class"circle" data-number"…

C++学习笔记--函数重载(2)

文章目录 1.3、Function Templates Handling1.3.1、Template Argument Deduction1.3.2、Template Argument Substitution 1.4、Overload Resolution1.4.1、Candidate functions1.4.2、Viable functions1.4.3、Tiebreakers 1.5、走一遍完整的流程1.6、Name Mangling1.7、总结 1.…

深度ESP32 PWM教程如何在ESP32 中使用PWM

关于ESP32PWM的简要说明 ESP32 SoC 满载了非常有用的外设&#xff0c;PWM 就是其中之一。是的。ESP32 的芯片中有一个专用的 PWM 硬件模块。脉宽调制或简称PWM是一种成熟且广泛使用的供电技术。 您可以使用 ESP32 的 PWM 来驱动 LED、电机&#xff08;普通直流电机和无刷电机…

qt文件操作

对话框练习 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//字体按钮 void Widget::on_ztbtn_clicked() {//调用QFontDia…

01_Flutter之下拉刷新和上拉加载

一.创建页面 由于我们需要请求网络&#xff0c;并将返回的数据渲染到页面上&#xff0c;所以需要继承StatefulWidget&#xff0c;本文涉及的接口&#xff0c;取自鸿神的玩android开放API class ProjectListPage extends StatefulWidget {overrideState<StatefulWidget>…

js摄像头动态检测

利用摄像头每一秒截图一次图像。然后计算2次图像之间的相似度。 如果相似度低于98%就会报警。 var video document.getElementsByClassName(inputvideo)[0]; video.innerHTML "<video classinput_video idcamera autoplay width640px height380px></video>…

windows10使用wheel安装tensorflow2.13.0/2.10.0

安装过程 安装虚拟环境安装virtualenv安装满足要求的python版本使用virtualenv创建指定python版本的虚拟环境 安装tensorflow2.13.0安装tensorflow-docs直接下载使用wheel下载 在VSCode编辑器中使用虚拟环境下的包 安装虚拟环境 这里笔者使用的是 virtualenv进行虚拟环境搭建的…

VIRTIO-BLK代码分析(2)VIRTIO驱动分析

QEMU模拟的VIRTIO设备同时也是PCIE设备&#xff0c;Guest中VIRTIO PCIE驱动与之匹配&#xff0c;根据设备驱动模型&#xff0c;最终触发probe函数virtio_pci_probe()。该probe函数使能PCIE设备&#xff0c;并注册VIRTIO设备&#xff0c;并与VIRTIO-BLK匹配&#xff0c;触发VIRT…