当我们 fseeko() 达到 /dev/zero 上的最大偏移量然后继续阅读时,它是否定义了行为?

Is it defined behavior when we fseeko() to the maximum offset on /dev/zero and then continue reading?

提问人:Stefan Rickli 提问时间:11/13/2023 更新时间:11/13/2023 访问量:49

问:

在使用 DD 用零覆盖我的硬盘驱动器时,我想知道在程序中读取的限制。具体来说,如果我们试图“最大化”读取位置会发生什么?/dev/zero

当我们用来将文件指针设置为最大可能的偏移量,然后继续读取字节时,它是否定义了行为?此外,行为是否特定于操作系统和编译器?fseeko()/dev/zero

我已经在 OnlineGDB 上运行了一个小测试,我发现虽然在使用长 int(64 位)的最大值时不会返回错误,但将返回 4095 作为我可以使用达到的最高文件偏移量。它只是简单地绕到零,没有错误。但是当我们从 start_offset=4095 读取非零数量的字节时,返回更高的数字。这是此类特殊文件的正常行为吗?fseeko()ftell()fseeko()ftell()

这是自己尝试一下的代码(我让 ChatGPT 给我做了一个脚手架,我针对这里的问题进行了调整):

#include <stdio.h>

// Defines the highest number that off_t can be on your platform, usually 9223372036854775807 on 64-bit systems
// Definition taken from https://stackoverflow.com/a/72019392/2721597
#define OFF_MAX ((((off_t)1 << (sizeof(off_t)*8 - 2)) - 1) * 2 + 1)

int main()
{
    FILE* fp;
    const int num_bytes = 1; // Number of bytes to read, try also 0
    char buffer[num_bytes];

    fp = fopen("/dev/zero", "rb");
    if (fp == NULL) {
        perror("Error opening /dev/zero");
        return 1;
    }

    off_t start_offset = OFF_MAX; // Try also 4095, 4096
    if (fseeko(fp, start_offset, SEEK_SET) != 0) {
        perror("Error on fseeko()");
        fclose(fp);
        return 1;
    }

    size_t bytes_read = fread(buffer, 1, num_bytes, fp);
    if (bytes_read < num_bytes) {
        if (feof(fp)) {
            printf("End of file reached.\n");
        }
        if (ferror(fp)) {
            perror("Error reading from /dev/zero");
        }
        fclose(fp);
        return 1;
    }


    printf("start_offset = %ld\n", start_offset);
    printf("num_bytes (bytes_read): %d (%ld)\n", num_bytes, bytes_read);
    printf("ftell: %ld\n", ftell(fp));

    fclose(fp);

    return 0;
}
C Linux 文件-IO

评论

0赞 Craig Estey 11/13/2023
虽然一个特定的impl可能会保留一个当前的位置,但为了提供一些理智的搜索,它真的无关紧要,因为它实际上更像是一个顺序字节流,无论文件位置如何,它总是返回零(并且没有EOF或限制)。4096 是页面大小。所以,它可能以这种方式呈现。但是,它在返回字节时会忽略文件位置。只有读取长度很重要。而且,我会使用较低级别的函数(例如,,)而不是 stdio 版本,因为它们可能会掩盖事情。4096 是默认的 stdio 流缓冲区长度,因此.../dev/zeroopenlseekread
0赞 0___________ 11/13/2023
Undefined Behaviour 是 C 语言的东西,而 C 语言对此一无所知,那么你的问题就无法回答。由于 Linux 也没有精确地指定很多东西,因此它是 100% 如何在特定的发行版和内核中实现的。这个问题没有答案。/dev/zero
0赞 Craig Estey 11/13/2023
/dev/zero并且无论您指定什么位置,都不会返回错误。对于这两个设备,将始终返回 0。并且,on 将始终返回 0(立即 EOF)。 将填充缓冲区,并始终用字节填充缓冲区。当然,会因错误的缓冲区地址等返回错误。这是因为顶层将在调用 or 设备特定函数之前检查缓冲区地址/大小限制。/dev/nulllseeklseekread/dev/null/dev/zerocountreadread/dev/null/dev/zero.read
0赞 Eric Postpischil 11/13/2023
@0___________:Re “Undefined Behaviour is C language thing...”:不,不是;行为的规范或定义通常是一个工程问题。C 标准使用“未定义的行为”,在标准中具有特定的含义,但这个问题根本没有使用该术语。
1赞 Eric Postpischil 11/13/2023
@CraigEstey: Re “ and won't return an error on ...”:该问题显示了通过标准 C 库流使用的代码,因此可以想象流实现可能与 或 的“无限流”交互不佳。因此,当流函数达到其最大偏移量但相关设备仍提供或接受数据时,Linux 上使用的 C 库的实现是否会遇到一些问题,这是一个合理的问题。/dev/zero/dev/nulllseek/dev/zero/dev/zero/dev/null

答: 暂无答案