共享内存是一种最为高效的进程间通信方式,因为进程可以直接读写内存,不需要任何数据的复制,为了在多个进程间交换信息,内核专门留出了一块内存区,这块内存进程可以直接将其映射到进程的私有空间中,可以直接读写,所以大大提高了效率,由于多个进程共享一个内存区,所以需要依靠某种同步机制,例如信号量,互斥锁等。
共享内存原理示意图:
实现共享内存有三个步骤:
- 创建共享内存,使用shmget(),也就是从内存中获取一段共享内存区域
- 映射共享内存,把创建的共享内存映射到具体的进程空间中,使用函数shmat(),现在就可以使用共享内存了,可以使用不带缓冲的的I/O读写命令对其进行操作
- 撤销映射的操作,使用shmdt()函数即可
shmget()的函数语法格式:
shmat()函数语法如下:
shmdt语法如下:
这里需要补充一个删除共享内存的函数shmctl()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 2048
int main()
{
pid_t pid ;
int shmid ;
char *shm_addr ;
char flag[] = "WROTE" ;
char *buff ;
//创建共享内存
if ((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
{
perror("shmget") ;
exit(1) ;
}
else
{
printf("create shared-memory:%d\n", shmid) ;
}
system("ipcs -m") ;//通过ipcs -m获取共享内存的信息
pid = fork() ;
if (pid == -1)
{
perror("fork") ;
exit(1) ;
}
else if (pid == 0)
{
//shm_addr是传回来的映射地址,判断其是否等于(void *)-1???
if ((shm_addr = shmat(shmid, 0, 0)) == (void *)-1)
{
perror("child:shmat") ;
exit (1) ;
}
else
{
printf("child:attach shared memeory:%p\n", shm_addr) ;
}
system("ipcs -m") ;//子进程映射之后查看共享内存的状态
/*通过查看共享内存是否含有flag,如果有表示父进程已经向共享内存中
写入了数据,以此做了一个简单的同步的工作*/
while(strncmp(shm_addr, flag, strlen(flag)))
{
printf("child:wait for enable data...\n") ;
sleep(5) ;
}
strcpy(buff, shm_addr + strlen(flag)) ;
printf("child:shared-memory:%s\n", buff) ;
if ((shmdt(shm_addr)) < 0)//撤销映射
{
perror("shmdt") ;
exit(1) ;
}
else
{
printf("child:deattach shared-memory\n") ;
}
system("ipcs -m") ;//撤销映射之后共享内存的状态
if (shmctl(shmid, IPC_RMID, NULL) == -1)//删除共享内存
{
perror("shmctl") ;
exit(1) ;
}
else
{
printf("delete shared-memory\n") ;
}
system("ipcs -m") ;//删除共享内存之后,查看共享内存的状态
}
else
{
if ((shm_addr = shmat(shmid, 0, 0)) == (void *) - 1)//父进程映射内存
{
perror("shmat") ;
exit(1) ;
}
else
{
printf("parent: attach shared-memory:%p\n", shm_addr) ;
}
sleep(1) ;
printf("\ninput some string\n") ;
fgets(buff, BUFFER_SIZE, stdin) ;//从标准输入中提取BUFFER_SIZE个字符
strncpy(shm_addr + strlen(flag), buff, strlen(buff)) ;//向共享内存中写入
strncpy(shm_addr, flag, strlen(flag)) ;
if (shmdt(shm_addr) < 0)//解除映射
{
perror("shmdt") ;
exit(1) ;
}
else
{
printf("deatch shared-memory\n") ;
}
system("ipcs -m") ;
waitpid(pid, NULL, 0) ;//等待子进程结束
printf("finished\n") ;
}
exit(0) ;
}
创建共享内存时我们指定键值为IPC_PRIVATE,用于创建当前进程的私有共享内存,这个例子中创建的共享内存是父子进程之间的共用部分,所以指定了IPC_PRIVATE
可以看一下运行结果:
在创建共享内存时:
父子进程分别作了映射:
此时看共享内存的状态:
然后我们向共享内存输入一个字符串"zhang",子进程将会读出这个字符串,然后父子进程分别解除映射
最后子进程删除共享内存,此时将查看不到共享内存.
因为共享内存是这里使用一个标志字符串作为同步的标志
这里有个问题弄不懂:
共享内存需要互斥访问,父子进程是怎么同步的?
子进程: while(strncmp(shm_addr, flag, strlen(flag)))
{
printf("child:wait for enable data...\n") ;
sleep(5) ;
}
父进程:
printf("\ninput some string\n") ;
fgets(buff, BUFFER_SIZE, stdin) ;//从标准输入中提取BUFFER_SIZE个字符
strncpy(shm_addr + strlen(flag), buff, strlen(buff)) ;//向共享内存中写入
strncpy(shm_addr, flag, strlen(flag)) ;
父子进程都对共享内存进行了操作,那怎么互斥访问呢?