【问题标题】:Converting statvfs to percentage free correctly正确将 statvfs 转换为百分比
【发布时间】:2023-03-28 22:40:01
【问题描述】:

我有一个非常简单的测试程序,可以打印出以下数字。

int main(int argc, char* argv[])
  struct statvfs vfs;
  statvfs(argv[1], &vfs);
  printf("f_bsize (block size): %lu\n"
       "f_frsize (fragment size): %lu\n"
       "f_blocks (size of fs in f_frsize units): %lu\n"
       "f_bfree (free blocks): %lu\n"
       "f_bavail free blocks for unprivileged users): %lu\n"
       "f_files (inodes): %lu\n"
       "f_ffree (free inodes): %lu\n"
       "f_favail (free inodes for unprivileged users): %lu\n"
       "f_fsid (file system ID): %lu\n"
       "f_flag (mount flags): %lu\n"
       "f_namemax (maximum filename length)%lu\n",
       vfs.f_bsize,
       vfs.f_frsize,
       vfs.f_blocks,
       vfs.f_bfree,
       vfs.f_bavail,
       vfs.f_files,
       vfs.f_ffree,
       vfs.f_favail,
       vfs.f_fsid,
       vfs.f_flag,
       vfs.f_namemax);

       return 0;
    }

打印出来:

f_bsize (block size): 4096
f_frsize (fragment size): 4096
f_blocks (size of fs in f_frsize units): 10534466
f_bfree (free blocks): 6994546
f_bavail free blocks for unprivileged users): 6459417
f_files (inodes): 2678784
f_ffree (free inodes): 2402069
f_favail (free inodes for unprivileged users): 2402069
f_fsid (file system ID): 12719298601114463092
f_flag (mount flags): 4096
f_namemax (maximum filename length)255

df 为根 fs 打印出来:

Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda5             42137864  14159676  25837672  36% /

但这就是我感到困惑的地方。

25837672+14159676 != 42137846(实际上是39997348)

因此,如果我要执行 calc 14159676 / 42137864 * 100,我会得到 33% 而不是 df 打印的 36%。

但是如果我计算

14159676 / 39997348 * 100 我得到 35%。

为什么会有所有的差异,df 是从哪里得到 42137864 的?是否与转换为 1k 块与实际系统块大小为 4k 相关?

这将集成到我的缓存应用程序中,以告诉我驱动器何时处于某个阈值...例如90% 在我开始释放大小为 2^n 大小的固定大小的块之前。 所以我所追求的是一个能够给我一个相当准确的 %used 的函数。

编辑: 我现在可以匹配 df 打印的内容。除了 %Used。这让我们想知道这一切有多准确。片段大小是多少?

unsigned long total = vfs.f_blocks * vfs.f_frsize / 1024;
unsigned long available = vfs.f_bavail * vfs.f_frsize / 1024;
unsigned long free = vfs.f_bfree * vfs.f_frsize / 1024;

printf("Total: %luK\n", total);
printf("Available: %luK\n", available);
printf("Used: %luK\n", total - free);

EDIT2:

unsigned long total = vfs.f_blocks * vfs.f_frsize / 1024;
unsigned long available = vfs.f_bavail * vfs.f_frsize / 1024;
unsigned long free = vfs.f_bfree * vfs.f_frsize / 1024;
unsigned long used = total - free;

printf("Total: %luK\n", total);
printf("Available: %luK\n", available);
printf("Used: %luK\n", used);
printf("Free: %luK\n", free);

// Calculate % used based on f_bavail not f_bfree.  This is still giving out a different answer to df???
printf("Use%%: %f%%\n",  (vfs.f_blocks - vfs.f_bavail) / (double)(vfs.f_blocks) * 100.0); 

f_bsize (block size): 4096
f_frsize (fragment size): 4096
f_blocks (size of fs in f_frsize units): 10534466
f_bfree (free blocks): 6994182
f_bavail (free blocks for unprivileged users): 6459053
f_files (inodes): 2678784
f_ffree (free inodes): 2402056
f_favail (free inodes for unprivileged users): 2402056
f_fsid (file system ID): 12719298601114463092
f_flag (mount flags): 4096
f_namemax (maximum filename length)255
Total: 42137864K
Available: 25836212K
Used: 14161136K
Free: 27976728K
Use%: 38.686470%

matth@kubuntu:~/dev$ df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda5             42137864  14161136  25836212  36% /

我得到 38% 而不是 36。如果按 f_bfree 计算,我得到 33%。 df 是错误的还是永远不会准确?如果是这种情况,那么我想倾向于保守。

【问题讨论】:

    标签: c++ c linux filesystems


    【解决方案1】:

    df 的数据可能基于f_bavail,而不是f_bfree。您可能会发现查看source code to df 以了解它是如何工作的会很有帮助。它有许多需要处理的边缘情况(例如,当已用空间超过非 root 用户可用的空间量时),但正常情况的相关代码在这里:

      uintmax_t u100 = used * 100;
      uintmax_t nonroot_total = used + available;
      pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
    

    换句话说,100 * used / (used + available),向上取整。插入 df 输出中的值得到 100 * 14159676 / (14159676 + 25837672) = 35.4015371,四舍五入为 36%,正如 df 计算的那样。

    【讨论】:

    • df 代码不是特别容易理解。不能从df打印的详细信息中计算出使用百分比吗?
    • 这对我来说毫无意义。给我看看。
    • 查看我的更新答案。我得到的数字下降了大约 36%。我尝试查看 df 代码库,但这并不容易理解。
    【解决方案2】:

    这是我与df -h 的输出最接近的使用百分比:

    const uint GB = (1024 * 1024) * 1024;
    
    struct statvfs buffer;
    int ret = statvfs(diskMountPoint.c_str(), &buffer);
    
    const double total = ceil((double)(buffer.f_blocks * buffer.f_frsize) / GB);
    const double available = ceil((double)(buffer.f_bfree * buffer.f_frsize) / GB);
    const double used = total - available;
    const double usedPercentage = ceil((double)(used / total) * (double)100);
    
    return usedPercentage;
    

    【讨论】:

      【解决方案3】:

      在您的编辑 #2 中,Usage% 计算需要更新为此以匹配 df 输出:

      100.0 * (double) (vfs.f_blocks - vfs.f_bfree) / (double) (vfs.f_blocks - vfs.f_bfree + vfs.f_bavail)
      

      推理:
      已使用 = f_blocks - f_bfree
      可用 = f_bavail
      df % = 已用 /(已用 + 可用)

      【讨论】:

        【解决方案4】:

        statvfs 指标有点令人困惑。您可以使用 psutil 源代码作为示例,了解如何以字节为单位获取有意义的值:https://github.com/giampaolo/psutil/blob/f4734c80203023458cb05b1499db611ed4916af2/psutil/_psposix.py#L119

        【讨论】:

          【解决方案5】:

          每当我处理这个问题时,我似乎都会感到困惑。我希望以下 C 代码对寻找已用空间百分比的人有所帮助:

              /*
               * It is helpful to use a picture to aid the calculation of disk space.
               *
               * |<--------------------- f_blocks ---------------------------->|
               *                 |<---------------- f_bfree ------------------>|
               *
               * ---------------------------------------------------------------
               * | USED          | f_bavail                | Reserved for root |
               * ---------------------------------------------------------------
               *
               * We want the percentage of used blocks vs. all the
               * non-reserved blocks: USED / (USED + f_bavail)
               */
              fsblkcnt_t used = fs_stats.f_blocks - fs_stats.f_bfree;
              double fraction_used = (double) used / ((double) used + (double) fs_stats.f_bavail);
              uint8_t percent_used = (uint8_t) ((fraction_used * 100.0) + 0.5); // Add 0.5 for rounding
          

          【讨论】:

            【解决方案6】:

            这是一个模仿df行为的实现:

            #include <string>
            #include <sys/statvfs.h>
            
            double amountOfDiskSpaceUsed(const std::string& filePath)
            {
                // Based on the implementation in https://github.com/coreutils/coreutils/blob/master/src/df.c
                // See how PCENT_FIELD and IPCENT_FIELD are calculated.
                struct statvfs diskInfo;
                statvfs(filePath.c_str(), &diskInfo);
                const auto total           = static_cast<unsigned long>(diskInfo.f_blocks);
                const auto available       = static_cast<unsigned long>(diskInfo.f_bavail);
                const auto availableToRoot = static_cast<unsigned long>(diskInfo.f_bfree);
                const auto used            = total - availableToRoot;
                const auto nonRootTotal    = used + available;
                return 100.0 * static_cast<double>(used) / static_cast<double>(nonRootTotal);
            }
            

            例如它可能返回39.623889,而df 输出40%(四舍五入值)。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-02-17
              • 2019-07-03
              • 2016-03-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-26
              相关资源
              最近更新 更多