windows下实现共享内存
进程A写入共享内存:
#include <stdio.h>
#include <windows.h>
int main() {
// 创建或打开共享内存对象
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
4096, // 内存大小,这里为4KB
TEXT("SharedMemory")); // 共享内存名称,进程B也需要使用相同的名称来打开共享内存
if (hMapFile == NULL) {
printf("Could not create file mapping object (%d).\n", GetLastError());
return 1;
}
// 将共享内存映射到当前进程的地址空间
LPVOID pBuf = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 4096);
if (pBuf == NULL) {
printf("Could not map view of file (%d).\n", GetLastError());
CloseHandle(hMapFile);
return 1;
}
// 写入数据到共享内存
strcpy((char *)pBuf, "Hello from Process A");
printf("Data written to shared memory: %s\n", (char *)pBuf);
// 保持进程运行,以便观察效果
while (1) {
Sleep(1000);
}
// 清理资源
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
进程B读取共享内存:
#include <stdio.h>
#include <windows.h>
int main() {
// 打开共享内存对象
HANDLE hMapFile = OpenFileMapping(
FILE_MAP_READ,
FALSE,
TEXT("SharedMemory")); // 使用与进程A相同的共享内存名称
if (hMapFile == NULL) {
printf("Could not open file mapping object (%d).\n", GetLastError());
return 1;
}
// 将共享内存映射到当前进程的地址空间
LPVOID pBuf = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 4096);
if (pBuf == NULL) {
printf("Could not map view of file (%d).\n", GetLastError());
CloseHandle(hMapFile);
return 1;
}
// 从共享内存读取数据
printf("Data read from shared memory: %s\n", (char *)pBuf);
// 清理资源
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
Linux下的共享内存
如果你的应用程序需要在不同的Unix/Linux系统上运行,并且希望保持较好的可移植性,那么建议使用POSIX共享内存。POSIX共享内存是POSIX标准的一部分,因此在符合POSIX标准的系统上都可以使用,包括大多数现代的Unix/Linux系统。
注意:你需要确保在编译时链接POSIX共享内存库。在编译时,需要使用 -lrt 参数链接 librt 库,因为 shm_open 函数位于该库中。比如:gcc process1.c -o process1 -lrt
process1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#define SHM_NAME "/my_shared_memory" // 定义共享内存的名称
#define SHM_SIZE 100 // 定义共享内存的大小 单位为字节
int main()
{
int shm_fd; // 共享内存文件描述符
char *shm_ptr; // 指向共享内存区域的指针
// 创建或打开共享内存对象
shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
if (shm_fd == -1)
{ // 检查共享内存文件描述符是否有效
perror("shm_open"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
// 调整共享内存对象的大小
if (ftruncate(shm_fd, SHM_SIZE) == -1)
{ // 将共享内存调整为指定的大小
perror("ftruncate"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
// 将共享内存对象映射到进程的地址空间
shm_ptr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shm_ptr == MAP_FAILED)
{ // 检查共享内存是否映射成功
perror("mmap"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
// 写入数据到共享内存
sprintf(shm_ptr, "Hello, shared memory from process 1!");
// 等待一段时间,以便让第二个进程有机会读取共享内存中的数据
sleep(60);
// 解除内存映射
if (munmap(shm_ptr, SHM_SIZE) == -1)
{ // 取消内存映射
perror("munmap"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
// 关闭共享内存对象
if (close(shm_fd) == -1)
{ // 关闭共享内存文件描述符
perror("close"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
// 删除共享内存对象
if (shm_unlink(SHM_NAME) == -1) {
perror("shm_unlink");
exit(EXIT_FAILURE);
}
return 0; // 程序正常结束
}
process2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#define SHM_NAME "/my_shared_memory" // 定义共享内存的名称
#define SHM_SIZE 100 // 定义共享内存的大小
int main() {
int shm_fd; // 共享内存文件描述符
char *shm_ptr; // 指向共享内存区域的指针
// 打开共享内存对象
shm_fd = shm_open(SHM_NAME, O_RDWR, 0666); // 以读写方式打开共享内存对象
if (shm_fd == -1) { // 检查共享内存文件描述符是否有效
perror("shm_open"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
// 将共享内存对象映射到进程的地址空间
shm_ptr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); // 映射共享内存到进程地址空间
if (shm_ptr == MAP_FAILED) { // 检查共享内存是否映射成功
perror("mmap"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
// 从共享内存中读取数据并打印
printf("Process 2 read from shared memory: %s\n", shm_ptr); // 打印共享内存中的数据
// 解除内存映射
if (munmap(shm_ptr, SHM_SIZE) == -1) { // 取消内存映射
perror("munmap"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
// 关闭共享内存对象
if (close(shm_fd) == -1) { // 关闭共享内存文件描述符
perror("close"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
// 删除共享内存对象
if (shm_unlink(SHM_NAME) == -1) {
perror("shm_unlink");
exit(EXIT_FAILURE);
}
return 0; // 程序正常结束
}
详解
/my_shared_memory
:这是一个绝对路径的名称,表示共享内存对象的全局路径。
在POSIX共享内存中,使用绝对路径可以确保在不同的目录下的进程都能够访问相同的共享内存对象。
#include <fcntl.h>
#include <sys/mman.h>
int shm_open(const char *name, int oflag, mode_t mode);
- name:共享内存对象的名称。这个名称类似于文件名,用于唯一标识共享内存对象。
- oflag:打开标志,指定 shm_open 的行为。可以使用 O_CREAT 创建共享内存对象,使用 O_RDWR 可以读写共享内存对象,使用 O_RDONLY 只读模式
- mode:创建新共享内存对象时指定的权限,与文件的权限类似。通常使用 0666,表示读写权限。
shm_open 函数成功时返回一个非负整数,即共享内存对象的文件描述符。失败时返回 -1,并设置 errno 来指示错误类型。
#include <unistd.h>
int ftruncate(int fd, off_t length);
- fd:共享内存对象的文件描述符。
- length:新的文件大小,以字节为单位。
调用成功时,ftruncate 返回 0;如果出现错误,返回 -1,并设置全局变量 errno 来指示错误类型。
ftruncate 函数用于更改文件的大小。在共享内存中,它通常用于调整共享内存对象的大小。
对于 POSIX 共享内存对象,你无法在创建时直接指定大小。相反,你首先使用 shm_open 函数创建共享内存对象,然后使用 ftruncate 函数调整其大小。
#include <sys/mman.h>
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
:允许写入映射区域的内容。PROT_EXEC
:允许执行映射区域的内容。PROT_NONE
:禁止访问映射区域的内容。
flags
:指定映射区域的标志,可以是以下值的组合:MAP_SHARED
:映射区域与文件或共享内存对象关联,对该区域的修改会影响到其他映射该文件或共享内存对象的进程。MAP_PRIVATE
:映射区域为私有,对该区域的修改不会影响到其他映射同一文件或共享内存对象的进程。MAP_ANONYMOUS
:创建一个匿名映射区域,不与任何文件关联。
fd
:如果映射的是文件,指定文件的文件描述符;如果映射的是共享内存对象,指定共享内存对象的文件描述符;如果是匿名映射,可以设置为-1
。offset
:指定文件映射的起始偏移量。
成功调用 mmap
会返回一个指向映射区域起始地址的指针,失败时返回 MAP_FAILED
,并设置全局变量 errno
来指示错误类型。
#include <sys/mman.h>
int munmap(void *addr, size_t length);
addr
:指向映射区域起始地址的指针,即mmap
函数返回的地址。length
:映射区域的大小,以字节为单位。
munmap
函数用于取消一个已经存在的内存映射区域。当你不再需要一个内存映射时,应该调用 munmap
函数来释放对应的内存区域。
成功调用 munmap
会返回 0
,失败时返回 -1
,并设置全局变量 errno
来指示错误类型。
当你调用 munmap 函数释放内存映射区域时,你释放的是当前进程地址空间中的一部分内存,而不是共享内存对象本身。这意味着释放后,当前进程将不再拥有对应的内存映射区域,但其他进程仍然可以继续访问和使用同一共享内存对象。
close(shm_fd)
函数关闭了与共享内存对象相关联的文件描述符 shm_fd。这个调用关闭了文件描述符,但并不会影响共享内存本身的存在或可用性。
关闭文件描述符的目的主要是为了释放进程对文件描述符的占用,而不是释放共享内存本身。共享内存对象在系统中的存在不受任何单个进程的控制,它只有在所有使用它的进程都调用 shm_unlink 函数将其删除后才会被系统清理。
#include <sys/mman.h>
int shm_unlink(const char *name);
参数 name 是要删除的共享内存对象的名称。调用该函数后,系统将删除该名称对应的共享内存对象,但只有当所有打开该共享内存对象的进程都关闭了它之后,共享内存对象的内存才会被释放。