我需要在进程之间使用共享内存,我找到了示例代码here。首先,我需要学习如何创建共享内存块并在其中存储字符串。为此,我使用了以下代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
void* create_shared_memory(size_t size) {
// Our memory buffer will be readable and writable:
int protection = PROT_READ | PROT_WRITE;
// The buffer will be shared (meaning other processes can access it), but
// anonymous (meaning third-party processes cannot obtain an address for it),
// so only this process and its children will be able to use it:
int visibility = MAP_ANONYMOUS | MAP_SHARED;
// The remaining parameters to `mmap()` are not important for this use case,
// but the manpage for `mmap` explains their purpose.
return mmap(NULL, size, protection, visibility, 0, 0);
}
int main() {
char msg[] = "hello world!";
void* shmem = create_shared_memory(1);
printf("sizeof shmem: %lu\n", sizeof(shmem));
printf("sizeof msg: %lu\n", sizeof(msg));
memcpy(shmem, msg, sizeof(msg));
printf("message: %s\n", shmem);
}
输出:
sizeof shmem: 8
sizeof msg: 13
message: hello world!
在main函数中,我正在创建 1字节共享内存块(shmem
)并尝试存储 13字节信息(char msg[]
) 在里面。当我打印出shmem
时,它会打印整条信息。我期待它,它打印出 1字节消息,在这种情况下只是"h"
。或者它可能在编译时给出有关内存大小的错误。
问题是我在这里失踪了吗?或者是否存在实施问题? memcpy
在这里重叠吗?我很感激任何简短的解释。
提前致谢。
答案 0 :(得分:6)
在printf("message: %s\n", shmem);
中,%s
说明符表示打印从shmem
开始的“字符串”。为此,字符串是以空字符结尾的字符序列。因此printf
将它在shmem
找到的所有字节打印到空字符。要将其限制为最多一个字符,您可以改为使用%.1s
,也可以使用printf("message: %c\n", * (char *) shmem);
明确打印字符。
使用mmap
分配内存时,系统将以页为单位使用内存。页面大小因系统而异,但通常为512或4096字节,而不是1. mmap
的标准规范仅保证您提供的字节数。除此之外可能还有其他字节可访问,但您不应该依赖它们。 (即使它们似乎暂时可用,当程序暂时从内存中换出时,系统可能无法将它们保存到磁盘,因此当程序返回内存以继续运行时,它们将无法恢复。)
sizeof(shmem)
提供shmem
的大小,这是一个指针。因此它提供了指针的大小,在现代系统上通常为4或8个字节。它没有提供shmem
指向的内容的大小。
相反,在sizeof(msg)
中,msg
是一个数组,而不是指针,因此sizeof(msg)
确实提供了数组的大小,正如您可能想要的那样。
memcpy(shmem, msg, sizeof(msg));
将13个字节(msg
的大小)复制到shmem
。那十三个字节是“hello world!”,最后是一个空字符(值0)。除了您传递的length参数之外,memcpy
无法知道源或目标的长度。因此它复制sizeof(msg)
个字节。它并不局限于shmem
指向的内存大小。你的工作是传递正确的长度。
要回答关于如果使用比mmap
提供的字节多的字符会发生什么情况的问题,则行为未定义。如果超出页面边界,则程序很可能会崩溃,因为超出该地址的内存未映射。但是您可能会将字节写入内存中您不想要的位置,这可能会导致各种各样的事情发生,因为它可能会损坏您的程序需要正确执行的代码或数据。
在这种情况下,您没有写入映射内存。你问了13个字节,可能给了4096(或者你系统上的任何一个页面)。然后你将这13个字节复制到缓冲区并打印出来。所以一切都“奏效了。”
答案 1 :(得分:0)
您的代码违反了mmap()
的合同,在1个字节大小的请求内存映射中写入了超过1个字节。
但是,正如您所发现的,它有时可能适用于某些系统。这可能是因为一页的大小(在存储器映射中)是例如4 KB。所以也许映射比请求的要大。但是,你没有权利像你一样使用它。
所以,停止这样做。
您询问是否应该是编译错误。答案是否定的:编译器没有像mmap()
这样的每个库例程的特殊情况。它不知道size
的{{1}}参数意味着返回的指针仅对那么多字节有效。静态分析器可能会解决这个问题,但编译器通常不会这样做。