Linux系统编程---5(共享存储映射,存储映射I/O,mmap函数,父子进程间通信,匿名映射)
共享存储映射
文件进程间通信
使用文件也可以完成 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 宏
参数:
addr: 建立映射区的首地址,由 Linux 内核指定。使用时,直接传递 NULL
length: 欲创建映射区的大小
prot: 映射区权限 PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区) MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。 MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。
fd: 用来建立映射区的文件描述符
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 注意事项
思考:
- 可以 open 的时候 O_CREAT 一个新文件来创建映射区吗?
答:可以,但新CREATE出来的文件不行,必须要有实际的大小 - 如果 open 时 O_RDONLY,mmap 时 PROT 参数指定 PROT_READ|PROT_WRITE 会怎样?
答:不行,创建映射区的权限小于等于打开文件的权限,映射区创建的过程中存在一次读文件操作权限不足。 - 文件描述符先关闭,对 mmap 映射有没有影响?
答: 没有影响,文件描述符是操作文件的句柄,有映射区了,就是地址的方式操作,所以文件描述符就没意义了。 - 如果文件偏移量为 1000 会怎样?
答:不行,偏移量必须是一页的大小(4k) - 对 mem 越界操作会怎样?
答:不行,释放映射区可能会失败,只有创建映射区的地址和释放时的地址要是同一个地址 - 如果 mem++,munmap 可否成功?
答:不能,释放映射区的时候要传首地址和映射区大小给munmap,但++后首地址就不是创建时的首地址,只有创建映射区的地址和释放时的地址要是同一个地址 - mmap 什么情况下会调用失败? 8. 如果不检测 mmap 的返回值,会怎样?
答:返回值必须检查,空间不能为0,文件大小和映射区要匹配等等情况下会失败
总结
- 创建映射区的过程中,隐含着一次对映射文件的读操作。
- 当 MAP_SHARED 时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护)。而 MAP_PRIVATE 则无所谓,因为 mmap 中的权限是对内存的限制。
- 映射区的释放与文件关闭无关。只要映射建立成功,文件可以立即关闭。
- 特别注意,当映射文件大小为 0 时,不能创建映射区。所以:用于映射的文件必须要有实际大小!! mmap 使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。
- munmap 传入的地址一定是 mmap 的返回地址。坚决杜绝指针++操作。
- 如果文件偏移量必须为 4K 的整数倍 7. mmap 创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。
mmap 父子进程通信
父子等有血缘关系的进程之间也可以通过 mmap 建立的映射区来完成数据通信。但相应的要在创建映射区的时 候指定对应的标志位参数 flags:
- MAP_PRIVATE: (私有映射) 父子进程各自独占映射区;
- 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;}
结论:父子进程共享:
- 打开的文件
- 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 系统中如无该 宏定义,可使用如下两步来完成匿名映射区的建立。
fd=open("/dev/zero",O_RDWR);
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函数,父子进程间通信,匿名映射)相关推荐
- Linux系统编程18:超详解进程程序替换exec函数的一些用法
文章目录 (1)进程程序替换是什么 (2)exec-替换函数 (3)实例展示-了解exec函数的替换原理 A:execl和execv B:execlp和execvp C:替换自己的程序和execle ...
- Linux系统编程【文件IO、进程、进程间通信、信号、线程、互斥】
linux系统编程 个人通过学习,手打了一份48000字的Linux系统编程的笔记,包含了[文件IO.进程.进程间通信.信号.多线程.互斥]等知识点,并给出了大量的代码案例对每个重要的知识点进行了代码 ...
- Linux系统编程(二)孤儿进程和僵尸进程
Linux系统编程(二) 一.exec函数族 1.exec函数 二.孤儿进程和僵尸进程 三.wait和waitpid 1.wait函数 2.waitpid函数 一.exec函数族 exec函数使用时, ...
- Linux系统编程(三)进程间的通信
Linux系统编程(三)进程间的通信 一.为什么需要进程之间的通信(IPC)? 二.管道 1.概念 2.特质 3.原理 4.局限性 5.代码 2.读入数据 三.共享存储映射 注意事项 父子进程通信 一 ...
- Linux系统编程(一)
Linux系统编程(一) 一.进程和程序 二.内存布局 内核空间 用户空间 三.进程状态 四.环境变量 五.进程共享 一.进程和程序 程序:是指编译好的二进制文件,存储在磁盘中,不占用系统资源. 进程 ...
- 【Linux | 系统编程】Linux系统编程(文件、进程线程、进程间通信)
文章目录 Linux系统编程 文件IO open/close函数 read/write函数 文件描述符 阻塞.非阻塞 fcntl函数 lseek函数 传入传出参数 文件系统 文件存储 文件操作 sta ...
- 【Linux系统编程】守护进程、线程
------------->[Linux系统编程/网络编程](学习目录汇总) <-------------- 目录 1.守护进程 1.1 进程组 1.2 会话 1.3 setsid()函数 ...
- 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 系统 ...
- Linux系统编程总结
day2 vim的三种工作模式 命令模式 vi hello.c zz 保存退出 2.编辑模式 i a o s (有大写)可以写东西 3.末行模式: 文本和末行模式不能直接切换 要切换回命令模式 再到末 ...
- 攻克 Linux 系统编程
课程亮点 完整学习路线图,系统掌握核心知识点 内核源码深入分析,知其然更知所以然 高频问题全面汇总,精准定位症结所在 八大主题商业案例,实操中获得拔高提升 专家推荐 曾与宇文拓共事五年,他对技术的钻研 ...
最新文章
- 这些 Shell 分析服务器日志命令集锦,收藏好
- jQuery学习笔记(Ajax)
- 练习:WinForm (PictureBox和Timer)
- oracle 添加登陆数据库触发器--记录IP 地址
- 上半年营收超阿迪,相当于2.2个李宁,安踏凭什么?
- 数据库面试题【二、MYSQL的两种存储引擎区别(事务、锁级别等等)】
- 5G 是否能让国产手机回到群雄割据时代?
- Oracle 取某100天的每一天的日期
- 免费python全套教程-0基础学python 全套教程送你参考
- 计算机网络基础系列(四)HTTP、七层模型及其内部对应协议
- POJ 1579 Function Run Fun
- 指纹识别 python实现_Python还真当是无所不能!利用Python做指纹识别播报!闻所未闻!-站长资讯中心...
- drop index mysql_MySQL修改和删除索引(DROP INDEX)
- 手机通话记录重复显示怎么处理_华为出现重复联系人 - 卡饭网
- 【计算机网络】集线器、网桥、交换机、路由器、网关大解析
- tablayout 滚动模式_Android底部导航栏(可滑动)----TabLayout+viewPager
- 苹果ios免越狱脚本实现方案
- 同花顺_代码解析_技术指标_P、Q
- Avoid object allocations during draw/layout operations
- IMS应用领域|IMS连接器系统使自动驾驶成为可能
热门文章
- 在java web工程中jsp页面中使用kindeditor
- 在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试
- jq的链式调用.end();
- Proximal Algorithms--Accelerated proximal gradient method
- 介绍“Razor”— ASP.NET的一个新视图引擎
- Sharepoint 2013 发布功能(Publishing features)
- hdu1247(Hat’s Words)
- windows 检查cuda安装_Windows云主机GPU驱动-CUDA安装使用
- linux子系统备份,使用LxRunOffline工具备份/还原Linux子系统(WSL)
- linux rsync删文件速度,为什么用rsync删除大量文件的时候比用rm快