目前,进程间通信主要集中在管道和共享内存上使用,共享内存是总所周知的直接对内存映射操作,速度最快的通信方式,缺点,可能就是数据同步没有提供同步机制
共享存储映射
存储映射I/O
存储映射I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不使用read和write函数的情况下,使用地址(指针)完成I/O操作。
使用这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过mmap函数来实现。
mmap函数
void *mmap(void *adrr, size_t length, int prot, int flags, int fd, off_t offset);
返回:成功:返回创建的映射区首地址;失败:MAP_FAILED宏
参数:
addr: 建立映射区的首地址,由Linux内核指定。使用时,直接传递NULL
length: 欲创建映射区的大小
prot: 映射区权限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区)
MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。
MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。
fd: 用来建立映射区的文件描述符
offset: 映射文件的偏移(4k的整数倍)
munmap函数
同malloc函数申请内存空间类似的,mmap建立的映射区在使用结束后也应调用类似free的函数来释放。
int munmap(void *addr, size_t length); 成功:0; 失败:-1
借鉴malloc和free函数原型,尝试装自定义函数smalloc,sfree来完成映射区的建立和释放。思考函数接口该如何设计?
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> void *smalloc(size_t size) { void *p; p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0); if (p == MAP_FAILED) { p = NULL; } return p; } void sfree(void *ptr, size_t size) { munmap(ptr, size); } int main(void) { int *p; pid_t pid; p = smalloc(4); pid = fork(); //创建子进程 if (pid == 0) { *p = 2000; printf("child, *p = %d ", *p); } else { sleep(1); printf("parent, *p = %d ", *p); } sfree(p, 4); return 0; }
mmap注意事项
【mmap.c】
#include <stdio.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/mman.h> void sys_err(char *str) { perror(str); exit(1); } int main(void) { char *mem; int len = 0; int fd = open("hello678", O_RDWR|O_CREAT|O_TRUNC, 0644); if (fd < 0) sys_err("open error"); len = lseek(fd, 3, SEEK_SET); //获取文件大小,根据文件大小创建映射区 write(fd, "