memcpy和mmap的误解
Misunderstanding of memcpy & mmap
我需要在进程之间使用共享内存,我找到了示例代码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!
在主函数中,我正在创建 1 字节 共享内存块 (shmem
) 并尝试存储 13 字节信息(char msg[]
)在里面。当我打印出 shmem
时,它会打印出整条消息。我期待它只打印出 1 字节 消息,在这种情况下只是 "h"
。或者它可能会在编译时给出有关内存大小的错误。
问题是我在这里遗漏了什么?还是存在执行问题? memcpy
这里有重叠吗?感谢您提供任何简短的解释。
提前致谢。
您的代码将超过 1 个字节写入大小为 1 字节的内存映射,这违反了 mmap()
的约定。
但是,正如您所发现的,它有时可能在某些系统上工作。这可能是因为一页的大小(在内存映射中)是例如4 KB。所以也许映射比请求的要大。尽管如此,您仍然没有权利像您一样使用它。
所以,停止这样做。
你问是不是应该是编译错误。答案是否定的:编译器没有像 mmap()
这样的每个库例程的特殊情况。它不知道 mmap()
的 size
参数意味着返回的指针仅对那么多字节有效。静态分析器可能会解决这个问题,但编译器通常不会这样做。
在 printf("message: %s\n", shmem);
中,%s
说明符表示打印从 shmem
开始的“字符串”。为此,字符串是以空字符结尾的字符序列。所以 printf
打印它在 shmem
找到的所有字节,直到空字符。要将其限制为最多一个字符,您可以使用 %.1s
代替,或者您可以使用 printf("message: %c\n", * (char *) shmem);
.
显式打印一个字符
当您使用 mmap
分配内存时,系统以页面为单位使用内存。页的大小因系统而异,但通常为 512 或 4096 字节,而不是 1。 mmap
的标准规范仅保证提供您请求的字节数。除此之外可能还有其他字节可访问,但您不应依赖它们可用。 (即使它们暂时可用,当您的程序暂时换出内存时,系统可能不会将它们保存到磁盘,因此当您的程序返回内存继续时,它们不会被恢复 运行。 )
sizeof(shmem)
提供了shmem
的大小,是一个指针。所以它提供了指针的大小,在现代系统上通常是四个或八个字节。它不提供 shmem
指向的东西的大小。
相比之下,在sizeof(msg)
中,msg
是一个数组,而不是指针,所以sizeof(msg)
确实提供了数组的大小,你可能打算.
memcpy(shmem, msg, sizeof(msg));
将 13 个字节(msg
的大小)复制到 shmem
。那十三个字节是“hello world!”和末尾的空字符(值 0)。 memcpy
除了您传递的长度参数外,没有任何方法知道源或目标有多长。所以它复制 sizeof(msg)
字节。它不会将自己限制为 shmem
指向的内存大小。传递正确的长度是你的工作。
要回答有关如果使用的字节数多于 mmap
提供的字节数会发生什么的问题,行为是未定义的。如果超出页面边界,程序很可能会崩溃,因为超出该地址的内存未被映射。但是您可能会将字节写入内存中您不想写入的位置,这可能会导致各种情况发生,因为它会损坏您的程序正常执行所需的代码或数据。
在这种情况下,您没有写入映射内存之外的内容。您要求 13 个字节,很可能得到 4096(或您系统上的任何一页)。然后将这 13 个字节复制到缓冲区中并打印出来。所以一切都“有效”。
我需要在进程之间使用共享内存,我找到了示例代码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!
在主函数中,我正在创建 1 字节 共享内存块 (shmem
) 并尝试存储 13 字节信息(char msg[]
)在里面。当我打印出 shmem
时,它会打印出整条消息。我期待它只打印出 1 字节 消息,在这种情况下只是 "h"
。或者它可能会在编译时给出有关内存大小的错误。
问题是我在这里遗漏了什么?还是存在执行问题? memcpy
这里有重叠吗?感谢您提供任何简短的解释。
提前致谢。
您的代码将超过 1 个字节写入大小为 1 字节的内存映射,这违反了 mmap()
的约定。
但是,正如您所发现的,它有时可能在某些系统上工作。这可能是因为一页的大小(在内存映射中)是例如4 KB。所以也许映射比请求的要大。尽管如此,您仍然没有权利像您一样使用它。
所以,停止这样做。
你问是不是应该是编译错误。答案是否定的:编译器没有像 mmap()
这样的每个库例程的特殊情况。它不知道 mmap()
的 size
参数意味着返回的指针仅对那么多字节有效。静态分析器可能会解决这个问题,但编译器通常不会这样做。
在
printf("message: %s\n", shmem);
中,%s
说明符表示打印从shmem
开始的“字符串”。为此,字符串是以空字符结尾的字符序列。所以printf
打印它在shmem
找到的所有字节,直到空字符。要将其限制为最多一个字符,您可以使用%.1s
代替,或者您可以使用printf("message: %c\n", * (char *) shmem);
. 显式打印一个字符
当您使用
mmap
分配内存时,系统以页面为单位使用内存。页的大小因系统而异,但通常为 512 或 4096 字节,而不是 1。mmap
的标准规范仅保证提供您请求的字节数。除此之外可能还有其他字节可访问,但您不应依赖它们可用。 (即使它们暂时可用,当您的程序暂时换出内存时,系统可能不会将它们保存到磁盘,因此当您的程序返回内存继续时,它们不会被恢复 运行。 )sizeof(shmem)
提供了shmem
的大小,是一个指针。所以它提供了指针的大小,在现代系统上通常是四个或八个字节。它不提供shmem
指向的东西的大小。相比之下,在
sizeof(msg)
中,msg
是一个数组,而不是指针,所以sizeof(msg)
确实提供了数组的大小,你可能打算.memcpy(shmem, msg, sizeof(msg));
将 13 个字节(msg
的大小)复制到shmem
。那十三个字节是“hello world!”和末尾的空字符(值 0)。memcpy
除了您传递的长度参数外,没有任何方法知道源或目标有多长。所以它复制sizeof(msg)
字节。它不会将自己限制为shmem
指向的内存大小。传递正确的长度是你的工作。
要回答有关如果使用的字节数多于 mmap
提供的字节数会发生什么的问题,行为是未定义的。如果超出页面边界,程序很可能会崩溃,因为超出该地址的内存未被映射。但是您可能会将字节写入内存中您不想写入的位置,这可能会导致各种情况发生,因为它会损坏您的程序正常执行所需的代码或数据。
在这种情况下,您没有写入映射内存之外的内容。您要求 13 个字节,很可能得到 4096(或您系统上的任何一页)。然后将这 13 个字节复制到缓冲区中并打印出来。所以一切都“有效”。