一,管道PIPE

二,FIFO通信

三,mmap通信

创建内存映射区。

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);

函数mmap:打开一个文件,指定一个文件的区域,作为一个区域,映射到内存中,以后就直接操作那个内存,就能够实现进程间的通信。因为是内存操作,所以速度最快。

  • addr:固定NULL
  • length:拿出文件中的多长的一段,映射到内存。
  • offset:从文件内容中的哪个位置开始拿。
  • prot
    • PROT_EXEC Pages may be executed
    • PROT_READ Pages may be read.
    • PROT_WRITE Pages may be written.
    • PROT_NONE Pages may not be accessed
  • flags
    • MAP_SHARED:对内存里的值进行修改,会反映到文件,也就是文件也被修改。
    • MAP_PRIVATE:对内存里的值进行修改,不会反映到文件,文件不会被修改。
  • offset:起始位置
  • 返回值
    • 成功:可用的内存的首地址
    • 失败:MAP_FAILED (that is, (void *) -1)

释放内存映射区。

#include <sys/mman.h>
int munmap(void *addr, size_t length);
  • addr:mmap的返回值
  • length:mmap创建的长度
  • 返回值:成功0;失败-1.

例子:

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h> int main(){
int fd = open("mem", O_RDWR);
//char* buf = mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
char* buf = mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
printf("%s\n", buf);
strcpy(buf, "FFFFF");
//释放映射区
munmap(buf, 8);
close(fd);
}

mmap的七个问题:

  • 如果更改上面例子里变量buf的地址,释放的时候munmap,还能成功吗?

    不能成功。错误信息:【Invalid argument】

  • 对映射区的操作,越界了会怎么样。

    • open文件size > 要写入的size > mmap参数的length:能够全部写入文件。
    • open文件size < 要写入的size > mmap参数的length:不能全部写入文件,能够写入的size是open文件的size
  • 偏移量随便填个数字会怎么样。

    mmap函数出错,错误信息:【Invalid argument】

    offset必须是4K的整数倍,【0,4*1024。。。】

    用【stat】命令查看文件,发现文件的size实际小于4096,但是【IO Block: 4096】

      File: pi2.c
    Size: 442 Blocks: 8 IO Block: 4096 regular file
    Device: 801h/2049d Inode: 424247 Links: 1
    Access: (0664/-rw-rw-r--) Uid: ( 1000/ ys) Gid: ( 1000/ ys)
    Access: 2019-05-02 12:54:13.812282158 +0800
    Modify: 2019-04-29 13:49:42.489004001 +0800
    Change: 2019-04-29 13:49:42.489004001 +0800
  • 如果文件描述符先关闭,对mmap映射有没有影响。

    没有影响。

  • open的时候,可以用新创建一个文件的方式,来创建映射区吗?

    错误:Bus error (core dumped)。

    错误理由是:创建的文件的size为0,所以出上面的错误。新创建一个文件后,马上把文件大小扩展一下就不会发生错误了。

    int fd = open("mem", O_RDWR|O_CREAT, 0666);
    ftruncate(fd, 8);//把新创建的文件mem的大小扩展为8.
  • open文件时,选择O_WRONLY,可以吗

    mmap函数出错,错误:【Permission denied】。

    因为要把文件的内容读到内存,所以隐含一次读取操作,所以没有读的权限的话,就出这个错误。

  • 当选择MAP_SHARED的时候,open文件选择O_RDONLY,prot可以选择【PROT_READ|PROT_WRITE】吗

    mmap函数出错,错误:【Permission denied】。

    MAP_SHARED的时候会去写文件,但是open的时候只有读权限,所以权限不够。

用mmap实现父子进程间通信的例子:

注意:参数flags必须是MAP_SHARED,因为2个进程间通信,需要互相读写,所以必须是MAP_SHARED

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h> int main(){
int fd = open("mem", O_RDWR);
int* mem = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(mem == MAP_FAILED){
perror("mmap");
return -1;
}
pid_t pid = fork(); if(pid == 0){
*mem = 100;
printf("child:mem=%d\n", *mem);
sleep(3);
printf("child:mem=%d\n", *mem);
}
else if(pid > 0){
sleep(1);
printf("parent:mem=%d\n", *mem);
*mem = 200;
printf("parent:mem=%d\n", *mem);
wait(NULL);
} munmap(mem, 4);
close(fd);
}

执行结果:

child:mem=100
parent:mem=100
parent:mem=200
child:mem=200

不知道读者同学们发现了没有,用mmap有个非常鸡肋的地方,就是必须要使用一个文件,其实这个文件对程序没有什么作业。所以linux给我们提供了一个方法,叫做【匿名映射】。

匿名映射:在调用mmap函数时候,在flags参数那里,设置【MAP_ANON】,并在fd参数那里设置【-1】。

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

有个问题,在有些unix系统里是没有【MAP_ANON】【MAP_ANONYMOUS】这2个宏的,这2个宏的作用是一样的,其中一个是简写。那怎么办呢?

使用下面2个文件去映射,因为要用文件,所以必须还得有open的调用,但好处是不用事先做出一个大小合适的文件了。

  • /dev/zero:可以随意映射,size无限大,诨名叫【聚宝盆】
  • /dev/null:可以随意映射,size无限大,但映射完后,文件里不会存有任何内容,所以也被叫成【无底洞】,一般错误日志太多,而且不想保留的时候,会重定向到这个文件。

匿名映射的弱点:不能实现无学员关系进程间的通信。

用mmap实现无血缘关系的进程间通信的例子:

写入端:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h> typedef struct _student{
int id;
char name[20];
}Student; int main(int argc, char* argv[]){ int fd = open("aaa", O_RDWR|O_TRUNC|O_CREAT, 0666);
int length = sizeof(Student);
ftruncate(fd, length);
Student* std = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(std == MAP_FAILED){
perror("mmap");
return -1;
} int num = 0;
while(1){
std->id = num;
sprintf(std->name, "xiaoming-%03d", num++);
sleep(1);
} munmap(std, length);
close(fd);
}

读入端:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h> typedef struct _student{
int id;
char name[20];
}Student; int main(int argc, char* argv[]){ int fd = open("aaa", O_RDWR);
int length = sizeof(Student);
Student* std = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(std == MAP_FAILED){
perror("mmap");
return -1;
} while(1){
printf("id:%03d, name:%s\n", std->id, std->name);
sleep(1);
} munmap(std, length);
close(fd);
}

利用mmap实现用多个进程拷贝一个文件的例子



核心思想:把要拷贝的文件和目标文件都映射成内存映射区,然后在各自的子进程里做拷贝,拷贝时,注意起点和大小。

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h> //argv[1]:进程的数量
//argv[2]:要拷贝的文件
int main(int argc, char* argv[]){
if(argc < 3){
printf("bad argv,need 3 arg\n");
return -1;
} //用stat函数取得文件的大小
struct stat sbuf;
int ret = stat(argv[2], &sbuf);
if(ret < 0){
perror("stat");
return -1;
} //文件的大小
off_t sz = sbuf.st_size; //余数
off_t yu = sz % atoi(argv[1]);
//每个进程拷贝的大小
off_t proSz = (sz - yu) / atoi(argv[1]); //open src file
int srcfd = open(argv[2], O_RDONLY); //create target file
char wk[20] = {0};
sprintf(wk, "%s.copy", argv[2]);
int decfd = open(wk,O_RDWR|O_CREAT|O_TRUNC, 0766);
//创建和src文件同等大小的目标文件
ret = ftruncate(decfd, sz);
if(ret < 0){
perror("ftruncate");
return -1;
} //打开要被拷贝文件的内存映射区
void* src = mmap(NULL, sz, PROT_READ, MAP_SHARED, srcfd, 0);
if(src == MAP_FAILED){
perror("mmap src:");
return -1;
} //打开要目标文件的内存映射区
void* dst = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, decfd, 0);
if(dst == MAP_FAILED){
perror("mmap dst:");
return -1;
} int i;
int pCnt = atoi(argv[1]); //创建子进程,都是这些子进程都是本程序的子进程
for(i = 0; i < pCnt; ++i){
//in child process
if(fork() == 0)
break;
} //子进程的处理
if(i < pCnt){
//last time
if(i == pCnt - 1){
memcpy(dst+i*proSz, src+i*proSz, proSz+yu);
}
else{
memcpy(dst+i*proSz, src+i*proSz, proSz);
}
}
//父进程的处理
else{
for(int i = 0; i < pCnt; ++i){
wait(NULL);
}
//printf("parent pid=%d\n", getpid());
if(munmap(src, sz) == -1){
perror("munmap src");
}
if(munmap(dst, sz) == -1){
perror("munmap dsc");
}
close(srcfd);
close(decfd); }
}

c/c++ 学习互助QQ群:877684253

本人微信:xiaoshitou5854

linux 进程通信之 mmap的更多相关文章

  1. Linux进程通信之mmap

    mmap()函数: void *mmap(void* addr,size_t length,int port,int flags,int fd,off_t offset); 返回:成功:返回创建的映射 ...

  2. Linux进程通信----匿名管道

    Linux进程通信中最为简单的方式是匿名管道 匿名管道的创建需要用到pipe函数,pipe函数参数为一个数组表示的文件描述字.这个数组有两个文件描 述字,第一个是用于读数据的文件描述符第二个是用于写数 ...

  3. Linux 进程通信之 ——信号和信号量总结

    如今最经常使用的进程间通信的方式有:信号,信号量,消息队列,共享内存.       所谓进程通信,就是不同进程之间进行一些"接触",这种接触有简单,也有复杂.机制不同,复杂度也不一 ...

  4. Linux进程通信学习总结

    http://blog.csdn.net/xiaoweibeibei/article/details/6552498 SYSV子系统的相关概念   引用标识符:引用标识符是一个整数,表示每一个SYSV ...

  5. Linux进程通信的几种方式总结

    进程通信的目的 数据传输 一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间 共享数据 多个进程想要操作共享数据,一个进程对共享数据 通知事 一个进程需要向另一个或一组进程发 ...

  6. linux进程通信之管道

    1.介绍: 1)同一主机: unix进程通信方式:无名管道,有名管道,信号 system v方式:信号量,消息队列,共享内存 2)网络通信:Socket,RPC 2.管道: 无名管道(PIPE):使用 ...

  7. linux 进程通信之 共享内存

    共享内存是被多个进程共享的一部分物理内存.共享内存是进程间共享数据的一种最快的方法.一个进程向共享内存区域写入了数据,共享这个内存区域的全部进程就能够立马看到当中的内容. 关于共享内存使用的API k ...

  8. linux 进程通信之 信号

    一,管道PIPE 二,FIFO通信 三,mmap通信 四,信号的概念 信号的特点:简单,但不能携带大量的信息,满足特定条件就会发生 信号的机制:进程B发送信号给进程A.信号是由内核来处理的. 信号的产 ...

  9. linux进程通信

    e14: 进程间通信(进程之间发送/接收字符串/结构体): 传统的通信方式: 管道(有名管道 fifo,无名管道 pipe) 信号 signal System V(基于IPC的对象):         ...

随机推荐

  1. Vuex简介

    一. 什么是Vuex?   Vuex Vuex是一个专门为Vue.js应用程序开发的状态管理模式, 它采用集中式存储管理所有组件的公共状态, 并以相应的规则保证状态以一种可预测的方式发生变化. Vue ...

  2. 面向对象程序设计(JAVA) 第11周学习指导及要求

    2019面向对象程序设计(Java)第11周学习指导及要求 (2019.11.8-2018.11.11)   学习目标 理解泛型概念: 掌握泛型类的定义与使用: 掌握泛型方法的声明与使用: 掌握泛型接 ...

  3. Pwn-TestYourMemory

    题目地址 https://dn.jarvisoj.com/challengefiles/memory.838286edf4b832fd482d58ff1c217561 32位的程序,有NX保护,拖到I ...

  4. 第七周第二次Scrum会议

    工作: 根据上个周对前端的布置学习,布置大家开发了相应的界面.(界面开发成果写在测试报告) 地点: 二餐二 会议照片: 忘记拍了... 核心问题: 如何编写统一风格的前端界面. 解决: 将相应资源打包 ...

  5. JDOJ1178:铺地板II

    JDOJ1178:铺地板II https://neooj.com/oldoj/problem.php?id=1178 题目描述 用1 x 1和2 x 2的磁砖不重叠地铺满N x 3的地板,共有多少种方 ...

  6. 2019 SDN上机第六次作业

    1.实验拓扑 (1)实验拓扑 (2)使用python脚本完成拓扑搭建 from mininet.topo import Topo class Mytopo(Topo): def __init__(se ...

  7. Codeforces Round #554 (Div. 2) D 贪心 + 记忆化搜索

    https://codeforces.com/contest/1152/problem/D 题意 给你一个n代表合法括号序列的长度一半,一颗有所有合法括号序列构成的字典树上,选择最大的边集,边集的边没 ...

  8. 【CF525E】Anya and Cubes(meet in middle)

    点此看题面 大致题意: 在\(n\)个数中选任意个数,并使其中至多\(k\)个数\(x_i\)变为\(x_i!\),求使这些数和为\(S\)的方案数. \(meet\ in\ middle\) 这应该 ...

  9. C语言第2

    今个那老哥替我刷题,然后遇到一个打印中文编码得问题,我多嘴一问,为啥 用 unsiged 然后他让我换成char试试:然后出现了以下结果 #include <stdio.h> #inclu ...

  10. Avalon的小故事 (1)

    我:这个游戏真没意思,我为什么要玩这种东西 A:你渡过了前期才能发现这个游戏的乐趣 我:那我为什么不换一个游戏玩呢?换一个开头就让人欲罢不能的游戏? B:你这是逃避!你个懦夫! 我:我连自己选择游戏的 ...