pread()中奇怪的行为?

时间:2014-03-19 21:31:55

标签: c linux hard-drive

所以我有一个函数,它从一个设备(通常是硬盘)读取一个偏移量和一个宽度。现在我使用fseeko()和fread()从磁盘读取。但是我喜欢用pread替换它,因为它更简洁。然而,无论如何,pread似乎始终从偏移0读取。

以下是该函数的代码:

只是一些人抬头。 我有

#define _FILE_OFFSET_BITS 64

在我的代码顶部。我也尝试过pread64(),结果相同!

fseeko()与fread(),这就是我想要的!:

uint8_t retrievedata(FILE *fp,uint64_t seekpoint, uint64_t seekwidth) {
    unsigned char     buf[seekwidth];

    if(fseeko(fp,seekpoint,SEEK_SET)==0) {
        if(fread(buf,sizeof buf,1,fp)==1) {
             /* do work with retrieved data */
        }
        else {
            printf("ERROR READING AT: %"PRIu64"| WITH VAL WIDTH: %"PRIu64"\n",seekpoint,seekwidth);
            return 4;
        }
    }
    else {
        printf("ERROR SEEKING AT: %"PRIu64"\n",seekpoint);
        return 3;
    }
}

pread(),无论“寻找点”是什么,它总是从偏移0读取。是:

uint8_t retrievedata(FILE *fp,uint64_t seekpoint, uint64_t seekwidth) {
    unsigned char     buf[seekwidth]

    if (pread(fileno(fp),buf,seekwidth,seekpoint)!=-1) {
        /* do something */
    } else {
        printf("ERROR SEEKING AND/OR READING AT: %"PRIu64"\n",seekpoint);
        return 3;
    }
}

1 个答案:

答案 0 :(得分:3)

启用编译器警告(GCC为-W -Wall),并阅读"Feature Test Macro Requirements for GLIBC"中的man pages部分。

警告表示您错过了所需的另一个宏定义,而man 2 pread手册页告诉您,您还需要的宏定义是

#define _POSIX_C_SOURCE 200809L

获取GNU C库版本2.12或更新版本以正确声明pread()等。

您所看到的行为是由于没有该声明。


我想告诉你我如何看待完整的故事,因为我希望它会通过阅读和理解man pages来确切地告诉你如何以及多少可以节省汗水和努力,更重要的是,通过< strong>启用和解决编译器警告。

我知道你认为你现在没有时间去做所有这些事情(可能更晚,对吧?),但你错了:这是保存的最佳途径之一时间,编写C for Linux(或类似POSIX的系统)。

我自己始终在GCC中使用-W -Wall。它只是更容易找到问题的原因所在。现在,你专注于主要症状,而且由于编程不是政治(但是),它不会让你到任何地方。

所以,全貌:

fread()返回读取的元素数。在您的情况下,您读取了一个seekwidth字节的元素,并验证是否正确读取了一个元素。

pread()返回读取的字节数。您的第二个代码段尝试从偏移seekwidth开始读取seekpoint个字节,但是您不检查实际读取了多少数据,只检查是否发生了错误。

以下是我认为发生在你身上的事情:

  1. 由于glibc 2.12已声明#define _POSIX_C_SOURCE 200809L函数原型声明

  2. ,因此省略了#include <unistd.h>声明(pread()之前)所需的声明
  3. 您正在编译而没有警告,或者您忽略“隐含声明函数`pread`”警告

  4. 您正在使用-m32 GCC选项

  5. 在32位架构或32位架构上进行编译

    如果没有函数原型,编译器会假定pread()函数的所有参数都是整数 - 因此是32位 - 并且只提供32位 seekpoint 值为pread()。三个第一个参数(int,指针和size_t)都是32位,但第四个参数,文件偏移量是64位。 (请记住,您使用#define _FILE_OFFSET_BITS 64告诉您的C库。)

    这意味着64位文件偏移pread()接收,是垃圾。 (这取决于字节顺序 - little-endian或Intel-like,或big-endian或Motorola / PowerPC / ARM-like - 以及特定架构二进制接口(ABI)如何将第四个参数传递给函数, 值是如何乱码。)

    在这种情况下,我相信您的目标是32位英特尔架构,其中pread()实际接收的值(为乱码)通常为正且大于文件大小,因此pread()返回0:“过去文件结束,没有更多数据要读”

    您的代码忽略了(因为它不是-1),而是假设它成功读取数据。您看到的数据很可能来自之前的读取 - pread()如果返回0,则不会修改缓冲区。

    (还有其他可能的情况变体;甚至有些pread()总是收到零作为(乱码)偏移量。它实际上并不重要,因为在所有情况下都有正确的函数原型修复问题。你确实需​​要检查pread()返回值,因为不能保证它实际上会读取你请求的字节数。它经常这样做,是的;但是没有保证,所以请不要做任何毫无根据的假设。)