在Window系统上,打开我的电脑,我们就能够看见系统的每个磁盘的可用空间以及总空间,如下所示:
在嵌入式Linux与QT界面结合的产品开发中,我们在做产品的文件管理模块通常来说也需要做这样一个功能。那么如何来实现呢?
利用Linux系统天生就已经提供的df命令来获取,例如我们可以带上-h参数,这样就可以获得以人类可读的格式显示输出,如下所示:
基于韦东山imx6ull开发板文件系统
执行df -h
以后,我们能发现一定的规律,即是输出是以行为单位输出的,并且,每一行通过空格来进行分隔标识。因此,我们能够借助QT提供的字符串分割方法以及一些简单的逻辑来实现获取其中一行的内容。
QT大佬-飞扬青云在他的磁盘容量控件里就介绍了这种方法,开源仓库:
https://gitee.com/feiyangqingyun/QWidgetDemo?_from=gitee_search
测试解析一行的函数如下:
#include "mainwindow.h"
#include "ui_mainwindow.h"
void MainWindow::Get_Disk(const QString &result, const QString &name)
{
uint8_t index = 0;
uint8_t percent = 0;
QString dev, use, free, all;
QStringList list = result.split(" ");
for (int i = 0; i < list.count(); i++)
{
QString s = list.at(i).trimmed();
if (s == "")
continue;
index++;
if (index == 1)
dev = s;
else if (index == 2)
all = s;
else if (index == 3)
use = s;
else if (index == 4)
free = s;
else if (index == 5) {
percent = s.left(s.length() - 1).toInt();
break;
}
}
if (name.length() > 0)
dev = name;
qDebug() << "设备名称:" << dev ;
qDebug() << "总空间:" << all ;
qDebug() << "已经使用了多少空间:" << use ;
qDebug() << "剩余多少空间:" << free ;
qDebug() << "使用的空间的百分比:" << percent << "%";
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QString str = "/dev/sda4 14.4G 7.4G 7.0G 52% /mnt";
Get_Disk(str,"/dev/sda4");
}
MainWindow::~MainWindow()
{
delete ui;
}
运行结果:
这种方法即不需要去了解实现原理,简单来说就是一个字符串解析的过程。结合QT的QProcess
函数或者Linux C提供的popen
函数来调用df -h
命令来获取磁盘容量信息,然后通过这种方法循环读取每一行,结合自己产品的业务逻辑去获取对应的内容即可。
基于statfs
函数实现,这种方法其实就是df
命令的实现原理,statfs可以用于查询文件系统相关的信息。df命令实现如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/statfs.h>
static int ok = EXIT_SUCCESS;
//根据挂载的文件的大小来计算
static void printsize(long double n)
{
char unit = 'K';
n /= 1024;
if (n > 1024) {
n /= 1024;
unit = 'M';
}
if (n > 1024) {
n /= 1024;
unit = 'G';
}
printf("%-4.1Lf%c", n, unit);
}
static void df(char *s, int always) {
struct statfs st;
//statfs函数可用来查询文件系统相关的信息。
if (statfs(s, &st) < 0) {
fprintf(stderr, "%s: %s\n", s, strerror(errno));
ok = EXIT_FAILURE;
} else {
if (st.f_blocks == 0 && !always)
return;
printf("%-20s ", s);
printsize((long double)st.f_blocks * (long double)st.f_bsize);
printf(" ");
printsize((long double)(st.f_blocks - (long double)st.f_bfree) * st.f_bsize);
printf(" ");
printsize((long double)st.f_bfree * (long double)st.f_bsize);
printf(" %d\n", (int) st.f_bsize);
}
}
int df_main(int argc, char *argv[]) {
printf("Filesystem Size Used Free Blksize\n");
if (argc == 1) {
char s[2000];
//挂载的文件都在/proc/mounts下显示
FILE *f = fopen("/proc/mounts", "r");
while (fgets(s, 2000, f)) {
char *c, *e = s;
for (c = s; *c; c++) {
if (*c == ' ') {
e = c + 1;
break;
}
}
for (c = e; *c; c++) {
if (*c == ' ') {
*c = '\0';
break;
}
}
df(e, 0);
}
fclose(f);
} else {
int i;
for (i = 1; i < argc; i++) {
df(argv[i], 1);
}
}
exit(ok);
}
作为多年Ctrl-C and Ctrl-V职场老司机,想要把它融合到自己代码业务逻辑,那岂不是一件相当容易的事情?经过简单的魔改,需求就搞定了:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <sys/vfs.h>
#include <sys/stat.h>
#include <dirent.h>
/*判断目录是否存在*/
bool dirIsExist(const char *file_path)
{
DIR * mydir = NULL;
if ((mydir = opendir(file_path)) == NULL)
return false;
closedir(mydir);
return true;
}
/*获取磁盘的可用空间以及总空间*/
int getDiskInfo(const char *path,double * available,double *total)
{
uint64_t blocksize;
uint64_t totalsize;
uint64_t availablesize;
struct statfs diskInfo;
if(dirIsExist(path))
statfs(path, &diskInfo);
else
return -1 ;
// 每个block里包含的字节数
blocksize = diskInfo.f_bsize;
// 总的字节数,f_blocks为block的数目
totalsize = blocksize * diskInfo.f_blocks;
// 可用空间大小
availablesize = diskInfo.f_bavail * blocksize;
*available = availablesize ;
*total = totalsize ;
return 0 ;
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
char av_unit = 'K';
char total_unit = 'K';
double availablesize = 0.00 ;
double totalsize = 0.00 ;
getDiskInfo("/mnt",&availablesize,&totalsize);
availablesize /= 1024 ;
if(availablesize > 1024){
availablesize /= 1024 ;
av_unit = 'M';
}
if(availablesize > 1024){
availablesize /= 1024 ;
av_unit = 'G';
}
totalsize /= 1024 ;
if(totalsize > 1024){
totalsize /= 1024 ;
total_unit = 'M';
}
if(totalsize > 1024){
totalsize /= 1024 ;
total_unit = 'G';
}
qDebug() <<"availablesize:" << QString("%1%2").arg(QString::number(availablesize, 'f', 1)).arg(av_unit) ;
qDebug() <<"totalsize:" << QString("%1%2").arg(QString::number(totalsize, 'f', 1)).arg(total_unit) ;
}
在Linux下进行交叉编译后,在开发板将这个程序跑起来,运行如下:
大佬的方法简单粗暴,但个人推荐使用方法二来实现逻辑,可操作空间更大一些。下一期,我们结合iwlist以及wpa_cli来实现WIFI扫描、连接、状态查询等需求。