一、磁盘容量
在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),所以到罗马的大路有很多条,就看哪个合适即可。
四、总结
这种小问题,往往需要重复的造轮子。这个小总结就算给大家重复造轮子时的一个借鉴吧。