在64位环境中读取32位缓存

时间:2011-03-11 08:33:59

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

我们有很多基于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;
}

3 个答案:

答案 0 :(得分:3)

从行:

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

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

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

事实上,设计文件格式而不是仅仅使用内存转储可以防止出现这种问题(字节顺序,填充,FP格式......)其他问题会出现在您尝试稍微宽泛的可移植性而不仅仅是编写文件的程序 - 填充特别是可能随编译器发布和编译标志而改变。)

答案 1 :(得分:1)

您需要在64位计算机上更改this->values的内存布局(无论可能是哪种类型,您都没有提及这些关键信息),使内存布局与内存布局变得相同。 32位机器。

你可能需要使用像struct packing这样的编译器技巧或类似的东西来做这件事,如果this->values碰巧包含类,那么编译器生成的内部类指针将会有很多痛苦。

BTW,C ++是否有适当的显式大小整数类型呢? #include <cstdint>

答案 2 :(得分:0)

你使用long作为32位数据类型已经犯了错误......至少在UN * X系统上,不是64位(LP64数据模型,int正在使用32位但是long,指针是64位)。

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

在UN * X上,这意味着当迁移到64位时,为了保持代码的可移植性,最好切换到显式大小的int32_t(来自<stdint.h>),以确保您的数据在执行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这样可以外化/内化的测试库。)