Linux下的进程间通信也可以使用mmap的内存共享映射来实现,mmap的作用就是把磁盘文件的一部分直接映射到进程的内存中,那么进程就可以直接对该内存文件进行操作,mmap也设置了两种机制:共享和私有,如果是共享映射,那么在内存中对文件进行修改,磁盘中对应的文件也会被修改,相反,磁盘中的文件有了修改,内存中的文件也被修改。如果是私有映射,那么内存中的文件是独立的,二者进行修改都不会对对方造成影响。通过这样的内存共享映射就相当于是进程直接对磁盘中的文件进行读写操作一样,那么如果有两个进程来mmap同一个文件,就实现了进程间的通信。磁盘中的文件通过mmap函数来实现映射,然后通过munmap函数取消映射。先来看一下函数的原型:
#include
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
其中对于mmap来说,有6个参数,第一个参数是你要映射的起始地址,这里一般用NULL,这样系统会在0地址附近随机分配一块内存。第二个参数是所要映射的文件长度。第三个参数是所映射的文件的权限,其中包含(PROT_EXEC, PROT_READ, PROT_WRITE, PROT_NONE)这四种参数。第四个参数是映射类型,其中包括(MAP_SHARED, MAP_PRIVATE)。第五个参数是文件描述符。第六个参数是偏移量。我们可以看到函数的返回值为void *,那么如果映射成功则会返回映射的首地址,如果出错则返回常数MAP_FAILED。当进程终止时,该进程的映射内存会自动解除,也可以调用munmap解除映射,解除成功返回0,出错返回-1。那么这些参数对应着下图:
对于第三个参数,PROT_EXEC表示映射的这一段可执行,例如映射共享库,PROT_READ表示这一段可读,PROT_WRITE表示这一段可写,PROT_NONE表示这一段不可访问。对于第四个参数,MAP_SHARED表示共享映射,MAP_PRIVATE表示私有映射。最后一个参数偏移量表示要从文件的哪个地方开始映射,应为4096的整数倍。
下面简单实现通过mmap实现的进程间通信,先看以下写操作的代码:
#include
#include
#include
#include
#include
#include
#include
void sys_err(char *s){ // 出错返回函数
perror(s);
exit(1);
}
int main(int argc, char *argv[])
{
if(argc != 2){
printf("Run error\n");
exit(1);
}
int fd = open(argv[1], O_RDWR | O_CREAT, 0777);
char *mm;
int cnt = 1;
if(fd == -1)
sys_err("open");
if(lseek(fd, 0x1000 - 1, SEEK_SET) == -1) // 将文件大小变为4096
sys_err("lseek");
if(write(fd, "\0", 1) == -1) // lseek之后必须要有写操作
sys_err("write");
mm = mmap(NULL, 0x1000, PROT_WRITE, MAP_SHARED, fd, 0);
if(mm == MAP_FAILED)
sys_err("mmap");
close(fd);
while(1){
sprintf(mm, "Hello %d\n", cnt);
printf("Write :Hello %d\n", cnt ++);
sleep(1);
}
munmap(mm, 0x1000);
return 0;
}
下面是读操作的代码:
#include
#include
#include
#include
#include
#include
#include
void sys_err(char *str){
perror(str);
exit(1);
}
int main(int argc, char *argv[])
{
if(argc != 2){
printf("Open error\n");
exit(1);
}
int fd = open(argv[1], O_RDONLY);
if(fd == -1)
sys_err("open");
char *mm = mmap(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
while(1){
printf("Read :%s", mm);
sleep(1);
}
munmap(mm, 0x1000);
return 0;
}
运行结果如下图所示:
由于这只是实现两个进程间的通信,那么对于test文件来说在没有进程运行的时候没有什么作用,所以它可以设置为一个临时文件,所以可以在代码中使用unlink函数,在进程结束以后删除test文件。还有在进行通信时可以将数据封装为一个结构体,通过结构体进行数据的传递。如果在运行中出现Bus error (core dumped)错误,需要考虑共享文件是否有存储空间(也就是说你要mmap一个4096的文件,但实际文件没有4096那么大)。