LINUX学习笔记
- mmap 进程通信
- 1. mmap 函数声明及头文件包含
- 1.1 参数说明
- 1.2 mmap 通信demo
- 2. mmap 注意事项:
- 2.1 mmap 函数的保险使用方法:
- 3. demo 父子进程间mmap通信
- 4. demo 非血缘关系进程间mmap通信
- 5. mmap通信与fifo和文件通信的差异
- 6. 匿名映射(只能用于血缘关系进程间通信)
mmap 进程通信
1. mmap 函数声明及头文件包含
man 2 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);
1.1 参数说明
参数:
- addr:指定映射区的首地址。通常传NULL,表示让系统自动分配
- length:共享映射区的大小(<= 文件实际大小)
- prot:共享内存区的读写属性
PROT_READ
PROT_WRITE
PROT_READ | PROT_WRITE- flag:标注共享内存的共享属性
MAP_SHARED(对内存的修改会反映到磁盘上)
MAP_PRIVATE(对内存的修改不会反映到磁盘上)- fd:用于创建共享内存映射区的那个文件的 文件描述符
- offset:从文件偏移位置开始建立映射区 (必须是4k的整数倍4096)默认0,表示映射文件全部
mmap返回值:
- 成功—映射区的首地址
- 失败—MAP_FAILED( 实质(void*)-1 ),设置errno
!!!!!mmap 极易容易出错,切记一定要检查mmap的返回值!!!!!!
munmap返回值:
- 成功— 0
- 失败— -1,设置errno
1.2 mmap 通信demo
- open 创建一个文件,并用 lseek 或者 ftruncate 拓展文件大小
- mmap创建映射区
- 往映射区写
- 读映射区
- 释放映射区
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/mman.h>
#include<fcntl.h>void sys_error(const char * str)
{perror(str);exit(1);
}int main(int argc, char * argv [])
{int fd, ret;char* p = NULL;fd = open("testmap", O_RDWR | O_CREAT | O_TRUNC, 0644);if (fd < 0){sys_error("open error\n");}/*lseek(fd, 10 , SEEK_END); // 与 ftruncate 等效write(fd, "\0", 1);*/ftruncate(fd, 10); // 需要写权限才能拓展文件大小int len = lseek(fd, 0, SEEK_END);p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (p == MAP_FAILED) // 返回值一定要检查{sys_error("mmap error\n");}strcpy(p, "hello mmap");printf("------%s\n, p");ret = munmap(p, len);if (ret == -1)sys_error("munmap error\n");close(fd);return 0;}
有一个创建的 testmap 文件!并实现内存映射
2. mmap 注意事项:
- 用于创建映射区的文件大小为0,实际指定非0大小创建映射区,出“总线错误”
- 用于创建映射区的文件大小为0,实际制定0大小创建映射区,出“无效参数”
- 用于创建映射区的文件读写属性为 只读。映射区属性为 读|写, 出“无效参数”
- 创建映射区,需要read权限。(创建映射区的过程中,隐含着一次对映射文件的读操作)当访问权限指定为 MAP_SHARED 时mmap的读写权限,应该 <= 文件open权限。 只写不行
- 文件描述符fd,在mmap创建映射区完成即可关闭。后续访问文件,用 地址 访问
- offset必须是4096的整数倍。(MMU 映射的最小单位是 4k)
- 对申请的映射区内存,不能越界访问
- munmap 用于释放的地址,必须是mmap申请返回的地址,对申请的映射区地址做操作赋值之前应该备份一份地址用作munmap
- 映射区访问权限为“私有”MAP_PRIVATE,对内存所做的所有修改,只在内存有效,不会反映到物理磁盘上。
- 映射区访问权限为“私有”MAP_PRIVATE,只需要open文件有读权限,就可用于创建映射区。因为私有不反应到物理磁盘上面,没有写的过程
2.1 mmap 函数的保险使用方法:
1. fd = open("文件名", O_RDWR)
2. p = mmap(NULL,有效文件大小, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
3. close(fd)
4. munmap(p, length)
3. demo 父子进程间mmap通信
父子等有血缘关系的进程之间也可以通过 mmap 建立的映射区来完成数据通信。但相应的要在创建映射区的时候指定对应的标志位参数 flags:
- MAP_SHARED(共享映射): 父子进程共享映射区
- MAP_PRIVATE(私有映射):父子进程各自独占映射区(各自读各自的)
- open创建文件,ftruncate 拓展文件大小
- mmap 建立映射区
- close 关闭文件
- fork 创建子进程
- 对映射区进程写读操作(写读操作有先后顺序 靠sleep实现)
- 回收映射区
/* mmap_fork.c */
#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;int fd;pid_t pid;fd = open("temp", O_RDWR | O_CREAT | O_TRUNC, 0644);if (fd < 0){perror("open error\n");exit(1);}ftruncate(fd, 4);p = (int*)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (p == MAP_FAILED){perror("mmap error\n");exit(1);}close(fd);pid = fork();if (pid == 0){*p = 7000; // 对映射区空间进行写操作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\n");exit(1);}}return 0;
}
4. demo 非血缘关系进程间mmap通信
memcpy 函数:
memcpy --copy memory area------------
#include<string.h>void* memcpy(void* dest, void* src, size_t n);
---------------
写进程
- open创建文件,ftruncate 拓展文件大小
- mmap 创建映射区
- close关闭文件
- 往映射区内进行写操作
- 回收映射区
/* mmap_w.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/mman.h>void sys_error(const char * str)
{perror(str);exit(1);
}struct student{int id;char name[256];int age;
};int main(int argc, char * argv [ ])
{struct student stu = {0, "xiaoming", 18};struct student* p;int fd;fd = open("mmap_test", O_RDWR | O_CREAT |O_TRUNC, 0664);if (fd == -1)sys_error("open error\n");ftruncate(fd, sizeof(stu));p = (struct student*)mmap(NULL, sizeof(stu), PROT_READ |PROT_WRITE, MAP_SHARED, fd, 0);if (p == MAP_FAILED)sys_error("mmap error\n");close(fd);while(1){memcpy(p, &stu, sizeof(stu));stu.id++;sleep(1);}int ret = munmap(p, sizeof(stu));if (ret == -1){sys_error("munmap error\n");}return 0;}
读进程
- open打开写进程创建的文件
- mmap 建立映射区
- close关文件
- 读映射区内容
- 回收映射区
/* mmap_r.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/mman.h>void sys_error(const char * str)
{perror(str);exit(1);
}struct student{int id;char name[256];int age;
};int main(int argc, char * argv [])
{struct student stu;struct student* p;int fd;fd = open("mmap_test", O_RDONLY);if (fd == -1)sys_error("open error\n");p = (struct student*)mmap(NULL, sizeof(stu), PROT_READ , MAP_SHARED, fd, 0);if (p == MAP_FAILED)sys_error("mmap error\n");close(fd);while(1){printf("id = %d, name = %s, age = %d\n",p->id,p->name, p->age);sleep(1);}int ret = munmap(p, sizeof(stu));if (ret == -1){sys_error("munmap error\n");}}
编译、运行:
5. mmap通信与fifo和文件通信的差异
mmap 同 fifo 区别:
- mmap:数据可以反复读取
- fifo:数据只能一次读取
mmap 同 文件 区别:
- mmap:映射到内存上,可以直接对内容进行指针操作
- 文件:open、close 打开关闭文件,read、write 来实现文件通信
6. 匿名映射(只能用于血缘关系进程间通信)
通常为了创建映射区要 open 一个 temp 文件,创建好了在 close 掉,比较麻烦。在父子进程通信中,可以直接使用匿名映射来代替。Linux 借助 flags 位 MAP_ANONYMOUS 来实现。
p = (int*)mmap(NUll, 4, PROT_READ |PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1,0)
PS:因为没有指定文件名,所以只限于有血缘关系进程间通讯
类似方法还有 /dev/zero
int fd = open("/dev/zero", O_RDWR);