我们选择采用 Python 语言作为这次实验的开发语言。同时使用Tkinter 模块实现 GUI 图形界面。
一、设计流程
整个资源管理器分为四个模块:CPU、内存、进程和硬盘。
二、内存模块
在/proc 目录中的 meminfo 文件记录有内存的基本信息。通过使用 cat 命令查看 meminfo 文件的内容,从中确定了我们需要读取的字段:
1、读取/proc/meminfo 文件, 其中我们需要的字段有:(单位均为 KB)
通过 Python 的字符串分割函数, 将以上的值存入数据结构字典中。
2、计算内存和交换空间的使用率:
内存使用率: 1 – MemFree / MemTotal
交换空间的内存使用率: 1 – SwapFree / MemTotal
3、相关代码
代码如下(示例):
def read_meminfo(read_type):
with open('/proc/meminfo',read_type) as file:
for line in file.readlines():
data = line.split()
if data[0] == 'MemTotal:':
meminfo['mem_total'] = data[1]
elif data[0] == 'MemAvailable:':
meminfo['mem_available'] = data[1]
elif data[0] == 'SwapTotal:':
meminfo['swap_total'] = data[1]
elif data[0] == 'SwapFree:':
meminfo['swap_free'] = data[1]
# 已使用内存
mem_used = float(meminfo['mem_total'])-float(meminfo['mem_available'])
# 内存使用率
meminfo['mem_usage']=100*(mem_used/float(meminfo['mem_total']))
# 交换空间使用大小
swap_used = float(meminfo['swap_total'])-float(meminfo['swap_free'])
# 交换空间
meminfo['swap_usage']=100*(swap_used/float(meminfo['swap_total']))
return meminfo
def get_meminfo():
read_meminfo('r')
meminfo['mem_usage'] = 100 * (float(meminfo['mem_total']) - float(meminfo['mem_available'])) / float(meminfo['mem_total'])
meminfo['swap_usage'] = 100 * (float(meminfo['swap_total']) - float(meminfo['swap_free'])) / float(meminfo['swap_total'])
return meminfo
三、CPU模块
部分代码如下:
def read_cpuinfo(read_type):
with open('/proc/cpuinfo',read_type) as file:
for line in file.readlines():
data = line.split(':')
#print(data)
# 获取cpu名 CPU属于的名字及其编号、标称主频
if data[0].startswith('model name') :
cpuinfos['cpu_name'] = data[1].strip('\n').strip()
# CPU的实际使用主频
elif data[0].startswith('cpu MHz'):
#round()四舍五入
cpuinfos['mhz'] = round(float(data[1].strip('\n').strip()) / 1024, 2)
#cpu利用率
def calc_cpuUsage():
read_cpuinfo('r')
read_stat('r')
idle1 = cpuinfos['idle'].copy()
total1 = cpuinfos['total'].copy()
#记录第一次的数据
cpuinfos['idle'].clear()
cpuinfos['total'].clear()
time.sleep(0.1)
read_stat('r')
# 重新记录一次数据
idle2 = cpuinfos['idle']
total2 = cpuinfos['total']
cpuinfos['usage'].clear()
for i in range(cpuinfos['num'] + 1):
usage = (1-(idle2[i]-idle1[i])/(total2[i]-total1[i]))*100
cpuinfos['usage'].append(usage)
# 获取系统启动到现在的时间(以妙为单位)
def read_uptime(read_type):
# 第一列输出的是,系统启动到现在的时间(以秒为单位),这里简记为num1;
# 第二列输出的是,系统空闲的时间(以秒为单位),这里简记为num2。
with open('/proc/uptime', read_type) as file:
seconds = file.read()
t = seconds.strip('\n').split(' ')
# 返回一个包含除法和余数的元组,这里取第一个数字
m, s = divmod(int(float(t[0])), 60)
h, m = divmod(m, 60)
cpuinfos['usage_time'] = ("%d:%02d:%02d" % (h, m, s))
def read_stat(read_type):
with open('/proc/stat',read_type) as file:
for line in file.readlines():
data = line.split()
if data[0].startswith('cpu'):
#cpu user nice system idle iowait irq softirq steal guest guest_nice
total = 0
for i in range(1, len(data)):
total = total+ int(data[i])
cpuinfos['total'].append(total)
cpuinfos['idle'].append(int(data[4]))
cpuinfos['num'] = len(cpuinfos['total']) - 1
# def read_cpu_cache(read_type='r'):
# """
# 获取CPU的cache,先从/sys/devices/system/cpu/中获取每个Core的Cache的级数level
# 然后获取Cache的size,
# """
# cpu_index = os.
def get_loadavg():
with open('/proc/loadavg', 'r') as file:
data = file.read().split()
n = 0
for i in loadavg.keys():
loadavg[i] = data[n]
n += 1
return loadavg
def get_cpuinfos():
calc_cpuUsage()
read_uptime('r')
read_meminfo('r')
return cpuinfos
四、磁盘模块
部分代码如下:
def read_mounts():
zoning_num = 0
with open('/proc/mounts','r') as f: #/proc/mounts的内容为系统当前挂载的所有文件系统
for line in f.readlines(): #readline逐行读取
data = line.split() #把末尾的\n删掉
if data[0].startswith('/dev/sda'): #检查字符串是否是以/dev/sda开头(磁盘),如果是则返回 True
disk_path['path'].append(data[1]) #磁盘挂载点,append()在列表末尾添加新的对象
zoning_num += 1
disk_number['zoning'] = zoning_num
list_dz = [disk_path,disk_number]
return list_dz
def read_disk():
st = os.statvfs('/') #os模块的statvfs()方法--读取各分区挂载点,查看文件系统信息、磁盘使用情况
#st返回值---f_blocks分区文件系统数据块总数;f_frsize基本文件系统块大小;
#f_bfree可用块数;f_bavail非超级用户可用的空闲块
#round四舍五入返回一个数值,1即小数位后只有1位
total = round(st.f_blocks * st.f_frsize / 1024 /1024 /1024 , 1) #磁盘总容量
used = round((st.f_blocks - st.f_bfree) * st.f_frsize / 1024 / 1024 /1024 , 1) #已使用容量
available = round(st.f_bavail * st.f_frsize / 1024 / 1024 /1024 , 1) #剩余可使用
actual_total = round(used + available , 1) #实际总容量
usage = round(used / (used + available) * 100, 2) #磁盘使用率
disk_usage['total'].append(total)
disk_usage['actual_total'].append(actual_total)
disk_usage['used'].append(used)
disk_usage['available'].append(available)
disk_usage['usage'].append(usage)
read_mounts() #统计磁盘使用情况
for i in range(disk_number['zoning']): #range返回从0开始到zoning_num的数字
st = os.statvfs(disk_path['path'][i])
total = round(st.f_blocks * st.f_frsize / 1024 /1024 /1024 , 1) #分区总容量
used = round((st.f_blocks - st.f_bfree) * st.f_frsize / 1024 / 1024 /1024 , 1) #已使用容量
available = round(st.f_bavail * st.f_frsize / 1024 / 1024 /1024 , 1) #剩余可使用
actual_total = round(used + available , 1) #实际总容量
usage = round(used / (used + available) * 100, 2) #分区使用率
disk_usage['total'].append(total)
disk_usage['actual_total'].append(actual_total)
disk_usage['used'].append(used)
disk_usage['available'].append(available)
disk_usage['usage'].append(usage)
return disk_usage
#读取diskstat
def read_diskstats():
disk_num = 0
#这个文件用于显示磁盘、分区和统计信息:sda为整个硬盘的统计信息
with open('/proc/diskstats','r') as f:
for line in f.readlines():
data = line.split()
if data[2].startswith('sda'):
disk_active['I/O_oper_time'].append(int(data[-6])) #I/O操作花费毫秒数
sector['read_sector'].append(int(data[5])) #读扇区次数
sector['write_sector'].append(int(data[9])) #写扇区次数
re_wr_num['read_num'].append(int(data[3])) #读完成次数
re_wr_num['write_num'].append(int(data[7])) #写完成次数
re_wr_time['read_time'].append(int(data[6])) #读操作花费毫秒数
re_wr_time['write_time'].append(int(data[10])) #写操作花费毫秒数
disk_num += 1
disk_number['disk_num'] = disk_num
list_disk = [disk_active,sector,re_wr_num,re_wr_time,disk_number]
return list_disk
五、进程模块
部分代码如下:
procproc.py
import os
from .process.process import Process
class Processes:
PROC_ROOT = "/proc/"
def __init__(self):
self.process = []
self.__process = []
def __clear(self):
self.process.clear()
self.__process.clear()
def clear(self):
self.__clear()
def to_dict(self):
self.process.clear()
for p in self.__process:
d = p.to_dict()
if d is None:
continue
self.process.append(d)
return self.process
def refresh(self):
try:
flist = os.listdir(Processes.PROC_ROOT)
try:
iter_proc = iter(self.__process)
no_more = False
last_proc = None
for p in flist:
if not p.isnumeric():
continue
if last_proc is None:
if not no_more:
try:
last_proc = next(iter_proc)
except StopIteration:
no_more = True
last_proc = Process()
else:
last_proc = Process()
if not last_proc.read_process(int(p)):
continue
if no_more:
self.__process.append(last_proc)
last_proc = None
except Exception:
return False
else:
return True
except Exception:
return False
if __name__ == '__main__':
a = Processes()
a.refresh()
print(a.to_dict()['uid'])
process.py
def __parse_cmdline(self):
if len(self.__cmdline) > 0:
self.cmdline = self.__cmdline[:-1]
else:
self.cmdline = self.__cmdline
return True
def __get_cmdline(self, root_path):
cmdline = None
path = root_path + "cmdline"
cmdline = Process.open_and_readline(path)
if cmdline is None:
return False
self.__cmdline = cmdline
return True
def __parse_stat(self):
try:
value = self.__stat.split(' ')
if value[2].endswith(')'):
value[1] = value[1] + ' ' + value[2]
value.pop(2)
self.__comm = value[1]
self.comm = self.__comm[1:-1]
self.__state = value[2]
if self.__state in Process.STATE2STR.keys():
self.state = Process.STATE2STR[self.__state]
else:
self.state = self.__state
self.stime = value[13]
self.utime = value[14]
self.cputime = int(self.stime) + int(self.utime)
self.priority = int(value[17])
self.nice = int(value[18])
self.__starttime = value[21]
self.starttime = Process.tick2strtime(int(self.__starttime))
self.__rss = value[23]
self.rss = int(value[23]) * PAGE_SIZE
if self.rss == 0:
self.rss = 'N/A'
elif self.rss / 1024 > 0.5:
self.rss /= 1024
if self.rss / 1024 > 0.5:
self.rss /= 1024
if self.rss / 1024 > 0.5:
self.rss = round(self.rss / 1024, 2)
self.rss = str(self.rss) + "GB"
else:
self.rss = round(self.rss, 2)
self.rss = str(self.rss) + "MB"
else:
self.rss = round(self.rss, 2)
self.rss = str(self.rss) + "KB"
else:
self.rss = round(self.rss, 2)
self.rss = str(self.rss) + "B"
self.rt_priority = int(value[39])
return True
except Exception:
return False
def __get_stat(self, root_path):
stat = None
path = root_path + "stat"
stat = Process.open_and_readline(path)
if stat is None or len(stat) < 103:
return False
self.__stat = stat
return True
def __get_uid(self, root_path):
try:
stat = os.stat(root_path)
self.uid = stat.st_uid
return True
except Exception:
return False
def to_dict(self):
if self.pid == Process.INVAL_PID:
return None
self.__parse_cmdline()
self.__parse_stat()
r = {}
for k, v in self.__dict__.items():
if k.startswith('_'):
continue
if hasattr(v, "__str__"):
r[k] = str(v)
elif hasattr(v, "to_dict"):
r[k] = v.getdict()
else:
r[k] = None
return r
六、图形化界面
部分代码:
from tkinter import *
from tkinter import messagebox
from tkinter.ttk import *
from frame_power import *
from frame_option import *
from frame_process import *
class Application(Tk):
def __init__(self, *args, **kw):
super().__init__()
self.wm_title('Linux Resource Monitor')
# self.title('manager')
self.geometry('1000x600')
self.fresh_time = 250
style = Style()
style.configure('BW.TLabel', bg='black', font=('Fira-Code','16'), width=8, height=10)
# init Frame
self.frame_showing = None
self.frame_option = Frame_option(self)
self.frame_process = Frame_process(self)
self.frame_power = Frame_power(self)
self.frame_option.button_power = Button(self.frame_option, text='Performance', command=lambda: self.switch('power'))
self.frame_option.button_process = Button(self.frame_option, text='Process', command=lambda: self.switch('process'))
self.frame_option.button_process.grid(row=0, column=0)
self.frame_option.button_power.grid(row=0, column=1)
self.__init_ui()
self.refresh_info()
self.mainloop()
# self.mainloop()
def __init_ui(self):
self.frame_option.pack(side=TOP, fill=X)
self.frame_process.pack(side=TOP, fill=BOTH, expand=TRUE)
self.frame_showing = self.frame_process
def refresh_info(self):
print(self.frame_showing)
if self.frame_showing is self.frame_process:
self.fresh_time = 2000
self.frame_process.refresh_info()
if self.frame_showing is self.frame_power:
self.fresh_time = 250
self.frame_power.refresh()
self.after(self.fresh_time, lambda: self.refresh_info())
def switch(self, frame_type):
print(self.frame_showing, self.frame_power, self.frame_process)
if frame_type == 'power' and self.frame_showing is not self.frame_power:
self.frame_showing.pack_forget()
self.frame_power.pack(side=TOP, fill=BOTH, expand=TRUE)
self.frame_showing = self.frame_power
if frame_type == 'process' and self.frame_showing != self.frame_process:
self.frame_showing.pack_forget()
self.frame_process.pack(side=TOP, fill=BOTH, expand=TRUE)
self.frame_showing = self.frame_process