进程之间的通信方式有管道,信号,信号量,共享内存以及套接字等等。
共享内存是进程间通信效率最高的一种,只需要复制两次内存空间即可
大体意思是,两个需要通信的进程A和B,都有相应的虚拟空间
那么将一块内存地址映射到两个进程的虚拟地址空间中
A进程通过指针访问共享内存空间,将产生一个缺页中断
A进程对内存的任何更改,B进程将会访问到更改后的内容,这样就达到了进程间的通信的目的
共享内存通信方式一般应用到两个进程之间以及父进程与子进程之间通信
这里调用函数mmap()和ummmap函数,mmap函数是一个函数指针
mmap函数的返回值是映射区的起始地址,作用就是把文件映射到内存中,用户对内存的操作可以反映到文件中去
假设进程A的代码
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
char name[4];
int age;
}people;
int main(int argc, char** argv) // map a normal file as shared mem:
{
int fd,i;
people *p_map;
char temp;
fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
lseek(fd,sizeof(people)*5-1,SEEK_SET);
write(fd,"",1);
p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
close(fd);
temp = 'a';
for(i=0; i<10; i++)
{
temp += 1;
memcpy((*(p_map+i)).name,&temp,2);
(*(p_map+i)).age = 20+i;
}
printf("initialize over\n");
sleep(10);
munmap(p_map,sizeof(people)*10);
printf("umap ok \n");
return 0;
}
这里定义了一个people结构体,用来保存写入的数据
文件描述符我们使用open函数打开一个文件来获得
在一个for循环里,往共享的内存空间写入10个people结构体类型的数据
完成后,调用unmmap函数
来看看B进程的代码
#include <sys/mman.h>;
#include <sys/types.h>;
#include <fcntl.h>;
#include <unistd.h>;
typedef struct{
char name[4];
int age;
}people;
main(int argc, char** argv) // map a normal file as shared mem:
{
int fd,i;
people *p_map;
fd=open( argv[1],O_CREAT|O_RDWR,00777 );
p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
for(i = 0;i<10;i++)
{
printf("name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age);
}
munmap(p_map,sizeof(people)*10);
}
同样,获得 共享内存的首地址
那么通过控制台的输出,我们可以看到进程B可以获取进程A向内存段写入的数据
通过匿名映射来实现父子进程之间的通信
父进程和子进程之间的通信,这里我借鉴一下网上的例子
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_SIZE 100
int main(int argc, char** argv)
{
char *p_map;
/* 匿名映射,创建一块内存供父子进程通信 */
p_map = (char *)mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if(fork() == 0) {
sleep(1);
printf("child got a message: %s\n", p_map);
sprintf(p_map, "%s", "hi, dad, this is son");
munmap(p_map, BUF_SIZE); //实际上,进程终止时,会自动解除映射。
exit(0);
}
sprintf(p_map, "%s", "hi, this is father");
sleep(2);
printf("parent got a message: %s\n", p_map);
return 0;
}
子进程先休息一秒,那么父进程开始向p_map赋值hi, this is father
随后父进程开始休眠,那么共享的内存空间里面的p_map变量已经被更改为hi, this is father
子进程执行printf的时候,就会输出hi, this is father
同样的,子进程开始给p_map变量赋值hi, dad, this is son
那么当父进程不再休眠的时候,就会输出hi, dad, this is son
这个例子我觉得很好
一些项目中,涉及到文件读写的内容,一般都用到了内存映射
那么内存映射和read函数之间有什么区别呢,为什么不用read函数直接读写某个文件
当进程需要读写某个文件的时候,内核将文件先读入页缓存中,因为缓存可以加快IO操作的速度
但是页缓存是处于内核,用户进程不能访问,所以还需要将页缓存中的内容复制到内存中,那么这样就需要两次复制
但是,mmap直接将文件映射到进程虚拟内存空间中,从而进程可以使用指针直接访问文件,
当进程首次访问文件的时候,文件并不在内存空间,产生一次缺页异常,那么只需要一次复制,即将文件从磁盘复制到内存中即可