对memcpy& amp的误解MMAP

时间:2017-11-12 14:12:46

标签: c mmap memcpy

我需要在进程之间使用共享内存,我找到了示例代码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在这里重叠吗?我很感激任何简短的解释。

提前致谢。

2 个答案:

答案 0 :(得分:6)

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

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

  3. sizeof(shmem)提供shmem的大小,这是一个指针。因此它提供了指针的大小,在现代系统上通常为4或8个字节。它没有提供shmem指向的内容的大小。

  4. 相反,在sizeof(msg)中,msg是一个数组,而不是指针,因此sizeof(msg)确实提供了数组的大小,正如您可能想要的那样。

  5. memcpy(shmem, msg, sizeof(msg));将13个字节(msg的大小)复制到shmem。那十三个字节是“hello world!”,最后是一个空字符(值0)。除了您传递的length参数之外,memcpy无法知道源或目标的长度。因此它复制sizeof(msg)个字节。它并不局限于shmem指向的内存大小。你的工作是传递正确的长度。

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

    在这种情况下,您没有写入映射内存。你问了13个字节,可能给了4096(或者你系统上的任何一个页面)。然后你将这13个字节复制到缓冲区并打印出来。所以一切都“奏效了。”

答案 1 :(得分:0)

您的代码违反了mmap()的合同,在1个字节大小的请求内存映射中写入了超过1个字节。

但是,正如您所发现的,它有时可能适用于某些系统。这可能是因为一页的大小(在存储器映射中)是例如4 KB。所以也许映射比请求的要大。但是,你没有权利像你一样使用它。

所以,停止这样做。

您询问是否应该是编译错误。答案是否定的:编译器没有像mmap()这样的每个库例程的特殊情况。它不知道size的{​​{1}}参数意味着返回的指针仅对那么多字节有效。静态分析器可能会解决这个问题,但编译器通常不会这样做。