共享内存
共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。
因为所有进程共享同一块内存,共享内存在各种进程间通信方式中具有最高的效率。访问共享内存区域和访问进程独有的内存区域一样快,并不需要通过系统调用或者其它需要切入内核的过程来完成。同时它也避免了对数据的各种不必要的复制。
因为系统内核没有对访问共享内存进行同步,您必须提供自己的同步措施。例如,在数据被写入之前不允许进程从共享内存中读取信息、不允许两个进程同时向同一个共享内存地址写入数据等。解决这些问题的常用方法是通过使用信号量进行同步。
要使用一块共享内存,进程必须首先分配它。随后需要访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中。当完成通信之后,所有进程都将脱离共享内存,并且必须由一个进程释放该共享内存块。
在 Linux 系统中,每个进程的虚拟内存是被分为许多页面的。这些内存页面中包含了实际的数据。每个进程都会维护一个从内存地址到虚拟内存页面之间的映射关系。尽管每个进程都有自己的内存地址,不同的进程可以同时将同一个内存页面映射到自己的地址空间中,从而达到共享内存的目的。
**所有共享内存块的大小都必须是系统页面大小的整数倍。**系统页面大小指的是系统中单个内存页面包含的字节数。在 Linux 系统中,内存页面大小是4KB,不过您仍然应该通过调用 getpagesize 获取这个值。
使用共享内存:
- 创建/获得 一个共享内存 —— shmget
- 连接(映射) 共享内存到当前进程的地址空间 —— shmat
- 断开与共享内存的连接 —— shmdt
- 控制共享内存的相关信息 —— shmctl
头文件:
#include <sys/ipc.h>
#include <sys/shm.h>
创建/获得/分配 共享地址
int shmget(key_t key, size_t size, int shmflg);
返回值 : 成功则返回共享内存 ID,
失败则返回 -1
key_t key : 一个用来标识共享内存块的键值。彼此无关的进程可以通过指定同一个键以获取对同一个共享内存块的访问。
然而,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突。
用特殊常量 IPC_PRIVATE 作为键值可以保证系统建立一个全新的共享内存块。
size_t size : 指定了所申请的内存块的大小。因为这些内存块是以页面为单位进行分配的,
实际分配的内存块大小将被扩大到页面大小的整数倍。
int shmflg : 一组标志,通过特定常量的按位或操作来shmget
特定常量有: IPC_CREAT:这个标志表示应创建一个新的共享内存块。
通过指定这个标志,我们可以创建一个具有指定键值的新共享内存块。
IPC_EXCL:这个标志只能与 IPC_CREAT 同时使用。当指定这个标志的时候,如果已有一个具有这个键值的共享内存块存在,则shmget会调用失败。
也就是说,这个标志将使线程获得一个“独有”的共享内存块。如果没有指定这个标志而系统中存在一个具有相同键值的共享内存块,shmget会返回这个已经建立的共享内存块,而不是重新创建一个。
陈哥直接把这里写成 shmget(key,1024*4,0); 这样直接就是去寻找 这个键值(key)的共享内存,牛皮啊
模式标志 :由9个位组成,分别表示属主、属组和其它用户对该内存块的访问权限。
<sys/stat.h>中指定,并且在手册页第二节stat条目中说明了的常量指定。
连接(映射) 共享内存
断开共享内存的连接
控制共享内存
开始编程
./write
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
// int shmget(key_t key, size_t size, int shmflg);
int main(){
key_t key = ftok(".",2);
// 创建共享内存
int shmid = shmget(key,1024*4,IPC_CREAT|0666); // 陈哥说,共享内存的大小,必须以 兆 对齐
if (shmid == -1){
printf("shmget is failer \n");
exit(-1);
}
// 当前进程连接至共享内存
char* shmaddr = shmat(shmid,NULL,0);
if(shmaddr == (void *)-1 ){
printf("共享内存连接失败 \n");
}
printf("已成功连接共享内存 \n");
strcpy(shmaddr,"王天小"); // 我曹,还可以这样用 strcpy 牛皮
sleep(5);
int shmdt_n = shmdt(shmaddr);
if(shmdt_n == -1){
perror("sdmdt is fail, because : ");
}
shmctl(shmid,IPC_RMID,0);
printf("quit\n");
return 0;
}
./read
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
// int shmget(key_t key, size_t size, int shmflg);
int main(){
key_t key = ftok(".",2);
// 创建共享内存
int shmid = shmget(key,1024*4,0); // 陈哥说,共>享内存的大小,必须以 兆 对齐
// 只获取不创建,陈哥直接写的 0 我当场痴呆
if (shmid == -1){
printf("shmget is failer \n");
exit(-1);
}
// 当前进程连接至共享内存
char* shmaddr = shmat(shmid,NULL,0);
if(shmaddr == NULL ){
printf("共享内存连接失败 \n");
}
printf("已成功连接共享内存 \n");
printf("data : %s\n",shmaddr);
int shmdt_n = shmdt(shmaddr);
if(shmdt_n == -1){
perror("sdmdt is fail, because : ");
}
printf("quit\n");
return 0;
}
运行结果:
最后,简述 ipcs 命令
ipcs -m 打印出使用共享内存进行进程间通信的信息
更改之前的代码,在创建共享内存后,不删除
然后输入 ipcrm -m 34 可移除所创立的共享内存