【问题标题】:Why is stat::st_size 0 for devices but at the same time lseek defines the device size correctly?为什么设备的 stat::st_size 0 但同时 lseek 正确定义了设备大小?
【发布时间】:2019-08-05 11:06:04
【问题描述】:

我注意到当我使用open + lseek 查询设备的大小时,一切正常,但是当我stat 设备时,我得到的是零而不是实际的设备大小。设备很干净,没有任何文件系统,设备的第一个字节以“1234567890ABC”之类的文本开头。怎么了?

代码:

#include <sys/stat.h>
#include <dirent.h>

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

【问题讨论】:

  • 欢迎来到“统计信息不同于寻求+告诉信息”。
  • 我不相信使用PRIu64 打印尺寸,除非您将尺寸转换为uint64_t。也就是说,如果出现问题,您可能不会得到零。

标签: c linux posix stat


【解决方案1】:

魔鬼在细节中...首先,Unix 设计的基本原则是:一切都是文件,很好explained here

第二个是 stat(2) 调用为您提供 inode 存储在文件系统上的关于大小为零的 device-special 文件 的统计信息(想想其中lstat(2))。如果您的块设备上有一个文件系统,您可以使用statfs(2)getfsstat(2)statvfs(2) 以独立于文件系统/设备的方式获取有关它的信息。

处理特殊文件(通常位于 /dev 中)一直是系统特定的,手册页位于第 4 部分。因此,如果您想直接操作设备,您应该阅读那里的详细信息。例如,在 Linux 中,man 4 hd 将向您展示如何以编程方式与 IDE 块设备进行交互。而man 4 sd 将告诉您如何与 scsi 光盘等进行交互。

第三件事,系统调用不应该在功能上不一致他们的局限性。

希望这有帮助。

【讨论】:

  • 它有,我的印象是现有的 3 个答案相互补充。
  • @Jean-FrançoisFabre 他们似乎没有 :)
  • @Jean-François Fabre Masud 所有答案都是正确且相互补充的。但这种想法似乎很关键:stat 与 inode 一起使用,它提供来自现有 FS 的信息,而不是设备文件。但是在另一端设备文件有大小,有开始和结束(在块设备的情况下),因此 lseek 可以正常工作并返回正确的设备大小。正如艾哈迈德所说的“一切都是文件”,因此我在无法使用的地方使用了 stat 。 “魔鬼在细节中……”——准确地描述了这种情况。谢谢你们!
【解决方案2】:

来自这个Unix Stack Exchange 问题:

设备文件本身不是文件。它们是在类 Unix 操作系统中使用设备的 I/O 接口。它们不使用磁盘空间,但是,它们仍然使用 stat 命令报告的 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 并从中读取。你也可以寻求它。它允许在最低级别读取磁盘,因此需要查找(这就是它起作用的原因,为什么它不像任何“真实”文件一样返回新位置?)。

根据this other UnixSE answer,你可以通过读取这个/dev/sda/size文件得到设备大小。

【讨论】:

    【解决方案3】:

    /dev/sda 等“设备”的长度不是由the 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 同样does not specify that stat() 应返回磁盘设备的大小:

    st_size

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

    【讨论】:

      【解决方案4】:

      在 Linux 上,记录可打开的原始磁盘设备大小的方法是使用 BLKGETSIZE ioctl。请参阅 sd(4) 联机帮助页。

      请注意,这将返回设备的大小,以 sectors 为单位。您可能认为,对于以字节为单位的大小,您必须乘以BLKSSZGET ioctl 返回的值,但if I'm reading the source code correctly,实际上无论BLKSSZGET 返回什么,您都必须乘以 512。

      【讨论】:

        【解决方案5】:

        lseek 是 C 的 fseek 的主干,因此它具有相似的语义,匹配 fseek - 并且与 Unix API 的其他领域完全分离。从出处来看,您会期望 lseek 的行为类似于文件句柄处理 fseek,而 fseek 是一个 C 库接口,它不是 Unix 特定的。

        stat 是特定于 Unix 的,并且做自己的事情。如果您考虑出处,这是一个合理的预期差异。当然,问题在于 C API 的类型模型非常弱,因为 C 距离真正的类型安全还差一步。

        为什么这很重要?因为,从根本上说,seekable_sizefile_object_size 是两个根本不同的概念,并且需要不同的类型——即使 C++ 标准库也弄错了。

        但是,虽然在 C++ 和现代编译器中,它现在是一个完全无缘无故的遗留缺陷,但在 C 中确实没有办法在不影响性能和代码可读性的情况下有效地将整数包装成不兼容的类型。因此,您最终会使用 offs_tlong 之类的东西来表示完全不兼容的概念。这就是混淆的根源:仅仅因为您从与文件相关的函数中获得与大小相关的数字并不意味着该数字将具有相同的含义。并且含义通常以类型捕获...... long 固有的唯一含义是“嘿,我是一个数字,你可以和我一起做数字的事情”...... :(

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-16
          • 1970-01-01
          相关资源
          最近更新 更多