(三)获取linux内核的系统信息
这一章,使用qt来获取linux内核的系统信息并显示到界面上。这里的系统信息包括CPU的使用率,CPU的温度,内存信息以及硬盘信息。
-
CPU温度
-
NanoPI Fire3的开发团队为系统提供了读取CPU温度的接口,一个设备文件,使用命令查看CPU温度
-
cat /sys/class/thermal/thermal_zone0/temp
-
文件里面的值为65000,这里的值除1000就是实际的温度值,65000/1000=65。说明现在的CPU温度为65度。
知道了设备文件,那么就可以开始写程序了。
-
//获取CPU温度 QString temp_file_name("/sys/class/thermal/thermal_zone0/temp"); file_info.setFile(temp_file_name); if(file_info.isFile()) { QFile temp_file(temp_file_name); if(temp_file.open(QFile::ReadOnly)) { QString cpu_temp_str = temp_file.readAll(); cpu_info_data.temp = static_cast<double>((double)(cpu_temp_str.toDouble()) / 1000.0); qDebug() << QString("cpu temp : %1°C").arg(QString::number(cpu_info_data.temp,'f',2)); temp_file.close(); } } else { qDebug() << "No file named : " << temp_file_name; cpu_info_data.temp = 0; }
先判断文件是否存在,如果不存在那么直接返回。如果存在则使用QFile进行信息读取。然后把信息转换为double型进行存储。
为了方便的数据处理,我建立了一个结构体
-
// system info struct struct CpuInfo_data { long long total; long long usr; long long nic; long long sys; long long idle; long long iowait; long long irq; long long softirq; long long steal; double rate; double temp; };
其中temp是温度的值,其他的为CPU使用率所使用
-
CPU使用率
CPU使用率如何获得,我们都知道linux中有一个top命令可以查看CPU的信息
输入top命令,出来的最上面5行便是系统信息,我们关心的是3,4行,CPU使用率和内存信息。
CPU这一行的id属性的意思是剩余的CPU资源,总共是100,所以可以算出已经使用的CPU资源为100-92.5
大家都知道linux中的所有命令都来自于busybox的支持,如果不知道,可以暂停一下,去了解一下busybox是什么。
所以我们可以通过busybox中top命令的实现源码来判断这里的信息时如何读取出来的。
去busybox官网下载源码(如果打不开网站,可以在我的github下载)
https://busybox.net/downloads/
解压后通过source insight软件打开源码(source insight不会用的先去查找一下相关资料)
打开source insight,在右边输入top,打开top.c文件
先打开main函数,这里发现程序将工作目录切换到了/proc目录。
一路跟踪到do_stats函数,进入该函数
再进入get_jiffy_counts函数
这个函数解释了top命令中的cpu信息如何来的
首先看到,打开了stat文件,根据main函数中切换目录,可以判断这个文件名为/proc/stat,到linux系统中查看
ls /proc/stat
有这个文件,那么看看里面有什么
是一些CPU有关的数字,如果不知道是什么意思可以查一下/proc/stat关键字
有9行CPU开头的数据,其中CPU为总的CPU数据,其他0-7为各个核心的数据(没错,我这块板是8核的)。
现在我需要的是总CPU使用率,所以只关心第一行就行了。
看看busybox如何处理的
首先用fscanf格式化读取文件中的数据,并分别放到结构体保存,这里我们不用关心jif这个结构体是什么,只需要通过%lld知道这里的数据类型是long long就可以了。
接着求了一个总和,可以看到就是把其他的全部加起来。
总和减去剩余的,就是已经使用的。那么就可以求出使用率=(使用的/总和)*100
过程知道了,那么可以使用QT编写程序了。结构体上面已经展示过了,这里就直接给代码
//获得CPU占用信息
QString info_file_name("/proc/stat");
QFileInfo file_info(info_file_name);
//判断文件是否存在
if(file_info.isFile())
{
//提取文件中的数据
std::FILE *cpu_fd = std::fopen(info_file_name.toLatin1().data(),"r");
if(std::fscanf(cpu_fd,"cpu %lld %lld %lld %lld %lld %lld %lld %lld",\
&cpu_info_data.usr,&cpu_info_data.nic,&cpu_info_data.sys, \
&cpu_info_data.idle,&cpu_info_data.iowait, \
&cpu_info_data.irq,&cpu_info_data.softirq, \
&cpu_info_data.steal) < 4)
{
qDebug() << "failed to read " << info_file_name;
}
std::fclose(cpu_fd);
//计算CPU总资源数
cpu_info_data.total = cpu_info_data.usr + cpu_info_data.nic + cpu_info_data.sys + cpu_info_data.idle + cpu_info_data.iowait + cpu_info_data.irq + cpu_info_data.softirq + cpu_info_data.steal;
//获取CPU占用率
cpu_info_data.rate = static_cast<double>(1.0 - ((double)cpu_info_data.idle/(double)cpu_info_data.total)) * 100.0;
//qDebug() << QString("cpu usage rate : %1%").arg(QString::number(cpu_info_data.rate, 'f', 2));
}
else
{
qDebug() << "No file named : " << info_file_name;
return;
}
因为是C++语言,所以使用std::fscanf代替fscanf,其实参数都是一样的。求使用率的时候,因为是double所以记得使用static_case<double>进行类型转换。最后使用QString::number(cpu_info_data.rate, 'f', 2)方法输出小数点后2位的数据。
-
内存信息
上面说到的top命令同样也能读取内存数据,那么我们在top.c中找一下是如何实现的。
找到display_generic函数
发现这里使用的文件是meminfo,也就是/proc/meminfo,同样,我们在linux下查看这个文件里面的数据。
cat /proc/meminfo
可以看到是内存的所有信息,但是我们只关注前两行就可以了。
MemTotal是总的内存大小
MemFree是空闲的内存大小
那么可以得出使用的内存大小为MemTotal-MemFree,使用率为(MemTotal-MemFree)/MemTotal * 100
同样使用格式化读取,%lu为long unsigned int类型
那么接下来可以编写QT程序
定义结构体
struct MemInfo_data
{
long unsigned int total;
long unsigned int free;
long unsigned int used;
double rate;
};
读取文件信息
QString file_info_name("/proc/meminfo");
QFileInfo file_info(file_info_name);
if(file_info.isFile())
{
std::FILE *mem_fd = std::fopen(file_info_name.toLatin1().data(),"r");
std::fscanf(mem_fd,"MemTotal: %lu kB\n",&mem_info_data.total);
std::fscanf(mem_fd,"MemFree: %lu kB\n",&mem_info_data.free);
std::fclose(mem_fd);
mem_info_data.used = mem_info_data.total - mem_info_data.free;
mem_info_data.rate = static_cast<double>((double)mem_info_data.used / (double)mem_info_data.total) * 100.0;
mem_info_data.total = mem_info_data.total / 1024;
mem_info_data.used = mem_info_data.used / 1024;
mem_info_data.free = mem_info_data.free / 1024;
//qDebug() << QString("Mem : %1 / %2 MB %3%").arg(mem_info_data.used).arg(mem_info_data.total).arg(QString::number(mem_info_data.rate,'f',2));
}
else
{
qDebug() << "No file named : " << file_info_name;
return;
}
同样记得注意int-double的类型转换,读取数据的单位是KB,我想用MB所以除1024
-
硬盘信息
在linux上我们一般使用df命令来查看硬盘信息,这个命令也是由busybox实现,在源码中找到df.c,进入main函数。这里要注意一个结构体struct statfs,这个结构体保存了所有的硬盘信息,是内核实现的,不需要我们另外读取文件得到,不了解的先去查一下这个结构体的使用。
往下看,来到红框这里,这个函数的作用是读取以mount_point为结点的目录在硬盘中的空间使用情况,并放入s结构体,前面我们知道了s就是struct statfs
那么总的空间大小为f_blocks*f_bsize,空闲的空间大小为f_bfree*f_bsize
那么已经使用的空间大小为总的空间大小减去空闲的空间大小
编写QT代码
要使用struct statfs结构体,要先包括两个头文件
#include <sys/statfs.h>
#include <sys/vfs.h>
先定义结构体
struct DiskInfo_data
{
long long total;
long long free;
long long used;
double rate;
};
读取信息过程
struct statfs disk_info;
QString path = "/";
if (statfs(path.toLatin1().data(), &disk_info) == -1)
{
qDebug() << "Failed to get file disk infomation";
return;
}
disk_info_data.total = disk_info.f_blocks * disk_info.f_bsize;
disk_info_data.free = disk_info.f_bfree * disk_info.f_bsize;
disk_info_data.used = disk_info_data.total - disk_info_data.free;
disk_info_data.rate = static_cast<double>((double)disk_info_data.used / (double)disk_info_data.total) * 100.0;
disk_info_data.total = disk_info_data.total / 1024 / 1024;
disk_info_data.free = disk_info_data.free / 1024 / 1024;
disk_info_data.used = disk_info_data.used / 1024 / 1024;
//double total = static_cast<double>((double)disk_info_data.total / 1024);
//double used = static_cast<double>((double)disk_info_data.used / 1024);
//qDebug() << QString("Disk : %1 / %2 GB %3%").arg(QString::number(used,'f',2)).arg(QString::number(total,'f',2)).arg(QString::number(disk_info_data.rate,'f',2));
这里我想得到根文件系统的使用信息,所以传入的第一个参数为”/”,同样注意类型转换,以及单位转换,初始数据是字节为单位,我想使用GB为单位,所以要除三次1024
-
补充说明
4个系统数据都读取完成,于是我将他们整合起来,开一个线程来跑,每秒读取一次信息并返回给界面,使用QLabel显示。
这里遇到了一个问题就是,我使用了QT信号和槽机制来传输我自定义的三个结构体,但是信号和槽并不支持使用自定义的结构作为参数,所以报错
QObject::connect: Cannot queue arguments of type ‘CpuInfo_data’ (Make sure ‘CpuInfo_data’ is registed using qRegisterMetaType().)
解决方法
包含一个头文件
#include <QMetaType>
然后在connect信号槽之前注册自定义的结构体
//使用自定义结构体在信号槽中传递时需要先注册,否则会报错
qRegisterMetaType<CpuInfo_data>("CpuInfo_data");
qRegisterMetaType<MemInfo_data>("MemInfo_data");
qRegisterMetaType<DiskInfo_data>("DiskInfo_data");
这样就可以解决问题
运行结果在上一章已经展示过了。
Qt完整源码放在github上,有需要可以自行下载。 代码名称为190516.zip
https://github.com/ljy980330/opencv_face_sys
有任何问题可以在下面给我留言!大家一起学习!