为什么设备的stat :: st_size为0,但是lseek同时定义了设备的大小?

时间:2019-03-14 14:00:35

标签: c linux posix stat

我注意到,当我使用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

5 个答案:

答案 0 :(得分:12)

细节在于魔鬼...对于初学者来说,Unix设计的基本原理是:一切都是文件,很好explained here

第二个是stat(2)调用为您提供存储在文件系统中的 inode 统计信息,该统计信息有关大小为零的设备专用文件(请考虑其中为lstat(2))。如果您的块设备上有文件系统,则可以使用statfs(2)getfsstat(2)statvfs(2)以与文件系统/设备无关的方式获取有关该文件的信息。

处理特殊文件(通常位于/ dev中)始终是系统特定的,并且手册页位于第4节中。因此,如果您要直接操作设备,则应阅读那里的详细信息。例如,在Linux man 4 hd中,将向您展示如何与IDE块设备进行编程交互。而man 4 sd将为您提供如何与scsi光盘等进行交互的方法。

第三件事,系统调用不应在功能上有所矛盾。 NOR 其局限性。

希望这有所帮助。

答案 1 :(得分:9)

来自这个Unix Stack Exchange问题:

  

设备文件本身不是文件。它们是一个I / O接口,用于在类似Unix的操作系统中使用设备。他们不使用磁盘空间,但是,仍然使用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文件来获取设备大小。

答案 2 :(得分:6)

the POSIX struct stat未指定诸如/dev/sda之类的“设备”的长度:

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

     

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

答案 3 :(得分:0)

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

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

答案 4 :(得分:-1)

lseek是C语言fseek的骨干,因此它具有相似的语义,与fseek相匹配-并与Unix API的其他领域完全分离。在资源方面,您希望lseek像获取文件句柄fseek一样,并且fseek是一个C库接口,而该接口并不是特定于Unix的。 / p>

stat是特定于Unix的,并且可以完成自己的工作。如果您考虑出处,这是一个合理的差异。当然,问题在于,C API具有非常弱的类型模型,因为C距离实现真正的类型安全仅一步之遥。

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

但是在C ++和现代编译器中,这现在是一个完全免费的遗留缺点,而C语言中实际上没有办法在不影响性能和代码可读性的情况下将整数有效地包装为不兼容的类型。这样一来,您会得到offs_tlong之类的东西用于完全不兼容的概念。这就是造成混淆的原因:仅仅因为您从文件相关的函数中获得了与大小相关的数字,并不意味着该数字将具有相同的含义。含义通常是按类型捕获的……long固有的唯一含义是“嘿,我是一个数字,你可以和我一起做数字事情”…:(