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 参数意味着返回的指针仅对那么多字节有效。静态分析器可能会解决这个问题,但编译器通常不会这样做。

  1. printf("message: %s\n", shmem); 中,%s 说明符表示打印从 shmem 开始的“字符串”。为此,字符串是以空字符结尾的字符序列。所以 printf 打印它在 shmem 找到的所有字节,直到空字符。要将其限制为最多一个字符,您可以使用 %.1s 代替,或者您可以使用 printf("message: %c\n", * (char *) shmem);.

  2. 显式打印一个字符
  3. 当您使用 mmap 分配内存时,系统以页面为单位使用内存。页的大小因系统而异,但通常为 512 或 4096 字节,而不是 1。 mmap 的标准规范仅保证提供您请求的字节数。除此之外可能还有其他字节可访问,但您不应依赖它们可用。 (即使它们暂时可用,当您的程序暂时换出内存时,系统可能不会将它们保存到磁盘,因此当您的程序返回内存继续时,它们不会被恢复 运行。 )

  4. sizeof(shmem)提供了shmem的大小,是一个指针。所以它提供了指针的大小,在现代系统上通常是四个或八个字节。它不提供 shmem 指向的东西的大小。

  5. 相比之下,在sizeof(msg)中,msg是一个数组,而不是指针,所以sizeof(msg)确实提供了数组的大小,你可能打算.

  6. memcpy(shmem, msg, sizeof(msg)); 将 13 个字节(msg 的大小)复制到 shmem。那十三个字节是“hello world!”和末尾的空字符(值 0)。 memcpy 除了您传递的长度参数外,没有任何方法知道源或目标有多长。所以它复制 sizeof(msg) 字节。它不会将自己限制为 shmem 指向的内存大小。传递正确的长度是你的工作。

要回答有关如果使用的字节数多于 mmap 提供的字节数会发生什么的问题,行为是未定义的。如果超出页面边界,程序很可能会崩溃,因为超出该地址的内存未被映射。但是您可能会将字节写入内存中您不想写入的位置,这可能会导致各种情况发生,因为它会损坏您的程序正常执行所需的代码或数据。

在这种情况下,您没有写入映射内存之外的内容。您要求 13 个字节,很可能得到 4096(或您系统上的任何一页)。然后将这 13 个字节复制到缓冲区中并打印出来。所以一切都“有效”。