Linux下获得磁盘和目录大小

一、磁盘容量

在Linux的系统中,如何获取磁盘的容易。最容易的方法是使用命令:

lsblk -o NAME,SIZE    [lsblk --all]
...
sda         931.5G
├─sda1        500G
└─sda2      431.5G
nvme0n1     476.9G
├─nvme0n1p1   512M
└─nvme0n1p2 476.4G

....

或者:
df -h
文件系统        大小  已用  可用 已用% 挂载点
tmpfs           3.2G  2.6M  3.2G    1% /run
/dev/nvme0n1p2  468G  297G  148G   67% /
tmpfs            16G  107M   16G    1% /dev/shm
tmpfs           5.0M  4.0K  5.0M    1% /run/lock
/dev/nvme0n1p1  511M  6.1M  505M    2% /boot/efi
/dev/sda2       424G   56G  347G   14% /mnt/data
tmpfs           3.2G  164K  3.2G    1% /run/user/1000
/dev/sda1       500G   14G  487G    3% /media/fpc/D42E48EB2E48C866

其它还有很多,比如fdisk 或 parted(可能需要sudo权限),blockdev,vgs ,lvs,smartctl等等。当然也可以使用du命令来查看具体某个路径的大小空间容量:

du -sh /usr
19G	/usr

但是如果想在程序里获取相关的磁盘的目录和磁盘大小呢?当然也可以采用执行命令的方法,去拿返回值。其实在Linux中也提供了相关的函数接口的。

二、获得磁盘大小的方法

在POSIX中提供了一个比较简单的接口statvfs,它的定义如下:

struct statvfs
  {
    unsigned long int f_bsize;//文件系统块大小(file system block size)
    unsigned long int f_frsize;//碎片段大小(fragment size)
#ifndef __USE_FILE_OFFSET64
    __fsblkcnt_t f_blocks;
    __fsblkcnt_t f_bfree;
    __fsblkcnt_t f_bavail;
    __fsfilcnt_t f_files;
    __fsfilcnt_t f_ffree;
    __fsfilcnt_t f_favail;
#else
    __fsblkcnt64_t f_blocks;//块单元数量(size of file system in f_frsize units)
    __fsblkcnt64_t f_bfree;//空闲块
    __fsblkcnt64_t f_bavail;//空闲块(用户)
    __fsfilcnt64_t f_files;
    __fsfilcnt64_t f_ffree;//空闲节点
    __fsfilcnt64_t f_favail;//空闲节点(用户)
#endif
    unsigned long int f_fsid;
#ifdef _STATVFSBUF_F_UNUSED
    int __f_unused;
#endif
    unsigned long int f_flag;
    unsigned long int f_namemax;
    int __f_spare[6];
  };
#ifdef __USE_LARGEFILE64
struct statvfs64
  {
    unsigned long int f_bsize;
    unsigned long int f_frsize;
    __fsblkcnt64_t f_blocks;
    __fsblkcnt64_t f_bfree;
    __fsblkcnt64_t f_bavail;
    __fsfilcnt64_t f_files;
    __fsfilcnt64_t f_ffree;
    __fsfilcnt64_t f_favail;
    unsigned long int f_fsid;
#ifdef _STATVFSBUF_F_UNUSED
    int __f_unused;
#endif
    unsigned long int f_flag;
    unsigned long int f_namemax;
    int __f_spare[6];
  };
#endif

一般来说,f_bsize和f_frsize的大小相同。而f_bfree和f_bavail的表现则略有不同。可能会有较大的误差,后者可能更接近于实际情况。所以大家可以根据实际情况来操作磁盘空间的大小显示。
它还有两个类似的相关接口fstatvfs和fstatvfs64,有兴趣的可以看看帮助文档。

三、例程

在实际编写程序时,有一个问题需要说明。并不是说使用设备名字来获得空间大小,这个误差相当大。它需要使用挂载点(mount point)来处理,则就基本正常了。所以代码中需要一个动态转换或者说获取挂载点的过程,看下面的代码:

#ifndef __DISKCAPACITY_H__
#define __DISKCAPACITY_H__

#include <string>
#include <unordered_map>
#include <vector>

namespace archive {
struct DevInfo {
  std::string devPath;
  std::string mountPoint;
  std::string type;
  uint64_t totalSize;
  uint64_t freeSize;
};
class DiskCapacity {
public:
  DiskCapacity();

public:
  bool initDiskCapacity();
  std::unordered_map<std::string, uint64_t> getDiskCapacity();

private:
  std::vector<DevInfo> getCapacityFromMountPoint();

private:
  DevInfo m_devInfo;
  std::vector<DevInfo> m_vecDevInfo;
};
} // namespace archive
#endif // __DISKCAPACITY_H__
//cpp
#include "DiskCapacity.h"
#include <fcntl.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <linux/fs.h>
#include <sstream>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>

namespace fs = std::filesystem;

namespace archive {
DiskCapacity::DiskCapacity() {}
bool DiskCapacity::initDiskCapacity() { return true; }
//!
//! \brief DiskCapacity::getDiskCapacity
//! \return unordered_map:devPath:/dev/xxx ,capacity(uint64_t):M(K,M,G,T)
//!
std::unordered_map<std::string, uint64_t> DiskCapacity::getDiskCapacity() {
  std::unordered_map<std::string, uint64_t> umDev;
  auto vec = this->getCapacityFromMountPoint();
  for (auto &au : vec) {
    umDev.emplace(au.devPath, au.freeSize);
  }
  return umDev;
}
std::vector<DevInfo> DiskCapacity::getCapacityFromMountPoint() {
  struct statvfs fsStat;
  std::string devAllPath = R"path(/dev/)path";

  for (const auto &entry : fs::directory_iterator(devAllPath)) {
    std::string path = devAllPath;
    // ignore directory and non block devives
    if (entry.is_directory() || strncmp(path.c_str(), "sd", 2) != 0) {
      continue;
    }

    path += entry.path().string();

    struct stat st;
    if (stat(path.c_str(), &st) == 0 && S_ISBLK(st.st_mode)) {
      DevInfo devInfo;
      devInfo.devPath = path;

      std::string mountPoint = R"point(/proc/mounts)point";
      std::ifstream mounts(mountPoint);
      std::string line;

      while (std::getline(mounts, line)) {
        std::istringstream iss(line);
        std::string dev, mPoint, fType;
        iss >> dev >> mPoint >> fType;

        devInfo.mountPoint = mPoint;
        devInfo.type = fType;

        if (statvfs(devInfo.mountPoint.c_str(), &fsStat) == 0) {
          devInfo.totalSize = static_cast<unsigned long long>(fsStat.f_blocks) * fsStat.f_frsize;
          devInfo.freeSize = static_cast<unsigned long long>(fsStat.f_bavail) * fsStat.f_frsize;
        }
        break;
      }

      m_vecDevInfo.emplace_back(devInfo);
    }
  }
  return m_vecDevInfo;
}
} // namespace archive

代码并不复杂,这里就不再分析了。
在Linux及相关的环境中,还有其它相关的函数也可以达到类似的目的,如statfs(BSD,Linux),所以到罗马的大路有很多条,就看哪个合适即可。

四、总结

这种小问题,往往需要重复的造轮子。这个小总结就算给大家重复造轮子时的一个借鉴吧。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值