共享存储映射

文件进程间通信

使用文件也可以完成 IPC,理论依据是,fork 后,父子进程共享文件描述符。也就共享打开的文件。
编程:父子进程共享打开的文件。借助文件进行进程间通信。
测试代码

/*** 父子进程共享打开的文件描述符----使用文件完成进程间通信**/#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/wait.h>int main(void)
{int fd1,fd2;pid_t pid;char buf[1024];                                                                char *str="----------test for shared fd in parent child process -----\n";pid=fork();if(pid<0){perror("fork error");exit(1);}else if(pid==0){fd1=open("test.txt",O_RDWR);if(fd1<0){perror("open error");exit(1);}   write(fd1,str,strlen(str));printf("child wrote over...\n");}else{fd2=open("test.txt",O_RDWR);if(fd2<0){perror("open error");exit(1);}sleep(1);   //保证子进程写入数据int len =read(fd2,buf,sizeof(buf));write(STDOUT_FILENO,buf,len);wait(NULL);//回收防止僵尸进程}return 0;
}

思考,无血缘关系的进程可以打开同一个文件进行通信吗?为什么?
可以,,无血缘关系的进程也可以打开同一个文件进行通信,方法一样,因为这些进程打开的是同一个进程。其实在打开文件时(调用open时),操作系统内核就调用了mmap。因为一个文件只有一个文件结构体(FILE),打开时位于内核,被打开这个文件的多个进程共享。

存储映射 I/O

存储映射 I/O(Memory-mappedI/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取 数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可 在不适用 read 和 write 函数的情况下,使用地址(指针)完成 I/O 操作。
使用这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过 mmap 函数来实
现。

mmap 函数

void* mmap(void* adrr,size_t length,int prot,int flags,int fd,off_toffset); 返回:成功:返回创建的映射区首地址;失败:MAP_FAILED 宏
参数:

  1. addr: 建立映射区的首地址,由 Linux 内核指定。使用时,直接传递 NULL

  2. length: 欲创建映射区的大小

  3. prot: 映射区权限 PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE

  4. flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区) MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。 MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。

  5. fd: 用来建立映射区的文件描述符

  6. offset: 映射文件的偏移(4k 的整数倍)

     #include<stdio.h>#include<fcntl.h>#include<unistd.h>#include<string.h>#include<stdlib.h>#include<sys/mman.h>int main(void){int len,ret;char *p=NULL;int fd = open("mytest.txt",O_CREAT|O_RDWR,0644);if(fd<0){perror("open error");exit(1);}   //确定文件大小len = ftruncate(fd,4);if(len == -1){perror("ftruncate error:");exit(1);}   p = mmap(NULL, 4 , PROT_READ|PROT_WRITE , MAP_SHARED, fd, 0); if(p == MAP_FAILED){perror("mmap error:");exit(1);}   strcpy(p,"abc");//写数据ret = munmap(p,4);//释放映射区                                                 if(ret==-1){perror("munmap error:");exit(1);}   close(fd);return 0;}
    

munmap 函数

同 malloc 函数申请内存空间类似的,mmap 建立的映射区在使用结束后也应调用类似 free 的函数来释放。 int munmap(void *addr,size_t length); 成功:0; 失败:-1

mmap 注意事项

思考:

  1. 可以 open 的时候 O_CREAT 一个新文件来创建映射区吗?
    答:可以,但新CREATE出来的文件不行,必须要有实际的大小
  2. 如果 open 时 O_RDONLY,mmap 时 PROT 参数指定 PROT_READ|PROT_WRITE 会怎样?
    答:不行,创建映射区的权限小于等于打开文件的权限,映射区创建的过程中存在一次读文件操作权限不足。
  3. 文件描述符先关闭,对 mmap 映射有没有影响?
    答: 没有影响,文件描述符是操作文件的句柄,有映射区了,就是地址的方式操作,所以文件描述符就没意义了。
  4. 如果文件偏移量为 1000 会怎样?
    答:不行,偏移量必须是一页的大小(4k)
  5. 对 mem 越界操作会怎样?
    答:不行,释放映射区可能会失败,只有创建映射区的地址和释放时的地址要是同一个地址
  6. 如果 mem++,munmap 可否成功?
    答:不能,释放映射区的时候要传首地址和映射区大小给munmap,但++后首地址就不是创建时的首地址,只有创建映射区的地址和释放时的地址要是同一个地址
  7. mmap 什么情况下会调用失败? 8. 如果不检测 mmap 的返回值,会怎样?
    答:返回值必须检查,空间不能为0,文件大小和映射区要匹配等等情况下会失败

总结

  1. 创建映射区的过程中,隐含着一次对映射文件的读操作。
  2. 当 MAP_SHARED 时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护)。而 MAP_PRIVATE 则无所谓,因为 mmap 中的权限是对内存的限制。
  3. 映射区的释放与文件关闭无关。只要映射建立成功,文件可以立即关闭。
  4. 特别注意,当映射文件大小为 0 时,不能创建映射区。所以:用于映射的文件必须要有实际大小!! mmap 使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。
  5. munmap 传入的地址一定是 mmap 的返回地址。坚决杜绝指针++操作。
  6. 如果文件偏移量必须为 4K 的整数倍 7. mmap 创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。

mmap 父子进程通信

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

  1. MAP_PRIVATE: (私有映射) 父子进程各自独占映射区;
  2. MAP_SHARED: (共享映射) 父子进程共享映射区;
    编程:父进程创建映射区,然后 fork 子进程,子进程修改映射区内容,而后,父进程读取映射区内容,查验是 否共享

    #include<stdio.h>                                                                  #include<stdlib.h>#include<unistd.h>#include<fcntl.h>#include<sys/mman.h>#include<sys/wait.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);//父子进程共享映射区// p=(int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);//对映射区各自独>占    if(p==MAP_FAILED){              //不是p==NULL时出错perror("mmap,error");       exit(1);}close(fd);              //映射区建立完毕,即可关闭文件//完成数据传递pid=fork();if(pid==0){*p=2000;var=1000;printf("child,*p=%d,var = %d\n",*p,var);}else{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;}

结论:父子进程共享:

  1. 打开的文件
  2. mmap 建立的映射区(但必须要使用 MAP_SHARED)

匿名映射

通过使用我们发现,使用映射区来完成文件读写操作十分方便,父子进程间通信也较容易。但缺陷是,每次创 建映射区一定要依赖一个文件才能实现。通常为了建立映射区要 open 一个 temp 文件,创建好了再 unlink、close 掉,比较麻烦。 可以直接使用匿名映射来代替。其实 Linux 系统给我们提供了创建匿名映射区的方法,无需依赖一 个文件即可创建映射区。同样需要借助标志位参数 flags 来指定。
使用 MAP_ANONYMOUS(或 MAP_ANON),

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

"4"随意举例,该位置表大小,可依实际需要填写。

需注意的是,MAP_ANONYMOUS 和 MAP_ANON 这两个宏是 Linux 操作系统特有的宏。在类 Unix 系统中如无该 宏定义,可使用如下两步来完成匿名映射区的建立。

  1. fd=open("/dev/zero",O_RDWR);
  2. p=mmap(NULL,size,PROT_READ|PROT_WRITE,MMAP_SHARED,fd,0);

示例代码:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/wait.h>int var=100;int main(void )
{int *p; pid_t pid;//不使用文件参数传-1                                                           p=(int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);//对映射区>各自独占if(p==MAP_FAILED){              //不是p==NULL时出错perror("mmap,error");    exit(1);}   //完成数据传递pid=fork();if(pid==0){*p=2000;var=1000;printf("child,*p=%d,var = %d\n",*p,var);}else{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;
}


Linux下这两个文件无大小

第一个文件好比聚宝盆,可以随意映射
第二个文件,一般错误洗脑洗重定向到这个文件中

mmap 无血缘关系进程间通信

实质上 mmap 是内核借助文件帮我们创建了一个映射区,多个进程之间利用该映射区完成数据传递。由于内核 空间多进程共享,因此无血缘关系的进程间也可以使用 mmap 来完成通信。只要设置相应的标志位参数 flags 即可。 若想实现共享,当然应该使用 MAP_SHARED 了。

读数据

/** 非血缘关系进程间通信*/
#include<stdio.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/mman.h>
#include<string.h>struct STU{int id; char name[20];char sex;
};//出错处理函数
void sys_err(char *str)
{perror(str);exit(-1);
}int main(int argc,char *argv[])
{int fd; struct STU student;struct STU *mm;if(argc<2){printf("./a.out file_shared\n");exit(-1);}   fd=open(argv[1],O_RDONLY);if(fd == -1) sys_err("open error");mm = mmap(NULL,sizeof(student),PROT_READ,MAP_SHARED,fd,0);if(mm == MAP_FAILED)sys_err("mmap error");close(fd);while(1){  //读进程 printf("id=%d\tname=%s\t%c\n",mm->id,mm->name,mm->sex);sleep(2);}munmap(mm,sizeof(student));return 0;
}

写数据

/*                                                                                 * 非血缘关系之间的通信*/
#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/mman.h>
#include<string.h>struct STU{int id;char name[20];char sex;
};void sys_err(char *str)
{perror(str);exit(1);
}int main(int argc,char *argv[])
{int fd;struct STU student={10,"xiaoming",'m'};char *mm;if(argc<2){printf("./a.out file_shared\n");exit(-1);}fd = open(argv[1],O_RDWR | O_CREAT,0644);ftruncate(fd,sizeof(student));mm = mmap(NULL,sizeof(student),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);if(mm == MAP_FAILED)sys_err("mmap"); close(fd);while(1){memcpy(mm,&student,sizeof(student));student.id++; //循环向内存复制sleep(1);}munmap(mm,sizeof(student));return 0;
}



Linux系统编程---5(共享存储映射,存储映射I/O,mmap函数,父子进程间通信,匿名映射)相关推荐

  1. Linux系统编程18:超详解进程程序替换exec函数的一些用法

    文章目录 (1)进程程序替换是什么 (2)exec-替换函数 (3)实例展示-了解exec函数的替换原理 A:execl和execv B:execlp和execvp C:替换自己的程序和execle ...

  2. Linux系统编程【文件IO、进程、进程间通信、信号、线程、互斥】

    linux系统编程 个人通过学习,手打了一份48000字的Linux系统编程的笔记,包含了[文件IO.进程.进程间通信.信号.多线程.互斥]等知识点,并给出了大量的代码案例对每个重要的知识点进行了代码 ...

  3. Linux系统编程(二)孤儿进程和僵尸进程

    Linux系统编程(二) 一.exec函数族 1.exec函数 二.孤儿进程和僵尸进程 三.wait和waitpid 1.wait函数 2.waitpid函数 一.exec函数族 exec函数使用时, ...

  4. Linux系统编程(三)进程间的通信

    Linux系统编程(三)进程间的通信 一.为什么需要进程之间的通信(IPC)? 二.管道 1.概念 2.特质 3.原理 4.局限性 5.代码 2.读入数据 三.共享存储映射 注意事项 父子进程通信 一 ...

  5. Linux系统编程(一)

    Linux系统编程(一) 一.进程和程序 二.内存布局 内核空间 用户空间 三.进程状态 四.环境变量 五.进程共享 一.进程和程序 程序:是指编译好的二进制文件,存储在磁盘中,不占用系统资源. 进程 ...

  6. 【Linux | 系统编程】Linux系统编程(文件、进程线程、进程间通信)

    文章目录 Linux系统编程 文件IO open/close函数 read/write函数 文件描述符 阻塞.非阻塞 fcntl函数 lseek函数 传入传出参数 文件系统 文件存储 文件操作 sta ...

  7. 【Linux系统编程】守护进程、线程

    ------------->[Linux系统编程/网络编程](学习目录汇总) <-------------- 目录 1.守护进程 1.1 进程组 1.2 会话 1.3 setsid()函数 ...

  8. Linux系统编程笔记

    文章目录 1.Linux系统编程 2.文件IO 2.1 文件描述符 2.2 open 2.3 perror 2.4 close 2.5 write 2.6 read 2.7 remove 2.8 系统 ...

  9. Linux系统编程总结

    day2 vim的三种工作模式 命令模式 vi hello.c zz 保存退出 2.编辑模式 i a o s (有大写)可以写东西 3.末行模式: 文本和末行模式不能直接切换 要切换回命令模式 再到末 ...

  10. 攻克 Linux 系统编程

    课程亮点 完整学习路线图,系统掌握核心知识点 内核源码深入分析,知其然更知所以然 高频问题全面汇总,精准定位症结所在 八大主题商业案例,实操中获得拔高提升 专家推荐 曾与宇文拓共事五年,他对技术的钻研 ...

最新文章

  1. 这些 Shell 分析服务器日志命令集锦,收藏好
  2. jQuery学习笔记(Ajax)
  3. 练习:WinForm (PictureBox和Timer)
  4. oracle 添加登陆数据库触发器--记录IP 地址
  5. 上半年营收超阿迪,相当于2.2个李宁,安踏凭什么?
  6. 数据库面试题【二、MYSQL的两种存储引擎区别(事务、锁级别等等)】
  7. 5G 是否能让国产手机回到群雄割据时代?
  8. Oracle 取某100天的每一天的日期
  9. 免费python全套教程-0基础学python 全套教程送你参考
  10. 计算机网络基础系列(四)HTTP、七层模型及其内部对应协议
  11. POJ 1579 Function Run Fun
  12. 指纹识别 python实现_Python还真当是无所不能!利用Python做指纹识别播报!闻所未闻!-站长资讯中心...
  13. drop index mysql_MySQL修改和删除索引(DROP INDEX)
  14. 手机通话记录重复显示怎么处理_华为出现重复联系人 - 卡饭网
  15. 【计算机网络】集线器、网桥、交换机、路由器、网关大解析
  16. tablayout 滚动模式_Android底部导航栏(可滑动)----TabLayout+viewPager
  17. 苹果ios免越狱脚本实现方案
  18. 同花顺_代码解析_技术指标_P、Q
  19. Avoid object allocations during draw/layout operations
  20. IMS应用领域|IMS连接器系统使自动驾驶成为可能

热门文章

  1. 在java web工程中jsp页面中使用kindeditor
  2. 在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试
  3. jq的链式调用.end();
  4. Proximal Algorithms--Accelerated proximal gradient method
  5. 介绍“Razor”— ASP.NET的一个新视图引擎
  6. Sharepoint 2013 发布功能(Publishing features)
  7. hdu1247(Hat’s Words)
  8. windows 检查cuda安装_Windows云主机GPU驱动-CUDA安装使用
  9. linux子系统备份,使用LxRunOffline工具备份/还原Linux子系统(WSL)
  10. linux rsync删文件速度,为什么用rsync删除大量文件的时候比用rm快