进程间通信(IPC - InterProcess Communication)
通信的方式有很多: 文件, 管道, 信号, 共享内存, 消息队列, 套接字, 命名管道等等;
- 但是由于设计缺陷现在常用的有:
1.管道(简单, 默认匿名管道, 还有有名管道)
2.信号(开销小, 但是如果不是做系统编程一般不用, 因为缺点大于有点, 比如有没有收到无法确认)
3.共享映射区(无血缘关系之间)
4.本地套接字(最稳定)
三、 共享映射区
- 简述: 将本地磁盘上的文件映射(使用函数: void **mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);)到虚拟内存的加载动态库的区域, 这样便不用再去对文件进行操作, 直接对其内存上的地址进行操作就行了.
- 通信: 父子进程共享内存映射区, 那么创建内存映射区之后再fork子进程即可直接进行操作; 无血缘关系的, 他们可以同时映射同一个本地文件, 在对其进行读写操作, 但是该文件里的数据是不需要的.(重点)
- API:
# include <sys/mman.h>
void * mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
参数:
void *addr: 内存中空间的地址, 以便映射, 但是我们是不知道的, 内核会处理, 一般传null
size_t len: 映射的长度, 一般是和文件的大小对应, 但是你需要映射多大就写多少
int prot: 映射区权限, 对应的是一些宏, 在man文档中查看, 我们一般都要用到读写权限: PORT_READ PORT_WRITE, 设置时必须有读权限
int flags: 对应的宏, 一般如果通信使用一般设置成MAP_SHARED
int fd: 映射的文件描述符
off_t offset: 映射文件的偏移量, 对应磁盘文件的偏移量, 设置时必须是4K的整数倍, 不需要指定时传0即可
返回值:
调用成功返回映射区的首地址, 失败返回MAP_FAILED即(void *)(-1)
- 映射区映射本地文件代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
int main(int argc, const char* argv[])
{
int fd = open("test.txt", O_RDWR); //打开本地磁盘中的文件, 获取fd
if(fd == -1) {
perror("open error");
exit(1);
}
void* ptr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(ptr == MAP_FAILED) {
perror("mmap error");
exit(1);
}
close(fd);
printf("buf = %s\n", (char*)ptr);//读取数据
// 修改映射区数据
strcpy(ptr, "99999999999999");
// 释放内存映射区
int ret = munmap(ptr, 1024);
if(ret == -1) {
perror("munmap error");
exit(1);
}
return 0;
}
- 注意:
1 如果对mmap的返回值(ptr)做++操作(ptr++), 那么释放时调用munmap会失败
2 如果open时O_RDONLY, mmap时prot参数指定
PROT_READ | PROT_WRITE会怎样?--会提示权限不够, 因为文件打开的时候是reonly, 但是映射区对应的待会还要写操作, 但是文件没有该权限
3 文件偏移量必须是4k的整数倍, 不然mmap出错
4 调用mmap时要注意权限问题, 偏移量问题, 长度问题(长度必须大于0, 不然没意义, 并且要求本地文件大小也是大于0的, 文件长度>=映射区长度)
5 open的时候O_CREAT一个新文件也可以来创建映射区但是前提是创建成功之后使用truncate(truncate)做文件拓展, fseek也可以拓展, 但是需要进行一次写操作
6 映射区创建成功之后, 文件就可以关闭了
7 对ptr越界操作会出现无效操作或段错误, 但是越界之后不会对文件有影响的