进程间共享内存通信
Linux 分别创建client和server端,使用共享内存进行进程间通信 C++实现
mmap/shmget
mmap内存映射和shmget共享内存
mmap
和 shmget
是两种不同的机制,用于在Unix和Linux环境中实现内存共享或文件映射。尽管它们的目的都是为了提供某种形式的内存共享,但它们的实现和使用场景有所不同。
mmap(内存映射)
mmap
是一个系统调用,它可以将一个文件或其他对象映射进内存。文件被映射到调用进程的地址空间中,进程就像访问内存一样对文件进行访问。这种机制提供了对文件的方便访问,因为文件的内容被直接加载到内存中,而不需要传统的I/O系统调用(如 read
或 write
)。
优点:
- 提供了对文件的直接访问,无需传统的I/O系统调用
- 可以映射整个文件或文件的一部分到内存中
- 适用于大型文件,因为映射可以只覆盖文件的一部分
使用场景:
- 高效的文件访问,特别是当需要频繁读取或写入大文件时
- 进程间通信(IPC),通过映射到共享文件实现
shmget(共享内存)
shmget
是System V IPC(进程间通信)的一部分,它用于创建或访问一个共享内存段。这个内存段可以被多个进程同时访问,以实现进程间的数据共享。
优点:
- 提供了快速的进程间通信机制,因为数据直接在内存中共享
- 可以动态地分配和释放共享内存段
使用场景:
- 需要高效、低延迟的进程间通信场景
- 需要在多个进程之间共享大量数据
注意事项:
- 仅仅使用
shmget
创建共享内存是不够的,还需要使用shmat
将共享内存附加到进程的地址空间中,并使用shmdt
来分离共享内存 - 共享内存不提供任何同步机制,因此需要其他机制(如信号量、互斥量等)来确保数据的一致性和完整性
总结
mmap
主要用于将文件映射到内存中,以便进行高效的文件访问或实现进程间通信shmget
用于创建和访问共享内存段,以实现进程间的数据共享
这两种机制都有其特定的用途和优缺点,选择哪种机制取决于具体的应用场景和需求
shmfile文件夹为空
key_t key = ftok("shmfile", 'a'); // 创建唯一的键
/* Generates key for System V style IPC. */
extern key_t ftok (const char *__pathname, int __proj_id) __THROW;
在Linux系统中,共享内存并不是以文件系统中的文件形式存在的,而是作为内核管理的一个资源。当你使用共享内存(如通过 shmget()
, shmat()
, shmdt()
, shmctl()
等函数操作共享内存时),这些操作并不是在文件系统中创建一个可见的文件,而是在内核的内存中创建了一个共享内存段。
shmfile
在上述例子中是用来通过 ftok()
创建一个唯一的键(key),这个键用于标识共享内存段。
ftok()
函数需要两个参数:
- 一个文件的路径
- 一个项目标识符
即使这个文件不存在,只要项目标识符是唯一的,ftok()
就可以生成一个唯一的键。
共享内存的“文件”实际上是一个内核对象,它没有对应的文件系统中的文件,因此我们无法在文件系统中直接看到或访问它。然而,可以通过特定的系统调用和工具来查看和管理共享内存段,例如:
-
使用
ipcs
命令:这个命令可以列出系统中所有的IPC资源,包括消息队列、共享内存段和信号量。运行ipcs -m
可以查看共享内存的信息。 -
使用
ipcrm
命令:这个命令可以用来删除IPC资源,包括共享内存段。
共享内存的生命周期由创建它的进程和使用它的进程控制。当最后一个引用共享内存的进程释放它,或者显式地使用 shmctl()
函数将其删除后,共享内存段就会消失。
总结来说,共享内存文件在文件系统中不可见,因为它们不是文件系统中的文件,而是内核管理的内存对象。通过系统级的命令如 ipcs
和 ipcrm
可以进行查看和管理。
ipcs/ipcrm
ipcs
命令的全称是 Interprocess Communication Status
进程间通信状态,用于显示与进程间通信有关的信息
IPC
是操作系统提供给进程之间进行数据交换的一种机制,它主要分为以下三种类型:
-
消息队列(Message Queues)
- 消息队列是一个链表,存放在内核中并由消息队列标识符标识
- Linux内核维护了一个消息队列和相关操作函数的集合,用户进程可以调用操作函数完成消息发送和接收
-
信号量(Semaphores)
- 信号量主要作为进程间以及同一进程不同线程之间的同步手段
-
共享内存(Shared Memories)
- 让任何需要的进程都能访问的内存区域
- 因为多个进程可以同时操作,所以必须通过某种同步操作,如信号量,来确保进程间互不干扰
ipcs
命令可以显示每种IPC对象的关键信息,包括创建者、所有者、权限、关联的资源等
ipcs
命令语法
ipcs [options]
-i ID : 显示特定ID的信息
-q : 显示消息队列
-m : 显示共享内存
-s : 显示信号量
-a : 显示所有信息(消息队列,共享内存,信号量)
ipcrm命令
说明:删除消息队列、共享内存、信号灯
ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ] …
-M 以shmkey删除共享内存
-m 以shmid删除共享内存
-Q 以msgkey删除消息队列
-q 以msgid删除消息队列
-S 以semkey删除信号量
-s 以semid删除信号量
常用函数
shmxxx系列
在上述共享内存通信的C++示例代码中,使用了以下五个关键函数:
ftok()
- 创建唯一的键shmget()
- 创建或获取共享内存段shmat()
- 将共享内存段附加到调用进程的地址空间shmdt()
- 从调用进程的地址空间分离共享内存段shmctl()
- 控制共享内存段
下面详细介绍每个函数:
1. ftok()
系统建立IPC通讯 (消息队列、信号量和共享内存)时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
ftok是系统IPC键值的格式转换函数。
key_t ftok(const char *pathname, char proj_id);
ftok()
函数用于创建一个唯一的键(key),这个键可以用于IPC(进程间通信)操作,如共享内存、消息队列和信号量。
它通过将文件名(pathname
)和一个项目标识符(proj_id
)结合起来生成一个键。
参数:
pathname
:一个存在的文件路径名或目录路径名,用于生成键值,建议使用具有全局可见性的文件路径名。proj_id
:一个用户定义的整数值,用于生成键值的低序8位,它也可以是字符型变量,因为这个整型值可以通过将一个字符转换为整数来获得。实际上,ftok函数会将第二个参数的最低字节(即最后8位)作为整型值使用,一个字符变量(char类型)刚好是一个字节,也就是8位。因此,传递一个字符变量作为第二个参数也是可以的。
返回值:
- 成功时返回一个唯一的键。
- 失败时返回-1,并设置
errno
以指示错误。
ftok
函数将通过对pathname
的索引节点号(inode number)与proj_id
进行异或操作生成一个唯一的键值。返回的键值类型是key_t
,通常是一个长整型。
索引节点号,也称为Inode Index,是Linux文件系统中用于标识文件的一个唯一编号。
每个文件在磁盘分区中都有一个索引节点号,通过这个编号,操作系统可以识别文件。索引节点不仅包含了文件的地址信息,而且每个索引节点的地址都是唯一的,因此可以将地址转化为整数型的索引节点号,以此来唯一标识每个文件。此外,索引节点还包含了文件的状态信息、访问计数、逻辑设备号以及连接指针等数据
理解inode,要从文件储存说起。
文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。
操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个”块”(block)。这种由多个扇区组成的”块”,是文件存取的最小单位。”块”的大小,最常见的是4KB,即连续八个 sector组成一个 block。
文件数据都储存在”块”中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。
每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。
2. shmget()
int shmget(key_t key, size_t size, int shmflg);
shmget()
函数用于创建一个新的共享内存段,或者获取一个已存在的共享内存段的标识符(ID)。
参数:
key
:由ftok()
生成的唯一键。size
:共享内存段的大小(字节)。shmflg
:共享内存段的权限和选项,通常是文件权限(如0666)与IPC创建标志(如IPC_CREAT)的组合。
返回值:
- 成功时返回共享内存段的标识符 shm_id。
- 失败时返回-1,并设置
errno
以指示错误。
3. shmat()
把共享内存区对象映射到调用进程的地址空间
void *shmat(int shm_id, const void *shmaddr, int shmflg);
shmat()
函数用于将共享内存段附加到调用进程的地址空间。这使得进程可以访问共享内存段。
参数:
shm_id
:共享内存段的标识符,由shmget()
返回。shmaddr
:指定共享内存段附加到的地址。通常设置为nullptr
,让系统选择地址。shmflg
:控制共享内存附加行为的标志。
返回值:
- 成功时返回指向共享内存段的指针
shmaddr
。 - 失败时返回
(void *)-1
,并设置errno
以指示错误。
4. shmdt()
int shmdt(const void *shmaddr);
shmdt()
函数用于从调用进程的地址空间分离共享内存段,使得进程不再能够访问该共享内存。
参数:
shmaddr
:之前由shmat()
返回的共享内存段的地址。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
以指示错误。
5. shmctl()
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
shmctl()
函数用于对共享内存段进行控制操作,如获取信息、设置选项或删除共享内存段。
参数:
-
shm_id
:共享内存段的标识符。 -
cmd
:控制命令IPC_STAT
(获取共享内存段的状态信息)IPC_SET
(设置共享内存段的属性)IPC_RMID
(删除共享内存段)
-
buf
:一个指向shmid_ds
结构的指针,用于传递信息或接收信息,具体取决于cmd
。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
以指示错误。
shm_open系列
1. shm_open
shm_open操作的文件一定是位于tmpfs文件系统里的,常见的Linux发布版的tmpfs文件系统的存放目录就是/dev/shm
函数原型:
int shm_open(const char *name, int oflag, mode_t mode);
功能:
shm_open
用于创建一个新的共享内存对象或打开一个已存在的共享内存对象。它接受三个参数:
name
:共享内存对象的名称,是一个路径字符串(通常以非文件系统的特殊路径前缀开始,如/shm/
)。oflag
:标志位,如O_RDONLY
(只读)、O_RDWR
(读写)、O_CREAT
(如果不存在则创建)等。mode
:权限模式,如S_IRUSR | S_IWUSR
,定义了新创建的共享内存对象的权限。
返回值是一个文件描述符,可用于后续的共享内存操作。
2. ftruncate
函数原型:
int ftruncate(int fd, off_t length);
功能:
ftruncate
用于调整文件或共享内存段的大小。它接受两个参数:
fd
:由shm_open
返回的文件描述符。length
:新的大小,以字节为单位。
该函数可以用来初始化共享内存段的大小,确保有足够的空间供进程写入数据。
3. mmap
函数原型:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
功能:
mmap
用于将文件或共享内存对象映射到进程的地址空间中,使得进程可以直接通过指针访问共享内存中的数据,而不是通过读写文件描述符。参数含义如下:
addr
:建议的映射起始地址,通常设为NULL让系统决定。length
:映射区域的长度。prot
:保护标志,如PROT_READ
(可读)、PROT_WRITE
(可写)等。flags
:映射类型,如MAP_SHARED
(映射是共享的,对映射区的修改会影响所有映射该区域的进程)。fd
:由shm_open
返回的文件描述符。offset
:映射的偏移量,通常为0。
返回值是一个指向映射区起始地址的指针,或者在失败时返回MAP_FAILED
。
4. munmap
函数原型:
int munmap(void *addr, size_t length);
功能:
munmap
用于取消之前通过mmap
建立的内存映射,释放映射到进程地址空间的共享内存区域。参数包括:
addr
:映射区的起始地址,即mmap
返回的指针。length
:映射区域的长度,应与mmap
时提供的长度相同。
成功执行时返回0,失败返回-1。
5. shm_unlink
函数原型:
int shm_unlink(const char *name);
功能:
shm_unlink
用于删除一个共享内存对象。与文件的删除操作不同,即使调用了shm_unlink
,已打开的共享内存段仍可继续使用,直到最后一个使用它的进程关闭它。当最后一个引用被关闭时,系统才会真正回收共享内存资源。
参数name
是通过shm_open
创建共享内存时使用的名称。成功执行时返回0,失败返回-1。
demo
shmget
server.cpp
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
int main() {
key_t key = ftok("shmfile", 'a'); // 创建唯一的键
if (key == -1) {
perror("ftok");
return 1;
}
int shm_id = shmget(key, 1024, 0666 | IPC_CREAT); // 创建共享内存
if (shm_id < 0) {
perror("shmget");
return 1;
}
char *shmaddr = static_cast<char*>(shmat(shm_id, nullptr, 0)); // 将共享内存附加到进程地址空间
if (shmaddr == reinterpret_cast<char*>(-1)) {
perror("shmat");
return 1;
}
std::cout << "Server: Shared memory created and attached." << std::endl;
// 等待客户端发送消息
std::cout << "Waiting for client message..." << std::endl;
std::cin.get(); // 模拟等待客户端发送消息
// 读取共享内存中的消息
std::cout << "Server: Message from client: " << shmaddr << std::endl;
std::cin.get();
// 清理共享内存
shmdt(shmaddr); // 从进程地址空间分离共享内存
shmctl(shm_id, IPC_RMID, nullptr); // 删除共享内存
return 0;
}
使用POSIX的共享内存(Shared Memory)API来创建一个共享内存区域,并将其附加到进程的地址空间。然后,等待一个模拟的“客户端消息”(实际上是通过用户输入来模拟的),并尝试从共享内存中读取这个消息。最后,清理共享内存资源。
下面是对代码的详细解释:
- 创建唯一的键:
- 使用
ftok
函数创建一个唯一的键(key_t
类型),该键用于在系统中唯一标识共享内存。 ftok
需要两个参数:一个文件路径和一个字符。如果文件不存在或由于其他原因失败,它将返回-1。
- 创建共享内存:
- 使用
shmget
函数创建共享内存。 - 它接受三个参数:键(
key
)、共享内存的大小(在此例中为1024字节)和权限标志(0666表示所有用户都可以读写,但通常还需要与IPC_CREAT
标志组合以指示如果键不存在则创建它)。 - 如果成功,它将返回一个非负整数(共享内存标识符),否则返回-1。
- 将共享内存附加到进程地址空间:
- 使用
shmat
函数将共享内存附加到进程的地址空间。 - 它接受三个参数:共享内存标识符、附加地址(如果为nullptr,则由系统选择)和标志(在此例中为0)。
- 如果成功,它将返回一个指向共享内存起始地址的指针,否则返回-1的重新解释。
- 模拟等待客户端发送消息:
- 这部分只是使用
std::cin.get()
来模拟等待客户端发送消息。 - 在实际应用中,你会有其他方式来接收或检测来自客户端的消息。
-
读取共享内存中的消息:
- 这里尝试直接从
shmaddr
指针(指向共享内存的起始地址)打印消息。
- 这里尝试直接从
-
清理共享内存:
- 使用
shmdt
函数从进程地址空间分离共享内存。这不会删除共享内存,只是解除了它与进程的关联。 - 使用
shmctl
函数删除共享内存。它接受三个参数:共享内存标识符、命令(在此例中为IPC_RMID
,表示删除)和一个指向struct shmid_ds
的指针(在此例中为nullptr,因为我们不需要它)。
- 使用
注意:这个代码示例主要是为了演示如何使用POSIX的共享内存API,但在实际应用中,你需要考虑更多的错误处理和安全性问题,以及确保在客户端和服务器之间正确传递消息。
client.cpp
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main() {
key_t key = ftok("shmfile", 'a'); // 使用与服务器相同的键
if (key == -1) {
perror("ftok");
return 1;
}
int shm_id = shmget(key, 1024, 0666); // 连接到共享内存
if (shm_id < 0) {
perror("shmget");
return 1;
}
char *shmaddr = static_cast<char*>(shmat(shm_id, nullptr, 0)); // 将共享内存附加到进程地址空间
if (shmaddr == reinterpret_cast<char*>(-1)) {
perror("shmat");
return 1;
}
std::cout << "Client: Shared memory attached." << std::endl;
// 向共享内存写入消息
std::string message = "Hello from client!";
strcpy(shmaddr, message.c_str());
// 模拟消息发送完成
std::cout << "Client: Message sent to server." << std::endl;
std::cin.get();
// 清理共享内存
shmdt(shmaddr); // 从进程地址空间分离共享内存
return 0;
}
这段代码是一个简单的客户端程序,用于向一个预先存在的共享内存区域写入一条消息。以下是代码的详细解释:
-
创建唯一的键:
- 使用
ftok
函数创建一个唯一的键,这个键与服务器用来创建共享内存区域的键相同。 - 函数需要两个参数:一个文件路径和一个标识符字符。
- 使用
-
连接到共享内存:
- 使用
shmget
函数连接到共享内存。 - 这里不需要
IPC_CREAT
标志,因为我们只是连接到已经存在的共享内存区域。
- 使用
-
将共享内存附加到进程地址空间:
- 使用
shmat
函数将共享内存附加到进程的地址空间。 - 如果成功,它返回一个指向共享内存区域的指针。
- 使用
-
写入消息到共享内存:
- 创建一个
std::string
类型的变量message
,并给它赋一个值 "Hello from client!"。 - 使用
strcpy
函数将message
的 C 字符串表示(通过c_str()
方法获取)复制到共享内存区域。 - 注意,这里假设消息的长度不会超过共享内存区域的大小,并且没有同步机制来确保服务器在写入时不会同时读取或写入。
- 创建一个
-
模拟消息发送完成:
- 输出一条消息到控制台,表示消息已成功发送到服务器。然后等待用户输入以继续执行(这通常用于调试或测试目的)。
-
清理共享内存:
- 使用
shmdt
函数从进程的地址空间分离共享内存。这不会删除共享内存区域,只是解除了进程与它的关联。
- 使用
注意:
- 在实际的多进程环境中,当多个进程同时访问共享内存时,需要同步机制来确保数据的一致性和完整性。这个示例代码没有包含任何同步机制。
shmget
在这里只是连接到共享内存,而不是创建它。如果共享内存区域不存在,shmget
将失败。strcpy
在这里使用是安全的,因为我们知道消息的长度不会超出共享内存的大小。但在实际应用中,应该检查并确保没有缓冲区溢出。- 在实际的应用程序中,服务器和客户端可能需要更复杂的通信协议和错误处理机制。
shmget
服务器端(Server)
服务器端需要执行以下操作:
- 创建唯一的键。ftok
- 使用该键创建共享内存。shmget
- 将共享内存附加到其地址空间。shmat
- 向共享内存中写入数据。
- 等待客户端或其他进程访问共享内存。
- 清理共享内存。shmdt shmctl
#include <iostream>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
int main() {
key_t key = ftok("/tmp", 'S'); // 创建唯一的键
std::cout << "server key:" << key << std::endl;
std::cout << "server key(Hex):" << std::hex << key << std::endl;
int shm_id = shmget(key, 1024, IPC_CREAT | 0666); // 创建共享内存
if (shm_id == -1) {
perror("shmget");
return 1;
}
std::cout << "server shm_id:" << std::dec << shm_id << std::endl;
char *shm_addr = (char*)shmat(shm_id, nullptr, 0); // 附加共享内存
if (shm_addr == (char*)-1) {
perror("shmat");
return 1;
}
std::strcpy(shm_addr, "Hello from Server!"); // 写入数据
// 模拟等待客户端读取数据
std::cout << "Server: Data written to shared memory. Waiting for client..." << std::endl;
sleep(10); // 等待一段时间让客户端连接
// 清理
shmdt(shm_addr); // 分离共享内存
shmctl(shm_id, IPC_RMID, nullptr); // 删除共享内存
return 0;
}
客户端(Client)
客户端需要执行以下操作:
- 使用与服务器相同的键连接到共享内存。ftok
- 将共享内存附加到其地址空间。shmat
- 从共享内存中读取数据。
- 清理共享内存。shmdt
#include <iostream>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
key_t key = ftok("/tmp", 'S'); // 使用与服务器相同的键
std::cout << "client key:" << key << std::endl;
std::cout << "client key(Hex):" << std::hex << key << std::endl;
int shm_id = shmget(key, 1024, 0666); // 连接到共享内存
if (shm_id == -1) {
perror("shmget");
return 1;
}
std::cout << "client shm_id:" << std::dec << shm_id << std::endl;
char *shm_addr = (char*)shmat(shm_id, nullptr, 0); // 附加共享内存
if (shm_addr == (char*)-1) {
perror("shmat");
return 1;
}
std::cout << "Client: Data from shared memory: " << shm_addr << std::endl; // 读取数据
// 清理(通常这不是客户端的责任)
shmdt(shm_addr); // 分离共享内存
// 注意:不要在这里删除共享内存,因为服务器可能仍然在使用它
return 0;
}
shm_open
shm_open
提供了一种基于文件描述符的共享内存对象管理方式
int shm_open(const char *name, int oflag, mode_t mode);
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);
int shm_unlink(const char *name);
int ftruncate(int fd, off_t length);
linux共享内存是通过tmpfs
这个文件系统来实现的,tmpfs文件系的目录为/dev/shm
,/dev/shm
是驻留在内存 RAM 当中的,因此读写速度与读写内存速度一样,/dev/shm
的容量默认尺寸为系统内存大小的一半大小,使用df -h
命令可以看到。但实际上它并不会真正的占用这块内存,如果/dev/shm/
下没有任何文件,它占用的内存实际上就是0字节,仅在使用shm_open
文件时,/dev/shm
才会真正占用内存
server.cpp
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
#define SHM_NAME "/my_shared_memory"
#define BUF_SIZE 100
int main() {
// 创建共享内存段,O_CREAT表示如果不存在则创建,O_RDWR为读写模式
int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
if (shm_fd == -1) {
perror("shm_open");
return 1;
}
// 设置共享内存大小
if (ftruncate(shm_fd, BUF_SIZE) == -1) {
perror("ftruncate");
close(shm_fd);
return 1;
}
// 将共享内存映射到当前进程地址空间
void* ptr = mmap(0, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
close(shm_fd);
return 1;
}
// 向共享内存写入数据
strcpy(static_cast<char*>(ptr), "Hello from Server!");
// 等待一段时间以便客户端可以读取数据
sleep(10);
// 解映射并关闭共享内存
if (munmap(ptr, BUF_SIZE) == -1) {
perror("munmap");
}
close(shm_fd);
if (shm_unlink(SHM_NAME) == -1) {
perror("shm_unlink");
exit(EXIT_FAILURE);
}
return 0;
}
client.cpp
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
#define SHM_NAME "/my_shared_memory"
#define BUF_SIZE 100
int main() {
// 连接到已存在的共享内存段
int shm_fd = shm_open(SHM_NAME, O_RDONLY, 0);
if (shm_fd == -1) {
perror("shm_open");
return 1;
}
// 将共享内存映射到当前进程地址空间
void* ptr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
close(shm_fd);
return 1;
}
// 从共享内存读取数据
std::cout << "Message from Server: " << static_cast<char*>(ptr) << std::endl;
// 解映射并关闭共享内存
if (munmap(ptr, BUF_SIZE) == -1) {
perror("munmap");
}
close(shm_fd);
return 0;
}
shm_open与shmget
shm_open
和shmget
都是在Linux系统中用于创建和管理共享内存的接口,但它们属于不同的API族,具有以下几点主要区别:
-
所属接口:
shm_open
属于POSIX标准的一部分,与mmap
一起使用,提供了更为通用的文件描述符接口来操作共享内存。shmget
则是System V IPC (Inter-Process Communication) 接口的一部分,通常与shmat
(附加共享内存到进程地址空间)、shmdt
(分离共享内存)和shmctl
(控制共享内存)等函数配合使用。
-
命名和标识:
shm_open
使用字符串名称(路径名风格)来标识共享内存对象,这允许使用fopen
/open
类似的命名规则,包括命名空间的隔离(通过前缀/shm
或其他约定)。shmget
则使用键(通常是整数,可以通过ftok
生成)来标识共享内存段,系统维护一个键到共享内存标识符的映射表。
-
文件系统表现:
- 使用
shm_open
创建的共享内存段可以在文件系统中看到(通常是/dev/shm
目录下),可以像普通文件那样被ls列出,尽管它们实际上是内存映射文件。 shmget
创建的共享内存段并不直接出现在常规文件系统中,它们位于一个特殊的IPC文件系统中,不能直接通过文件系统访问或操作。
- 使用
-
内存映射:
shm_open
之后通常会紧接着调用mmap
来将共享内存映射到进程的地址空间,这提供了一种更灵活的方式处理共享内存,因为mmap
不仅可以用于共享内存,还可以用于映射普通文件。shmget
后的共享内存通过shmat
映射到进程地址空间,这一过程更专注于传统的IPC共享内存操作。
-
安全性和权限:
shm_open
允许设置文件权限(如在调用时指定的mode
参数),这影响了哪些用户和进程可以访问共享内存。shmget
同样可以设置权限,但它是通过shmctl
的IPC权限控制来实现,允许对共享内存段进行更细致的访问控制。
总的来说,选择shm_open
还是shmget
取决于具体需求,如是否需要利用POSIX接口的灵活性、是否希望共享内存表现为文件系统中的实体,以及对权限控制的具体要求等。现代应用开发更倾向于使用POSIX风格的shm_open
,因为它提供了更熟悉的文件描述符接口和更好的兼容性。
参考
Linux: shm_xx系列函数使用详解 https://blog.csdn.net/weixin_45842280/article/details/136384000
linux 共享内存 shm_open ,mmap的正确使用 https://gitcode.csdn.net/65ed7de71a836825ed79b49f.html