system V共享内存
- [system V共享内存](#system V共享内存)
system V共享内存
概念
System V 共享内存是一种进程间通信(IPC)机制,允许多个进程共享同一块内存区域。与管道不同,共享内存不涉及内核缓冲区,而是直接映射到进程的地址空间,因此速度非常快。
System V 共享内存的定义方式
(1)在代码中创建共享内存
在 C 代码中,可以使用以下系统调用来操作共享内存:
-
shmget():创建或获取共享内存段。
-
shmat():将共享内存段附加到进程的地址空间。
-
shmdt():将共享内存段从进程的地址空间分离。
-
shmctl():控制共享内存段(如删除)。
函数详解
(1)ftok()
生成一个唯一的键值,用于标识共享内存段。
函数模型:
c
key_t ftok(const char *pathname, int proj_id);
参数:
-
pathname:文件路径名(必须存在且可访问)。
-
proj_id:项目 ID(通常为一个字符)。
返回值:
-
成功:返回生成的键值。
-
失败:返回 -1。
(2) shmget()
创建或获取共享内存段。
函数模型:
c
int shmget(key_t key, size_t size, int shmflg);
参数:
-
key:共享内存段的键值(通常由 ftok() 生成)。
-
size:共享内存段的大小(以字节为单位)。
-
shmflg:标志位,通常为权限模式(如 0666)与 IPC_CREAT 的组合。
返回值:
-
成功:返回共享内存段的标识符(shmid)。
-
失败:返回 -1。
(3)shmat()
将共享内存段附加到进程的地址空间。
函数模型:
c
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
-
shmid:共享内存段的标识符。
-
shmaddr:指定附加地址(通常为 NULL,由系统自动选择)。
-
shmflg:标志位(通常为 0)。
返回值:
-
成功:返回共享内存段的起始地址。
-
失败:返回 (void*) -1。
(4)shmdt()
将共享内存段从进程的地址空间分离。
函数模型:
c
int shmdt(const void *shmaddr);
参数:
- shmaddr:共享内存段的起始地址(由 shmat() 返回)。
返回值:
-
成功:返回 0。
-
失败:返回 -1。
(5)shmctl()
控制共享内存段(如删除)。
函数模型:
c
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
-
shmid:共享内存段的标识符。
-
cmd:控制命令(如 IPC_RMID 用于删除)。
-
buf:指向 struct shmid_ds 的指针(通常为 NULL)。
返回值:
-
成功:返回 0。
-
失败:返回 -1。
System V 共享内存的本质
(1)内核中的共享内存段
共享内存段是内核中的一块内存区域,由内核分配和管理。
每个共享内存段有一个唯一的标识符(shmid)。
(2)进程地址空间映射
进程通过 shmat() 将共享内存段映射到自己的地址空间。
映射后,进程可以直接访问共享内存中的数据。
(3)同步机制
共享内存本身不提供同步机制,需要结合信号量或互斥锁来避免竞争条件。
System V 共享内存与匿名管道的不同
特性 | System V 共享内存 | 管道(Pipe) |
---|---|---|
通信方式 | 直接共享内存区域 | 通过内核缓冲区传递数据 |
速度 | 非常快,无需内核介入 | 较慢,涉及内核缓冲区拷贝 |
同步机制 | 需要额外同步机制(如信号量) | 无需额外同步机制 |
进程关系 | 可用于无亲缘关系的进程 | 仅用于具有亲缘关系的进程 |
生命周期 | 持久存在,直到显式删除 | 随进程结束而销毁 |
使用场景 | 大数据量、高性能要求的通信 | 小数据量、简单通信 |
通信方向 | 双向 | 单向 |
缓冲区大小 | 由用户指定 | 通常为 64KB |
内核实现 | 通过 struct shmid_kernel 管理 |
通过 pipe_inode_info 管理 |
适用性 | 适用于无亲缘关系的进程 | 适用于有亲缘关系的进程 |
删除方式 | 使用 shmctl() 显式删除 |
随进程结束自动销毁 |
System V 共享内存的原理与底层解读
(1)内核数据结构
共享内存段在内核中通过 struct shmid_kernel 管理。
每个共享内存段包含以下信息:
-
唯一标识符(shmid)。
-
内存大小。
-
附加的进程数。
-
权限信息。
(2)内存映射
进程通过 shmat() 将共享内存段映射到自己的地址空间。
映射后,进程可以直接读写共享内存。
(3)同步机制
共享内存本身不提供同步机制,需要结合信号量或互斥锁来避免竞争条件。
分步操作案例
以下是一个完整的案例,分为 创建、挂载 和 销毁 三个部分。
(1)创建共享内存段
c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
// 生成唯一键值
key_t key = ftok("shmfile", 65);
if (key == -1) {
perror("ftok");
return 1;
}
// 创建共享内存段
int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
if (shmid == -1) {
perror("shmget");
return 1;
}
printf("Shared memory created with ID: %d\n", shmid);
return 0;
}
(2)挂载共享内存段并写入数据
c
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main() {
// 生成唯一键值
key_t key = ftok("shmfile", 65);
if (key == -1) {
perror("ftok");
return 1;
}
// 获取共享内存段
int shmid = shmget(key, 1024, 0666);
if (shmid == -1) {
perror("shmget");
return 1;
}
// 挂载共享内存段
char *str = (char*) shmat(shmid, (void*)0, 0);
if (str == (char*) -1) {
perror("shmat");
return 1;
}
// 写入数据到共享内存
printf("Writer: Enter data to write: ");
fgets(str, 1024, stdin);
printf("Writer: Data written to shared memory: %s\n", str);
// 分离共享内存段
if (shmdt(str) == -1) {
perror("shmdt");
return 1;
}
return 0;
}
(3)挂载共享内存段并读取数据,最后销毁
c
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
// 生成唯一键值
key_t key = ftok("shmfile", 65);
if (key == -1) {
perror("ftok");
return 1;
}
// 获取共享内存段
int shmid = shmget(key, 1024, 0666);
if (shmid == -1) {
perror("shmget");
return 1;
}
// 挂载共享内存段
char *str = (char*) shmat(shmid, (void*)0, 0);
if (str == (char*) -1) {
perror("shmat");
return 1;
}
// 读取数据
printf("Reader: Data read from shared memory: %s\n", str);
// 分离共享内存段
if (shmdt(str) == -1) {
perror("shmdt");
return 1;
}
// 销毁共享内存段
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl");
return 1;
}
printf("Shared memory destroyed.\n");
return 0;
}
通信案例
(1)写入进程(writer.c)
c
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main() {
key_t key = ftok("shmfile", 65); // 生成唯一键值
int shmid = shmget(key, 1024, 0666 | IPC_CREAT); // 创建共享内存段
char *str = (char*) shmat(shmid, (void*)0, 0); // 附加共享内存段
printf("Writer: Enter data to write: ");
fgets(str, 1024, stdin); // 写入数据到共享内存
printf("Writer: Data written to shared memory: %s\n", str);
shmdt(str); // 分离共享内存段
return 0;
}
(2)读取进程(reader.c)
c
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
key_t key = ftok("shmfile", 65); // 生成唯一键值
int shmid = shmget(key, 1024, 0666 | IPC_CREAT); // 获取共享内存段
char *str = (char*) shmat(shmid, (void*)0, 0); // 附加共享内存段
printf("Reader: Data read from shared memory: %s\n", str);
shmdt(str); // 分离共享内存段
shmctl(shmid, IPC_RMID, NULL); // 删除共享内存段
return 0;
}