1.什么是共享内存:共享内存允许两个或多个进程访问给定的同一块存储区域。已知当一个进程被启动时,系统会为其创建一个 0~4G 的虚拟内存空间, 根据虚拟地址与物理地址之间的映射关系,进程可以通过操作虚拟地址,实现对物理页面的操作。
一般情况下,每个进程的虚拟地址空间会与不同的物理地址进行映射,但是当使用共享内存进行通信时,系统会将同一段物理内存映射给不同的进程。
两个进程的虚拟地址空间与共享内存之间的映射关系如下图
系统中的物理内存和虚拟内存都通过页面来管理,为多个进程分配共享内存,实际是为进程分配一个或多个物理页面,因此,共享内存的大小必须是系统中页面大小的整数倍
2.共享内存建立的过程
<1>首先将虚拟内存空间与共享
内存进行映射,
<2>映射完成后,进程对虚拟地址的读写,就相当于直接对物理内存
的读写。
<3>另外,与申请堆空间类似,当通信完成之后,也应释放物理内存,解除进程与共享内存的映射关系。
3.共享内存有关的函数
1.int shmget(key_t key, size_t size, int shmflg);
//功能是创建一块新的共享内存,或打开一块已经存在的共享内存
//第一个参数key,代表共享内存的键值
//第二个参数size用于设置共享内存的大小
//第三个参数 shmflg 用于设置 shmget()函数的创建条件(一般设置为 IPC_CREAT 或 IPC_EXCL)及进程对共享内存的读写权限。
2.void *shmat(int shmid, const void *shmaddr, int shmflg);
//功能是进行地址映射,将共享内存映射到进程虚拟地址空间中
//第一个参数 shmid 为共享内存标识符,该标识符一般由shmget()函数返回;
//参数 shmaddr 为一个指针类型的传入参数,用于指定共享内存映射到虚拟内存时的虚拟地址,当设置为 NULL 时,映射地址由系统决定;
//参数 shmflg 用于设置共享内存的使用方式,若 shmflg 设置为 SHM_RDONLY,则共享内存将以只读的方式进行映射,当前进程只能从共享内存中读取数据。
3.int shmdt(const void *shmaddr);
//功能是解除物理内存与进程虚拟地址空间的映射关系
4.int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//功能是对已存在的共享内存进行操作,具体的操作由参数决定
//参数 shmid 表示共享内存标识符;
//参数 cmd 表示要执行的操作,常用的设置为IPC_RMID,功能为删除共享内存;
//参数 buf 用于对共享内存的管理信息进行设置,该参数是一个结构体指针,
5.struct shmid_ds *buf结构体如下
struct shmid_ds {
struct ipc_perm shm_perm; //所有者和权限标识
size_t shm_segsz; //共享内存大小
time_t shm_atime; //最后映射时间
time_t shm_dtime; //最后解除映射时间
time_t shm_ctime; //最后修改时间
pid_t shm_cpid; //创建共享内存进程的 id
pid_t shm_lpid; //最近操作共享内存进程的 id
shmatt_t shm_nattch; //与共享内存发生映射的进程数量
...
};
共享内存代码
//com.h
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/wait.h>
#include<unistd.h>
#define PATH "."
#define SIZE 256
int GetShm();
char* At_Shm(int shm_id);
int Delete_Shm(char* addr);
int Rm_Shm(int shm_id);
int GetShm(){
key_t key=ftok(PATH,0);
if(key==-1){
perror("ftok");
exit(1);
}
int flag=IPC_CREAT|0666;
int shm_id=shmget(key,SIZE,flag);
if(shm_id==-1){
perror("shmget");
exit(1);
}
return shm_id;
}
char* At_Shm(int shm_id){
return (char*)shmat(shm_id,NULL,0);
}
int Delete_Shm(char* addr){
return shmdt(addr);
}
int Rm_Shm(int shm_id){
return shmctl(shm_id,IPC_RMID,NULL);
}
//myshm.c
#include<stdio.h>
#include<string.h>
#include"com.h"
int main()
{
int shm_id=GetShm();
pid_t pid=fork();
if(pid<0){
perror("fork");
exit(1);
}
else if(pid==0){
//child
char* buf=At_Shm(shm_id);
int i=0;
while(i<60){
buf[i]='A'+i;
i++;
}
buf[59]='\0';
Delete_Shm(buf); //删除映射
}
else{
//father
char* buf=At_Shm(shm_id);
sleep(2);
printf("%s\n",buf);
waitpid(pid,NULL,0);
Rm_Shm(shm_id);
}
return 0;
}
运行结果
3.共享内存的特点
(1)共享内存是进程间通信最快的方式。
(2)共享内存没有保护机制,需要信号量控制。
(3)共享内存的基本单位是页,即大小最小是4K,且是向上取整数页的。
(4)共享内存的生命周期是随内核的。
4.systemV IPC通信特点
优点
1. 信息共享:Web服务器,通过网页浏览器使用进程间通信来共享web文件(网页等)和多媒体;
2. 加速:维基百科使用通过进程间通信进行交流的多服务器来满足用户的请求;
3. 模块化;
4. 私有权分离;
缺点
1.采用了某种形式的内核开销,降低了性能;
2.几乎大部分IPC都不是程序设计的自然扩展,往往会大大地增加程序的复杂度。