我们知道进程间同行的实质是让两个进程看到同一份资源。共享内存正是让两个进程看到同一份资源的方法之一。那么两个进程怎么共享一个内存呢?
我们都知道,每个进程都有自己的进程空间,进程的虚拟地址空间通过页表映射到物理内存上,而不同的进程的虚拟地址空间是相互独立的,也就不可能出现两个进程的虚拟地址空间橡胶的情况。但是,不同的进程虚拟地址空间都要映射到物理内存中,因此如果两个进程空间映射到同一块物理内存,那么这两个进程将看到相同的内容,而这块物理内存就是两个进程的临界资源了。
我们可以用这样的模型来看待这个过程:
共享内存实现进程间通信的机制就是这样的。
有关函数:
创建共享内存:
int shmget(key_t key, size_t size, int shmflg);
key:共享内存段名字。
size:共享内存大小。
shmflg:权限标志。
返回值:成功返回共享内存段的标识码,失败返回-1。
将共享内存段连接到进程地址空间:
void shmat(int shmid, const void *shmaddr, int shmflg);
shmid:共享内存标识符。
shmaddr:指定连接的地址。
shmaddr为NULL,核⼼心⾃自动选择⼀一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃自动向下调整为SHMLBA的整数倍。公式:s hmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表⽰示连接操作⽤用来只读共享内存
shmflg:两个可能取值为SHM_RND和SHM_RDONLY。
返回值:成功返回共享内存指针,失败返回-1。
将共享段与当前进程脱离:
int shmdt(const void *shmaddr);
shmaddr由shmat返回的指针。
返回值:成功返回0,失败返回-1。
控制共享内存:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:由shmget返回的共享内存标识码。
cmd:将要采取的动作(有三个选择值,删除用IPC_RMID)。
buf:指向一个保存着共享内存的模式状态和权限的数据结构(一般可赋为NULL)。
可以看到,共享内存实现进程间通信,是两个进程直接与内存的数据交换,不涉及内核,因此少了中间数据进出内核的开销,因此共享内存是速度最快的进程间通信方式,也因此共享内存必须进行显式地释放,它的生命周期是随内核的。而因为共享内存没有同步互斥,所以在使用共享内存时最好加上信号量来解决同步问题,当然这篇文章不涉及信号量问题。
实现进程间简单通信:
Makefile文件:
.PHONY : all
all : server client
server : server.c comm.c
gcc -o server server.c comm.c
client : client.c comm.c
gcc -o client client.c comm.c
.PHONY : clean
clean :
rm -f server client
comm.c文件:
#include "comm.h"
static int _GetCommShm(int size, int flag)
{
key_t key = ftok(PATHNAME, PROJ_ID);
if(key < 0)
{
printf("ftok\n");
return -1;
}
int shmid = shmget(key, size, flag);
if(shmid < 0)
printf("shmget\n");
return shmid;
}
int CreatShm(int size)
{
return _GetCommShm(size, IPC_CREAT|IPC_EXCL|0666);
}
int GetShm(int size)
{
return _GetCommShm(size, IPC_CREAT);
}
int DelShm(int shmid)
{
return shmctl(shmid, IPC_RMID, NULL);
}
server.c文件:
#include "comm.h"
int main()
{
int shmid = CreatShm(4096);
if(shmid < 0)
{
printf("CreatShm\n");
exit(EXIT_FAILURE);
}
char *addr = shmat(shmid, NULL, 0);
int i = 0;
while(i++ < 20)
{
printf("%s%d\n", addr, i);
sleep(1);
}
sleep(2);
shmdt(addr);
DelShm(shmid);
return 0;
}
clean.c文件:
#include "comm.h"
int main()
{
int shmid = GetShm(4096);
if(shmid < 0)
{
printf("GetShm\n");
exit(EXIT_FAILURE);
}
char *addr = shmat(shmid, NULL, 0);
int i = 0;
while(i < 20)
{
printf("%d\n", i);
addr[i] = 'A';
i++;
addr[i] = 0;
sleep(1);
}
shmdt(addr);
return 0;
}
代码功能是client端向共享内存中放入逐渐变长的由A组成的字符串,server端从共享内存中取出并打印到屏幕上,运行结果图: