为什么是统计:: st _ size 0 为设备, 但同时 lseek 正确地定义了设备大小?

共5个回答,已解决, 标签: c linux posix stat

我注意到, 当我使用 + 查询设备的大小 openlseek , 一切都没问题, 但当 stat 我使用设备时, 我得到的是零, 而不是真正的设备大小。该设备是干净的, 没有任何文件系统和设备的第一个字节开始与一些文本, 如 "1234567890ABC"。怎么了?

验证码:

#include
#include

bool
GetFileSize(const char* pPath, uint64_t& Size)
{
    pPath = "/home/sw/.bashrc";
    pPath = "/dev/sda";

    struct stat buffer;
    if (stat(pPath, &buffer))
    {
        printf("Failed to stat file. Error: %s. FilePath: %s\n", strerror(errno), pPath);
        return false;
    }

    printf("File size by stat: %" PRIu64 " WTF?\n", buffer.st_size);

    //
    // Note: It's strange, but stat::st_size from the stat call is zero for devices
    //

    int File = open(pPath, O_RDONLY);
    if (File < 0)
    {
        printf("Failed to open file. Error: %s. FilePath: %s\n", strerror(errno), pPath);
        return false;
    }

    long off = lseek(File, 0, SEEK_END);
    if (off == (off_t)-1)
    {
        printf("Failed to get file size. Error: %s. FilePath: %s\n", strerror(errno), pPath);
        close(File);
        return false;
    }
    close(File);

    printf("File size by lseek: %" PRIu64 "\n", off);
    fflush(stdout);

    Size = off;
    return true;
}

输出:

File size by stat: 0 Huh?
File size by lseek: 34359738368

如果我对常规文件使用 stat, 那么一切都可以 (用 "/dev/sda" 注释掉行):

File size by stat: 4019 Huh?
File size by lseek: 4019
第1个答案(采用)

魔鬼在细节中..。首先, Unix 设计的基本原则是: 一切都是一个文件, 很好地解释了这里。

第二种情况是, stat(2) 调用为您提供存储在文件系统上的有关大小为零的设备特殊文件inode统计信息 (将其视为 lstat(2) )。如果您有一个包含文件系统的块设备, 则可以使用 statfs(2)getfsstat(2) statvfs(2) 以独立于文件系统设备的方式获取有关该文件系统的信息。

处理特殊文件 (通常驻留在/dev 中) 始终是系统特定的, 而手动页驻留在第 4 节中。 所以, 如果你想直接操纵一个设备, 你应该阅读那里的细节。例如, 在 Linux man 4 hd 中, 您将向您展示如何以编程方式与 ide 块设备进行交互。而 man 4 sd 将给你如何与 scsi 光盘等进行交互。

第三, 系统调用不应该是不一致的, 在他们的功能知道他们的局限性。

希望这有所帮助。

第2个答案

从这个Unix 堆栈交换问题:

设备文件本身不是文件。它们是一个 ito 接口, 用于在类似 unix 的操作系统中使用这些设备。它们在磁盘上不使用任何空间, 但是, 它们仍然使用统计命令报告的 inode:

$ stat /dev/sda
      File: /dev/sda
      Size: 0               Blocks: 0          IO Block: 4096   block special file
Device: 6h/6d   Inode: 14628       Links: 1     Device type: 8,0

这解决了 stat 这个部分。

你可以在这个 "文件" 中寻找的事实是不相关的。这不是一个真正的文件, 但你可以 open 从它阅读。你也可以去找它。它允许在最低的级别读取磁盘, 因此需要查找 (这就是它起作用的原因, 为什么它不像任何 "真实" 文件一样返回新位置?)

根据另一个 UnixSE 的答案, 您可以通过读取此文件来获取设备大小 /dev/sda/size

第3个答案

"设备" 的长度, 如 /dev/sda posix struct stat 未指定的:

off_t st_size       For regular files, the file size in bytes.

                    For symbolic links, the length in bytes of the
                    pathname contained in the symbolic link.

                    For a shared memory object, the length in bytes.

                    For a typed memory object, the length in bytes.

                    For other file types, the use of this field is
                    unspecified.

因此, POSIX 不需要磁盘设备的 "大小"。

同样, Linux也没有 stat() 指定应返回磁盘设备大小:

st_size

此字段以字节为单位给出文件的大小 (如果它是常规文件或符号链接)。 符号链接的大小是它所包含的路径名的长度, 而不终止 null 字节。

第4个答案

在 Linux 上, 获取可以打开的原始磁盘设备大小的文档化方法是使用 BLKGETSIZE ioctl。 请参阅 sd(4) 管理页。

请注意, 这将返回扇区中设备的大小。 您可能会认为, 对于以字节为单位的大小, 您必须乘以 BLKSSZGET ioctl 返回的值, 但如果我正确地读取源代码, 实际上无论返回什么, 您都必须乘以 512 BLKSSZGET

第5个答案

lseek是 C 的主干 fseek , 因此它具有相似的语义、匹配 fseek 性, 并且与 UNIX api 的其他区域完全分离。从外观上看, 您可能会 lseek 像文件处理一样 fseek , fseek 并且是一个 c 库接口, 它是在不特定于 unix 的情况下出现的。

stat不过, 是 unix 特定的, 并且做自己的事情。如果你想到产地, 这是一个合理的差异。当然, 问题是, C Api 有非常弱的类型模型, 因为 C 距离使真正的类型安全成为可能还有一步之遥。

为什么这很重要?因为, 从根本上说,可查找的 \ size 和_ file _ object _ size是两个根本不同的概念, 并且需要不同的类型--即使是 c++ 标准库也会出错。

但是, 虽然在 C++ 和现代编译器中, 它现在是一个完全没有意义的遗留缺点, 但在 C 中, 实际上无法在不影响性能和代码可读性的情况下高效地将整数包装到不兼容的类型中。因此, 你最终会得到类似 offs_tlong 被用于完全不兼容的概念的东西。而这也是造成困惑的原因: 仅仅因为你从一个与文件相关的函数中得到了一个与大小相关的数字, 并不意味着这个数字就会有同样的含义。而意义通常是在类型中捕捉到的.....。一个 long 固有的唯一含义是 "嘿, 我是一个数字, 你可以和我一起做数字的事情"......:(

相关问题

我投的是 malloc 的结果吗? 为什么是统计:: st _ size 0 为设备, 但同时 lseek 正确地定义了设备大小? 允许在易失对象上进行优化 UTF-8 一路通过 错误: TCP 提供程序: 错误代码 0x2746。在 linux 下通过终端进行 Sql 设置时 getline () vs. fgets (): 控制内存分配