共享内存(Shared Memory)是一种用于在进程之间共享数据的机制。多个进程可以将同一块内存映射到它们的地址空间中,从而实现对该内存区域的并发访问。一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间上的共享区的一块地址空间形成映射关系。
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
在使用共享内存之前,需要进行以下步骤:
-
创建共享内存:调用
shmget()
函数创建一个共享内存段,并指定大小和权限等参数。它返回一个标识符(shmid)用于标识共享内存段。 -
连接共享内存:调用
shmat()
函数将共享内存段连接到进程的地址空间中。它接受 shmid 和一些附加参数,并返回指向共享内存段的指针。 -
访问共享内存:通过操作指针来读取和写入共享内存中的数据,就像访问普通的内存一样。
-
分离共享内存:当不再需要使用共享内存时,调用
shmdt()
函数将共享内存从当前进程中分离。 -
删除共享内存:在最后一个进程使用完共享内存后,可以调用
shmctl()
函数删除共享内存段。(如果不删除的话将一直存在)
在使用共享内存的过程中,涉及到以下几个系统调用函数:
- int shmget(key_t key, size_t size, int shmflg)
- 功能:创建一个新的共享内存段或获取已存在的共享内存段标识符。
- 参数:
- key:共享内存段的键值,通常使用 ftok() 函数生成。
- size:共享内存段的大小,以字节为单位。
- shmflg:标志参数,指定创建共享内存的权限和行为选项。
- IPC_CREAT:若共享内存不存在,则创建一个新的共享内存段。
- IPC_EXCL:与 IPC_CREAT 一起使用,用于确保只创建新的共享内存段,而不是获取已存在的。
- mode:指定共享内存的访问权限,一般以八进制形式表示(例如 0644 表示读写权限为用户、组和其他人)。
- 返回值:成功时返回共享内存段的标识符(shmid),失败时返回 -1。
- void *shmat(int shmid, const void *shmaddr, int shmflg)
- 功能:将共享内存段连接到当前进程的地址空间。
- 参数:
- shmid:共享内存段的标识符。由
shmget()
返回。 - shmaddr:指定共享内存连接的地址,通常设为 NULL,让系统自动选择合适的地址。
- shmflg:标志参数,指定共享内存的访问权限和行为选项。
- shmid:共享内存段的标识符。由
(SHM_RDONLY
:以只读方式连接到共享内存段,或者(SHM_WRONLY,0
为默认
)
-
- 返回值:成功时返回指向共享内存段的指针,失败时返回 -1。
- int shmdt(const void *shmaddr)
- 功能:将共享内存段从当前进程中分离。
- 参数:
- shmaddr:指向共享内存段的指针。
- 返回值:成功时返回 0,失败时返回 -1。
- int shmctl(int shmid, int cmd, struct shmid_ds *buf)
- 功能:对共享内存段进行控制操作,如删除共享内存段。
- 参数:(可以组合使用)
- shmid:共享内存段的标)识符。
- cmd:控制命令,用于指定需要执行的操作。
- IPC_STAT:获取共享内存段的状态信息,将结果存储在 buf 参数指向的 struct shmid_ds 结构体中。
- IPC_SET:设置共享内存段的状态信息,通过将 buf 参数指向的 struct shmid_ds 结构体中的字段进行修改。
- IPC_RMID:删除共享内存段。
- buf:结构体指针,用于传递或接收额外的参数信息。
- 返回值:成功时返回 0,失败时返回 -1。
以上是一些常用的共享内存相关函数。在实际使用中,可以根据具体需求调用这些函数进行共享内存的创建、连接、访问和管理操作。
下面是实现两个进程通过共享内存的方式实现通信:
#include <sys/ipc.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
// 生成 key 值,用于唯一标识共享内存段
key_t key;
key = ftok(".", 1);
if (key == -1) {
perror("ftok error");
exit(-1);
}
printf("key=%d\n", key);
// 创建共享内存段
int shmid;
shmid = shmget(key, 1024 * 4, IPC_CREAT | IPC_EXCL | 0666);
if (shmid == -1) {
perror("shmget error");
exit(-1);
}
printf("shid=%d\n", shmid);
// 连接到共享内存段
char *shmadd;
shmadd = shmat(shmid, NULL, 0);
strcpy(shmadd,"liuhao handsome");
sleep(5);
// 解除共享内存连接
int rev_shmdt = shmdt(shmadd);
if (rev_shmdt == -1) {
perror("shmdt error");
}
// 删除共享内存段
int rev_shmctl = shmctl(shmid, IPC_RMID, NULL);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
// 生成 key 值,用于唯一标识共享内存段
key_t key;
key = ftok(".", 1);
if (key == -1) {
perror("ftok error");
exit(-1);
}
printf("key=%d\n", key);
// 获取共享内存段的标识符
int shmid;
shmid = shmget(key, 1024 * 4, 0);
if (shmid == -1) {
perror("shmget error");
exit(-1);
}
printf("shid=%d\n", shmid);
// 连接到共享内存段
char *shmadd;
shmadd = shmat(shmid, NULL, 0);
printf("data=%s\n", shmadd);
// 解除共享内存连接
int rev_shmdt = shmdt(shmadd);
if (rev_shmdt == -1) {
perror("shmdt error");
}
return 0;
}
结果:
20230820