【问题标题】:Read 32bit caches in 64bit environment在 64 位环境下读取 32 位缓存
【发布时间】:2011-03-11 08:33:59
【问题描述】:

我们有很多构建在 32 位机器上的缓存,现在我们必须在 64 位环境中读取这些缓存。 当我们想要打开读取缓存文件时,我们会遇到分段错误。

复制缓存需要数周时间,所以我想知道如何在 64 位机器上处理我们的 32 位缓存文件。

这是我们用来读写缓存的代码:

bool IntArray::fload(const char* fname, long offset, long _size){
  long size = _size * sizeof(long);

  long fd = open(fname, O_RDONLY);
  if ( fd >0  ){
    struct stat file_status;
    if ( stat(fname, &file_status) == 0 ){
      if ( offset < 0 || offset > file_status.st_size ){
        std::__throw_out_of_range("offset out of range");
        return false;
      }
      if ( size + offset > file_status.st_size ){
        std::__throw_out_of_range("read size out of range");
        return false;
      }

      void *map = mmap(NULL, file_status.st_size, PROT_READ, MAP_SHARED, fd, offset);
      if (map == MAP_FAILED) {
        close(fd);
        std::__throw_runtime_error("Error mmapping the file");
        return false;
      }

      this->resize(_size);
      memcpy(this->values, map, size);

      if (munmap(map, file_status.st_size) == -1) {
        close(fd);
        std::__throw_runtime_error("Error un-mmapping the file");
        return false;
        /* Decide here whether to close(fd) and exit() or not. Depends... */
      }

      close(fd);
      return true;
    }
  }
  return false;
}
bool IntArray::fsave(const char* fname){
  long fd = open(fname, O_WRONLY | O_CREAT, 0644); //O_TRUNC
  if ( fd >0  ){
    long size = this->_size * sizeof(long);
    long r = write(fd,this->values,size);
    close(fd);

    if ( r != size ){
        std::__throw_runtime_error("Error writing the file");
    }
    return true;
  }
  return false;
}

【问题讨论】:

  • 这些缓存包含什么?
  • 如果您提供了正在读取的实际数据类型和您正在使用的平台,那将会很有趣。在一些 64 平台中,long 是 32 位,而在其他平台中是 64 位,这可以解释这个问题。附带说明一下,您不应该直接调用以__ 开头的方法,因为这些方法是为实现保留的,可以随时更改。如果你想抛出std::runtime_error,就这样做:throw std::runtime_error("my_error")

标签: c++ 64-bit 32-bit


【解决方案1】:

从行:

long size = this->_size * sizeof(long);

我假设values 指向long 的数组。在除 Widnows 之外的大多数操作系统下,long 在 32 位构建中是 32 位,在 64 位构建中是 64 位。

您应该将文件读取为 32 位值的转储,例如 int32_t,然后复制它。并且可能对您的文件进行版本控制,以便您知道在阅读时应用哪种逻辑。

事实上,设计一种文件格式而不是仅仅使用内存转储将防止这类问题(字节序、填充、FP 格式,...如果您尝试更广泛的可移植性,还会出现其他问题不仅仅是编写文件的程序——尤其是填充可能会随着编译器版本和编译标志而改变。

【讨论】:

  • 或者只是将代码更改为显式使用int32_t 代替values,并确保编译器密集地打包那些int32_t 而没有填充。这应该适用于 32 位和 64 位机器上的旧文件。然后只是如何在 C++ 中可移植地使用 int32_t 类型的问题仍然存在,因为 AFAIR 只有 C 在标准中定义了 stdint.h,C++ 仍然必须迎头赶上。
  • 除非使用 64 位的原因是更高的容量,这意味着 int32_t 现在不能保存这些值。 (在 unix 下,&lt;stdint.h&gt; 是 POSIX 所要求的,所以它非常便携。)
  • 谢谢您,我们目前正在使用您的回答修改为 int32_t。
【解决方案2】:

您需要在 64 位机器上更改 this-&gt;values 的内存布局(无论是哪种类型,您都没有提及关键信息),使内存布局与 32 位使用的内存布局相同机器。

您可能需要使用编译器技巧,如结构打包或类似的东西来做到这一点,如果this-&gt;values 恰好包含类,那么编译器生成的内部类指针会给您带来很多麻烦。

顺便说一句,C++ 是否有适当的明确大小的整数类型? #include &lt;cstdint&gt;?

【讨论】:

    【解决方案3】:

    您已经犯规了使用long 作为 32 位数据类型......至少在 UN*X 系统上,64 位不是这种情况(LP64 数据模型,int 是 32 位但 @987654323 @ 和指针是 64 位的)。

    在 Windows64(IL32P64 数据模型,intlong 32 位,但指针 64 位)上,您的代码以 sizeof(long) 为单位执行大小计算并直接从映射文件执行 memcpy() 到对象化数组将居然继续工作……

    在 UN*X 上,这意味着当迁移到 64 位时,为了保持代码的可移植性,最好切换到显式大小的 int32_t(来自 &lt;stdint.h&gt;),以确保您的数据结构布局在执行 32 位和 64 位目标编译时保持不变。

    如果您坚持保留long,那么您必须将数组的内部化/外部化从简单的memcpy() / write() 更改为不同的处理方式。没有错误处理(上面已经有了),::fsave() 方法看起来像这样,而不是像你一样使用write()

    long *array = this->values;
    int32_t *filebase =
        mmap(NULL, file_status.st_size, PROT_WRITE, MAP_SHARED, fd, offset);
    
    for (int i = 0; i < this->_size; i++) {
        if (array[i] > INT32_MAX || array[i] < INT32_MIN)
            throw (std::bad_cast);  // can't do ...
        filebase[i] = static_cast<int32_t>(array[i]);
    }
    
    munmap(filebase, file_status.st_size);
    

    对于::fload(),您将执行以下操作而不是memcpy()

    long *array = this->values;
    int32_t *filebase =
        mmap(NULL, file_status.st_size, PROT_READ MAP_SHARED, fd, offset);
    
    for (int i = 0; i < this->_size; i++)
        array[i] = filebase[i];
    
    munmap(filebase, file_status.st_size);
    

    注意:正如已经提到的,如果您有比简单数组更复杂的东西,这种方法将失败,因为除了数据类型大小差异之外,可能还有不同的对齐限制和不同的填充规则。对您来说似乎并非如此,因此只有在考虑扩展此机制时才记住这一点(不要 - 使用经过测试的库,如 boost::any 或 Qt::Variant 可以外部化/内部化)。

    【讨论】:

      猜你喜欢
      • 2016-12-08
      • 2018-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-30
      • 2013-03-15
      • 1970-01-01
      • 2016-06-01
      相关资源
      最近更新 更多