【问题标题】:transferring binary files between systems在系统之间传输二进制文件
【发布时间】:2010-06-15 16:13:54
【问题描述】:

我正在尝试在 2 个 UNIX 集群之间传输我的文件,数据是二进制形式的纯数字(双精度向量)。不幸的是,其中一个系统是IBM ppc997,另一个是AMD Opteron,看起来这些系统中的二进制数格式不同。

到目前为止,我已经尝试了 3 种方法:

1- 将我的文件更改为 ASCII 格式(即在文本文件中的每一行保存一个数字),将它们发送到目的地并再次将它们更改为目标系统上的二进制文件(它们都是 UNIX,没有行尾性格差异??!)

2- 将纯二进制文件发送到目的地

3-使用uuencode将它们发送到目的地并解码它们

不幸的是,这些方法中的任何一个都不起作用(我在目标系统中的代码会产生垃圾,虽然它在第一个系统上工作,但我 100% 确定代码本身是可移植的)。我不知道我还能做什么?你有什么主意吗?我不是专业人士,请不要使用计算机科学家的术语!

而且:我的代码是 C 语言,所以二进制是指内存和硬盘之间的一对一映射。

谢谢

【问题讨论】:

    标签: c unix binary transfer


    【解决方案1】:

    如果您将内存内容写入文件,则代码不是 100% 可移植的。

    你需要一种叫做序列化的东西。好的,计算机科学术语,但它基本上意味着您获取数据并将其转换为定义良好且记录在案的字节序列,稍后可以由相同或另一个程序将其读回内存。此字节序列与架构和平台无关。

    大多数 Unix 环境已经带有 XDR 实现,它提供了数据序列化的例程。

    一个简单的例子,将 4 doubles 编码为 stdout(你可以使用 shell 重定向,或者使用 fopen() 打开文件而不是 stdout):

    XDR xdrs;
    double data[4] = { 1.0, 255.41, -357.1, 123.4 };
    int i;
    
    xdrstdio_create(&xdrs, stdout, XDR_ENCODE);
    for (i = 0; i < 4; i++)
        xdr_double(&xdrs, &data[i]);
    

    现在,要(从标准输入)取回这些双精度并打印它们:

    XDR xdrs;
    double data;
    int i;
    
    xdrstdio_create(&xdrs, stdin, XDR_DECODE);
    for (i = 0; i < 4; i++) {
        xdr_double(&xdrs, &data);
        printf("%g\n", data);
    }
    

    您可以使用 XDR 对复杂结构进行编码和解码。这是向文件发送四个双精度的一种非常愚蠢的方式,通常您应该使用 xdr_array() 来读取/写入某些数据类型的数组。在保存和加载文件时,必须以相同的顺序执行相同的命令。其实你可以使用rpcgen自动生成C结构体及其对应的xdr函数。

    【讨论】:

      【解决方案2】:

      方法 1 应该有效。只需创建一个值为 1、2、...、10 的测试向量并将其发送过去。您可以读取创建的 ascii(因此您可以验证“导出”),因此还要检查重新读取文件的“导入”步骤。您可能会以这种方式失去精确度,但它应该可以让您操作。

      方法 2 将在您使用处理不同字节顺序的库(例如 XDR)后起作用。这些事情曾经是“很久以前”的更大问题,并且有解决方案。这是例如R 这样的系统如何允许您在架构之间共享二进制文件。

      方法 3 不需要,除非你在传输文件时做了一些很尴尬的事情。

      【讨论】:

      • 谢谢,但似乎二进制文件依赖于架构,我不是计算机科学家,但我记得我的本科生有不同的方法来保存浮点数的符号。 XDR 现在可以工作吗?我在哪里可以找到它?
      • XDR 是 Sun 的一个编码库,请尝试使用谷歌搜索“xdr 库”,这让我获得了很多点击。 Ascii 导出(方法 1)不需要它。将一组已知数字导出为 ascii,检查文件。从该文件导入,再次检查。当你有这个工作时,在你的真实数据上使用它。
      【解决方案3】:

      提供的细节很少。尽我所能回答。

      ..其中一个系统是IBM ppc997,另一个是AMD Opteron

      以前的系统一般 (*) 使用大端表示,后来 - 小端表示。 Read this.

      (*) 这取决于操作系统。 IBM 的 POWER CPU 可以同时进行小端和大端,但实际上运行在它们上的操作系统都没有使用小端模式。

      通常,对于二进制表示,人们会选择一种字节序并与它一起进行二进制表示。对于网络东西,大端数字表示是一种规范。

      这意味着所有这样做的地方:

      /* writing to binary */
      int a = 1234;
      write(fd,&a,sizeof(a));
      /* reading from binary */
      int x;
      read(fd,&x,sizeof(x));
      

      应该转换成这样的:

      /* writing to binary */
      int a = htonl(1234);
      write(fd,&a,sizeof(a));
      /* reading from binary */
      int x;
      read(fd,&x,sizeof(x));
      x = ntohl(x);
      

      另一种方法是将字节序指示符(例如,编写魔法并在另一侧检查它:MAGIC = 0x12345678 v. MAGIC = 0x78563412)与二进制数据一起保存,并且仅在字节序不同时应用转换。虽然这种方法不太优雅,并且没有我所知道的真正优势。

      【讨论】:

      • 其实,对于网络的东西,big-endian 是常态。它甚至被称为“网络字节顺序”。由于 OP 使用的是浮点类型,因此它不仅仅是字节序。
      • @caf,谢谢,愚蠢的错字。我通常只使用 big-endian(主要用于标准 ntoh/hton 函数),因此我一直混合字节序。
      【解决方案4】:

      解决方案 2 和 3 通常不起作用,因为不同的处理器可能使用不同的数字内部表示。对于整数,而不是浮点数/双精度数,您可以使用只处理不同机器的字节顺序的东西。 浮点表示要复杂得多,您必须详细查找不同架构使用的表示。但是对于 double 来说,例如,对精度的要求只有最低限度,并且您可能会发现自己处于必须截断为两者的较小表示的情况。 这些问题与您使用的操作系统(无论是否为 Unix)没有太大关系,而是与硬件的喜好有关。

      【讨论】:

      • 谢谢,那没有别的办法了吗?您知道为什么解决方案 1 不起作用吗?无论如何谢谢你
      • 解决方案 1 通常应该可以工作,但相对昂贵(时间、带宽)。为什么您的特定实现没有做到这一点,我们不知道,您没有向我们提供详细信息。
      【解决方案5】:

      所有支持 IEEE 754 的处理器对浮点数(技术上称为单数)和双数具有相同的二进制表示。唯一的区别在于处理器的字节顺序。

      因此,IBM PPC 和 AMD Opteron 之间唯一的不兼容应该是双精度的字节序。

      当您将双打从磁盘字节交换到内存时,不要这样做

      double swap(double a); // THIS IS NEVER THE RIGHT THING TO DO.
      

      按值传入 double 可能会通过浮点寄存器传入。由于并非所有位组合都是有效的双精度,因此处理器可能会静默地将双精度转换为 NaN,它可能具有与传入的值不同的位表示。这更可能发生在具有相反端序的有效双精度. (更详细的解释请参见here。)

      换句话说,将要字节交换的双精度作为指针或字符数组传递。 (字符数组应该是最好的选择。)

      【讨论】:

      • 代码很有趣,但据我了解,这只是用于交换-取消交换,很好,但我需要在目标系统上使用交换的数据,而在文章中它已经进行了合理的解释,并且您在这里也提到了将这些代码用于此目标是错误的。感谢任何方式
      • @tim,我的意思是你可以字节交换你想要的任何东西,只是不要以相反的字节顺序将双打作为双打传递;将它们作为字符数组传递。所以继续进行字节交换。
      • 哦,谢谢,不过很复杂,我用XDR代替。再次感谢
      猜你喜欢
      • 2012-10-26
      • 2010-12-26
      • 2011-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-01
      相关资源
      最近更新 更多