进程通信:进程与进程间的数据交换,称为进程通信。进程通讯的方式有:共享内存、信号量、管道、消息队列、socket等等。
共享内存:内核管理一片物理内存,允许不同的进程同时映射,多个进程可以映射同一块内存,被多个进程同时映射的物理内存,即共享内存。映射物理内存叫挂接,用完以后解除映射叫脱接。
优点:共享内存是进程通讯最快最有效的。
缺点:不存在同步机制,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,需与其他机制合作来实现同步。
共享内存有两种方式:mmap、shm。
mmap:mmap方式是将文件与进程地址空间进行映射,对实际物理内存影响小。mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read,write等操作。即在磁盘上建立一个文件,每个进程地址空间中开辟出一块空间进行映射。
shm:shm方式是将每个进程的共享内存与实际物理存储器进行映射,对实际物理内存影响大。即每个进程最终会映射到同一块物理内存。shm保存在物理内存,这样读写的速度要比磁盘要快,但是存储量不是特别大。
函数:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize);
参数start:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。
参数length:代表将文件中多大的部分映射到内存。
参数prot:映射区域的保护方式。可以为以下几种方式的组合:
PROT_EXEC 映射区域可被执行
PROT_READ 映射区域可被读取
PROT_WRITE 映射区域可被写入
PROT_NONE 映射区域不能存取
参数flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。
MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享, 原来的文件会改变。
MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任 何修改都不会写回原来的文件内容。当共享的对象的虚拟存储区域为私有对象时, 修改只会被本进程中改变。
MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。
参数fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系 统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。
参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。
返回值:若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。
mmap父子进程的实现例子如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/mman.h>
struct studen{
int age;
char name[32];
};
const char *names[]={"zhangsan","lisi","wangwu"};
int main(int argc, char *argv[])
{
int i = 0;
struct studen *studen_t;
studen_t = (struct studen*)mmap(NULL,sizeof(struct studen)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
if(fork() == 0) //创建子进程,等于0是子进程,子进程读
{
sleep(1);
for(i=0;i<3;i++)
{
printf("number:%d name: %s age: %d\n",i+1,studen_t[i].name,(*(studen_t+i)).age);
}
(*studen_t).age = 2019; //改变zhangsan的年纪为2019
munmap(studen_t,sizeof(struct studen)*10); //解除映射
return 0;
} else {
//父进程写
for(i=0;i<3;i++)
{
memcpy((*(studen_t+i)).name,names[i],strlen(names[i]));
(*(studen_t+i)).age = 18+i;
}
sleep(3);
printf("name: %s age: %d\n",(*studen_t).name,(*studen_t).age);
munmap(studen_t,sizeof(struct studen)*10); //解除映射
printf("umap success\n");
return 0;
}
}
实现结果如下:
mmap以文件进行通信的例子如下:
//----------------------------------write---------------------------------
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
struct studen{
int age;
char name[32];
};
const char *names[]={"zhangsan","lisi","wangwu"};
int main(int argc, char *argv[])
{
int i = 0;
int fd = 0;
struct studen *studen_t;
fd=open("./mmap.txt",O_CREAT|O_RDWR|O_TRUNC,00777);
lseek(fd,sizeof(struct studen)*3-1,SEEK_SET);
write(fd,"",1);
studen_t = (struct studen*)mmap(NULL,sizeof(struct studen)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
close(fd);
for(i=0;i<3;i++)
{
memcpy((*(studen_t+i)).name,names[i],strlen(names[i]));
(*(studen_t+i)).age = 18+i;
}
sleep(3);
//printf("name: %s age: %d\n",(*studen_t).name,(*studen_t).age);
munmap(studen_t,sizeof(struct studen)*10); //解除映射
printf("umap success\n");
return 0;
}
//----------------------------------read---------------------------------
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
struct studen{
int age;
char name[32];
};
int main(int argc, char *argv[])
{
int i = 0;
int fd;
struct studen *studen_t;
fd=open( "./mmap.txt",O_CREAT|O_RDWR,00777 );
studen_t = (struct studen*)mmap(NULL,sizeof(struct studen)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
close(fd);
sleep(1);
for(i=0;i<3;i++)
{
printf("number:%d name: %s age: %d\n",i+1,studen_t[i].name,(*(studen_t+i)).age);
}
munmap(studen_t,sizeof(struct studen)*10); //解除映射
return 0;
}
运行的结果如下:
mmap_test1.c写入mmap.txt,mmap_test2.c再从mmap.txt读出。
总结:一般来说mmap方式是将文件与进程地址空间进行映射,对实际物理内存影响小,操作简单,重启会保存操作系统同步的映像和本身的文件,shm方式是将每个进程的共享内存与实际物理存储器进行映射,对实际物理内存影响大,所以一般用mmap会比较好。