一、使用开源库监控Linux
在这一小节,我们将介绍一个在Python生态中广泛使用的开源项目,即psutil。随后,我们将使用psutil重构前一小节编写的监控程序。另外,还会简单介绍psutil提供的进程管理功能。
1、psutil介绍
psutil = process and system utilities
- psutil是一个开源且跨平台的库,其提供了便利的函数用来获取操作系统的信息,比如CPU,内存,磁盘,网络等。此外,psutil还可以用来进行进程管理,包括判断进程是否存在、获取进程列表、获取进程详细信息等。而且psutil还提供了许多命令行工具提供的功能,包括:ps,top,lsof,netstat,ifconfig, who,df,kill,free,nice,ionice,iostat,iotop,uptime,pidof,tty,taskset,pmap。
psutil是一个跨平台的库
,支持Linux、Windows、OSX、FreeBSD、OpenBSD、NetBSD、Sun Solaris、AIX等操作系统。同时,psutil也支持32位与64位的系统架构,支持Python2.6到Python3.x之间的所有Python版本。- psutil具有简单易用、功能强大、跨平台等诸多优点,广泛应用于开源项目中,比较有名的有glances、Facebook的osquery、Google的grr等。psutil不但广泛应用于Python语言开发的开源项目中,还被移植到了其他编程语言中,如Go语言的gopsutil、C语言的cpslib、Rust语言的rust-psutil、Ruby语言的posixpsutil等。
pip安装psutil
psutil是一个第三方的开源项目,因此,需要先安装才能够使用。如果安装了Anaconda,psutil就已经可用了。否则,需要在命令行下通过pip安装:
[root@localhost ~]# pip3 install psutil
Collecting psutil
Downloading psutil-5.7.0.tar.gz (449 kB)
|████████████████████████████████| 449 kB 4.6 kB/s
Installing collected packages: psutil
Running setup.py install for psutil ... done
Successfully installed psutil-5.7.0
[root@python scripts]# ipython #打开ipython
psutil包含了异常、类、功能函数和常量,其中功能函数用来获取系统的信息,如CPU、磁盘、内存、网络等。类用来实现进程的管理功能。
2、psutil提供的功能函数
根据函数的功能,主要分为CPU、磁盘、内存、网络几类,下面将会总几个方面来介绍psutil提供的功能函数。在这一小节,我们也将学习如何使用psutil来简化使用shell脚本获取监控信息的程序,并获取CPU、内存、磁盘和网络等不同维度。
(1)CPU
与CPU相关的功能函数如下:
函数 | 描述 |
---|---|
psutil.cpu_count() | cpu_count(,[logical]):默认返回逻辑CPU的个数,当设置logical的参数为False时,返回物理CPU的个数。 |
psutil.cpu_percent() | cpu_percent(,[percpu],[interval]):返回CPU的利用率,percpu为True时显示所有物理核心的利用率,interval不为0时,则阻塞时显示interval执行的时间内的平均利用率 |
psutil.cpu_times() | cpu_times(,[percpu]):以命名元组(namedtuple)的形式返回cpu的时间花费,percpu=True表示获取每个CPU的时间花费 |
psutil.cpu_times_percent() | cpu_times_percent(,[percpu]):功能和cpu_times大致相同,看字面意思就能知道,该函数返回的是耗时比例。 |
psutil.cpu_stats() | cpu_stats()以命名元组的形式返回CPU的统计信息,包括上下文切换,中断,软中断和系统调用次数。 |
psutil.cpu_freq() | cpu_freq([percpu]):返回cpu频率 |
1)cpu_count
默认返回逻辑CPU的个数,当设置logical的参数为False时,返回物理CPU的个数。
In [1]: import psutil
In [2]: psutil.cpu_count() #查看处理器内核数
Out[2]: 1
In [3]: psutil.cpu_count(logical=False) #查看目前使用的处理器内核数
Out[3]: 1
2)cpu_percent
返回CPU的利用率,percpu为True时显示所有物理核心的利用率,interval不为0时,则阻塞时显示interval执行的时间内的平均利用率。
In [5]: psutil.cpu_percent() #查看每个内核使用率
Out[5]: 0.2
In [6]: psutil.cpu_percent(percpu=True)
Out[6]: [0.5]
In [7]: psutil.cpu_percent(percpu=True,interval=2)
Out[7]: [0.0]
3)cpu_times
以命名元组(namedtuple)的形式返回cpu的时间花费,percpu=True表示获取每个CPU的时间花费。
In [8]: psutil.cpu_times()
Out[8]: scputimes(user=11.1, nice=0.0, system=14.05, idle=3252.64, iowait=0.98, irq=0.0, softirq=0.18, steal=0.0, guest=0.0, guest_nice=0.0)
In [9]: psutil.cpu_times_percent()
Out[9]: scputimes(user=0.2, nice=0.0, system=0.1, idle=99.6, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
4)cpu_stats
以命名元组的形式返回CPU的统计信息,包括上下文切换,中断,软中断和系统调用次数。
In [10]: psutil.cpu_stats()
Out[10]: scpustats(ctx_switches=403960, interrupts=214204, soft_interrupts=217258, syscalls=0)
5)cpu_freq
返回cpu频率。
In [11]: psutil.cpu_freq()
Out[11]: scpufreq(current=1799.453, min=0.0, max=0.0)
(2)内存
与内存相关的功能函数如下:
1)virtual_memory
以命名元组的形式返回内存使用情况,包括总内存、可用内存、内存利用率、buffer和cache等。除了内存利用率,其它字段都以字节为单位返回。
In [1]: import psutil
In [12]: psutil.virtual_memory()
Out[12]: svmem(total=1023934464, available=383610880, percent=62.5, used=481861632, free=67833856, active=436379648, inactive=340885504, buffers=2166784, cached=472072192, shared=7598080, slab=79253504)
单位转换
#/usr/bin/python
#-*- conding:utf-8 _*_
import psutil
def bytes2human(n):
symbols = ('K','M','G','T','P','E','Z','Y')
prefix = {}
for i,s in enumerate(symbols):
prefix[s] = 1 << (i + 1) * 10
print(prefix[s])
print('============')
for s in reversed(symbols):
if n >= prefix[s]:
value = float(n) / prefix[s]
return '%.1f%s' % (value,s)
# return '{0}.1f{1}'.format(value, s)
return "%sB" % n
# return "{0}B" .format(n)
print("总内存:"+bytes2human(psutil.virtual_memory().total))
运行结果如下所示:
[root@python scripts]# python3 monitor_mem.py
1024
1048576
1073741824
1099511627776
1125899906842624
1152921504606846976
1180591620717411303424
1208925819614629174706176
============
总内存:976.5M
分析一下
2)swap_memory
以命名元组的形式返回swap/memory使用情况,包含swap中页的换入和换出。
In [13]: psutil.swap_memory()
Out[13]: sswap(total=2147479552, used=0, free=2147479552, percent=0.0, sin=0, sout=0)
(3)磁盘
与磁盘相关的功能如下:
函数 | 描述 |
---|---|
psutil.disk_io_counters() | disk_io_counters([perdisk]):以命名元组的形式返回磁盘io统计信息(汇总的),包括读、写的次数,读、写的字节数等。 当perdisk的值为True,则分别列出单个磁盘的统计信息(字典:key为磁盘名称,value为统计的namedtuple)。 |
psutil.disk_partitions() | disk_partitions([all=False]):以命名元组的形式返回所有已挂载的磁盘,包含磁盘名称,挂载点,文件系统类型等信息。 当all等于True时,返回包含/proc等特殊文件系统的挂载信息 |
psutil.disk_usage() | disk_usage(path):以命名元组的形式返回path所在磁盘的使用情况,包括磁盘的容量、已经使用的磁盘容量、磁盘的空间利用率等。 |
1)psutil.disk_io_counters
以命名元组的形式返回磁盘io统计信息(汇总的),包括读、写的次数,读、写的字节数等。 当perdisk的值为True,则分别列出单个磁盘的统计信息(字典:key为磁盘名称,value为统计的namedtuple)。有了disk_io_counters函数,省去了解析/proc/diskstats文件的烦恼。
In [1]: import psutil
In [2]: psutil.disk_io_counters()
Out[2]: sdiskio(read_count=86913, write_count=46560, read_bytes=5038501376, write_bytes=408987648, read_time=77974, write_time=79557, read_merged_count=5933, write_merged_count=35916, busy_time=42153)
In [3]: psutil.disk_io_counters(perdisk=Tru
Out[3]:
{'sda': sdiskio(read_count=41472, write_count=5340, read_bytes=2524417024, write_bytes=205662720, read_time=38302, write_time=4484, read_merged_count=5933, write_merged_count=35916, busy_time=21074),
'sda1': sdiskio(read_count=1854, write_count=4, read_bytes=6441472, write_bytes=2097152, read_time=370, write_time=35, read_merged_count=0, write_merged_count=0, busy_time=396),
'sda2': sdiskio(read_count=39587, write_count=5337, read_bytes=2516263424, write_bytes=203570688, read_time=37925, write_time=4449, read_merged_count=5933, write_merged_count=35916, busy_time=20675),
'sr0': sdiskio(read_count=0, write_count=0, read_bytes=0, write_bytes=0, read_time=0, write_time=0, read_merged_count=0, write_merged_count=0, busy_time=0),
'dm-0': sdiskio(read_count=38566, write_count=5197, read_bytes=2483773952, write_bytes=55885312, read_time=37685, write_time=3546, read_merged_count=0, write_merged_count=0, busy_time=19410),
'dm-1': sdiskio(read_count=6875, write_count=36059, read_bytes=30310400, write_bytes=147697664, read_time=1987, write_time=71537, read_merged_count=0, write_merged_count=0, busy_time=1673)}
2)psutil.disk_partitions
以命名元组的形式返回所有已挂载的磁盘,包含磁盘名称,挂载点,文件系统类型等信息。当all等于True时,返回包含/proc等特殊文件系统的挂载信息。
In [4]: psutil.disk_partitions() #查看挂载点信息
Out[4]:
[sdiskpart(device='/dev/mapper/centos-root', mountpoint='/', fstype='xfs', opts='rw,seclabel,relatime,attr2,inode64,noquota'),
sdiskpart(device='/dev/sda1', mountpoint='/boot', fstype='xfs', opts='rw,seclabel,relatime,attr2,inode64,noquota')]
In [5]: [device for device in psutil.disk_partitions() if device.mountpoint == '/']
Out[5]: [sdiskpart(device='/dev/mapper/centos-root', mountpoint='/', fstype='xfs', opts='rw,seclabel,relatime,attr2,inode64,noquota')]
In [6]: def get_disk_via_mountpoint(point): #创建一个函数
...: disk = [item for item in psutil.disk_partitions() if item.mountpoint == point]
...: return disk[0].device
//没有任何输出
In [7]: get_disk_via_mountpoint('/') #调用get_disk_via_mountpoint查看“/”的挂载点
Out[7]: '/dev/mapper/cl-root'
In [8]: get_disk_via_mountpoint('/boot') #调用get_disk_via_mountpoint查看“/boot”的挂载点
Out[8]: '/dev/sda1'
3)psutil.disk_usage
以命名元组的形式返回path所在磁盘的使用情况,包括磁盘的容量、已经使用的磁盘容量、磁盘的空间利用率等。
In [9]: psutil.disk_usage('/')
Out[9]: sdiskusage(total=18238930944, used=6775488512, free=11463442432, percent=37.1)
In [10]: psutil.disk_usage('/').percent
Out[10]: 37.2
In [11]: type(psutil.disk_usage('/').percent)
Out[11]: float
(4)网络
与网络相关的函数如下:
函数 | 详情 |
---|---|
psutil.net_io_counter([pernic]) | 以命名元组的形式返回当前系统中每块网卡的网络io统计信息,包括收发字节数,收发包的数量、出错的情况和删包情况。当pernic为True时,则列出所有网卡的统计信息。 |
psutil.net_connections([kind]) | 以列表的形式返回每个网络连接的详细信息(namedtuple)。命名元组包含fd, family, type, laddr, raddr, status, pid等信息。kind表示过滤的连接类型,支持的值如下:(默认为inet) |
psutil.net_if_addrs() | 以字典的形式返回网卡的配置信息,包括IP地址和mac地址、子网掩码和广播地址。 |
psutil.net_if_stats() | 返回网卡的详细信息,包括是否启动、通信类型、传输速度与mtu。 |
psutil.users() | 以命名元组的方式返回当前登陆用户的信息,包括用户名,登陆时间,终端,与主机信息 |
psutil.boot_time() | 以时间戳的形式返回系统的启动时间 |
1)psutil.net_io_counter
以命名元组的形式返回当前系统中每块网卡的网络io统计信息,包括收发字节数,收发包的数量、出错的情况和删包情况。当pernic为True时,则列出所有网卡的统计信息。使用net_io_counter函数与自己解析/proc/net/dev文件内容实现的功能相同。
In [1]: import psutil
In [2]: psutil.net_io_counters()
Out[2]: snetio(bytes_sent=720405, bytes_recv=3661606, packets_sent=5520, packets_recv=14886, errin=0, errout=0, dropin=0, dropout=0)
In [3]: psutil.net_io_counters(pernic=True)
Out[3]:
{'ens37': snetio(bytes_sent=724145, bytes_recv=3365944, packets_sent=5538, packets_recv=10017, errin=0, errout=0, dropin=0, dropout=0),
'lo': snetio(bytes_sent=0, bytes_recv=0, packets_sent=0, packets_recv=0, errin=0, errout=0, dropin=0, dropout=0),
'virbr0-nic': snetio(bytes_sent=0, bytes_recv=0, packets_sent=0, packets_recv=0, errin=0, errout=0, dropin=0, dropout=0),
'virbr0': snetio(bytes_sent=0, bytes_recv=0, packets_sent=0, packets_recv=0, errin=0, errout=0, dropin=0, dropout=0),
'ens33': snetio(bytes_sent=0, bytes_recv=298202, packets_sent=0, packets_recv=4899, errin=0, errout=0, dropin=0, dropout=0)}
2)net_connections
以列表的形式返回每个网络连接的详细信息(namedtuple),可以使用该函数查看网络连接状态,统计连接个数以及处于特定状态的网络连接个数。
In [4]: psutil.net_connections()
Out[4]:
[sconn(fd=6, family=, type=, laddr=addr(ip='::', port=111), raddr=(), status='LISTEN', pid=6558),
sconn(fd=7, family=, type=, laddr=addr(ip='::', port=111), raddr=(), status='NONE', pid=6558),
sconn(fd=8, family=, type=, laddr=addr(ip='::1', port=6010), raddr=(), status='LISTEN', pid=9047),
sconn(fd=6, family=, type=,
......
In [5]: conns = psutil.net_connections()
In [6]: len([conn for conn in conns if conn.status == 'TIME_WAIT'])
Out[6]: 0
3)net_if_addrs
以字典的形式返回网卡的配置信息,包括IP地址和mac地址、子网掩码和广播地址。
In [7]: psutil.net_if_addrs() Out[7]:
{'lo': [snicaddr(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None),
snicaddr(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
snicaddr(family=, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)],
'ens37': [snicaddr(family=, address='192.168.1.131', netmask='255.255.255.255', broadcast='192.168.1.131', ptp=None),
snicaddr(family=, address='240e:82:e03:7342:4378:7be3:558c:fc88', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None)
......
4)psutil.net_if_stats
返回网卡的详细信息,包括是否启动、通信类型、传输速度与mtu。
In [8]: psutil.net_if_stats()
Out[8]:
{'ens37': snicstats(isup=True, duplex=, speed=1000, mtu=1500),
'lo': snicstats(isup=True, duplex=, speed=0, mtu=65536),
'virbr0-nic': snicstats(isup=False, duplex=, speed=10, mtu=1500),
'virbr0': snicstats(isup=True, duplex=, speed=0, mtu=1500),
'ens33': snicstats(isup=True, duplex=, speed=1000, mtu=1500)}
(5)其他
1)users
以命名元组的方式返回当前登陆用户的信息,包括用户名,登陆时间,终端,与主机信息。
In [9]: psutil.users()
Out[9]:
[suser(name='root', terminal=':0', host='localhost', started=1582366080.0, pid=7991),
suser(name='root', terminal='pts/0', host='localhost', started=1582366208.0, pid=8927),
suser(name='root', terminal='pts/1', host='192.168.1.4', started=1582370816.0, pid=10099),
suser(name='root', terminal='pts/3', host='192.168.1.4', started=1582369408.0, pid=9787)]
2)boot_time
以时间戳的形式返回系统的启动时间。
In [9]: import datetime
In [10]: psutil.boot_time()
Out[10]: 1582527367.0
In [11]: datetime.datetime.fromtimestamp(psutil.boot_time()).strftime('%Y-%m-%d %H:%M:%S')
Out[11]: '2020-02-24 14:56:07'
3、综合案例:使用psutil实现监控程序
# coding=utf-8
# !/usr/bin/python
import psutil
import datetime
def bytes2human(n):
'''内存单位转换的方法'''
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i, s in enumerate(symbols):
prefix[s] = 1 << (i + 1) * 10
for s in reversed(symbols):
if n >= prefix[s]:
value = float(n) / prefix[s]
return '%.1f%s' % (value, s)
return "%sB" % n
def get_cpu_info():
'''获取CPU使用率'''
cpu_count = psutil.cpu_count()
cpu_percent = psutil.cpu_percent(interval=1)
return dict(cpu_count=cpu_count, cpu_percent=cpu_percent)
def get_memory_info():
'''获取内存信息'''
virtual_mem = psutil.virtual_memory()
mem_total = bytes2human(virtual_mem.total)
mem_percent = virtual_mem.percent
mem_free = bytes2human(virtual_mem.free + virtual_mem.buffers + virtual_mem.cached)
mem_used = bytes2human(virtual_mem.total * mem_percent / 100)
return dict(mem_total=mem_total, mem_percent=mem_percent, mem_free=mem_free, mem_used=mem_used)
def get_disk_info():
'''获取磁盘信息'''
disk_usage = psutil.disk_usage('/')
disk_total = bytes2human(disk_usage.total)
disk_percent = disk_usage.percent
disk_free = bytes2human(disk_usage.free)
disk_used = bytes2human(disk_usage.used)
return dict(disk_total=disk_total, disk_percent=disk_percent, disk_free=disk_free, disk_used=disk_used)
def get_boot_info():
'''获取启动时间'''
boot_time = datetime.datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S")
return dict(boot_time=boot_time)
def collect_monitor_data():
'''集中监控硬件信息'''
data = {}
data.update(get_boot_info())
data.update(get_cpu_info())
data.update(get_memory_info())
data.update(get_disk_info())
print(data)
return data
collect_monitor_data()
执行结果如下
[root@python scripts]# python3 monitor_psutil.py
{'boot_time': '2020-05-06 16:23:37', 'cpu_count': 1, 'cpu_percent': 0.0, 'mem_total': '976.5M', 'mem_percent': 71.4, 'mem_free': '448.0M', 'mem_used': '697.2M', 'disk_total': '17.0G', 'disk_percent': 30.4, 'disk_free': '11.8G', 'disk_used': '5.2G'}
4、psutil进程管理
psutil还提供了作为进程管理的功能函数,包括获取进程列表,判断是否存在,以及进程管理的类封装。
函数 | 详情 |
---|---|
psutil.Process() | 对进程进行封装,可以使用该类的方法获取进行的详细信息,或者给进程发送信号。 |
psutil.pids() | 以列表的形式返回当前正在运行的进程 |
psutil.pid_exists(1) | 判断给点定的pid是否存在 |
psutil.process_iter() | 迭代当前正在运行的进程,返回的是每个进程的Process对象 |
1)Process类
对进程进行封装,可以使用该类的方法获取进行的详细信息,或者给进程发送信号。
In [1]: import psutil
In [2]: init_process = psutil.Process()
In [3]: init_process.cmdline()
Out[3]: ['/usr/local/python38/bin/python3.8', '/usr/local/python38/bin/ipython']
Process类包含很多方法来获取进程的详细信息。下面是几个较常用的方法:
name:获取进程的名称
cmdline:获取启动进程的命令行参数
create_time:获取进程的创建时间(时间戳格式)
num_fds:进程打开的文件个数
num_threads:进程的子进程个数
is_running:判断进程是否正在运行
send_signal:给进程发送信号,类似与os.kill等
kill:发送SIGKILL信号结束进程
terminate:发送SIGTEAM信号结束进程
2)pids
以列表的形式返回当前正在运行的进程。
In [1]: import psutil
In [2]: init_process = psutil.Process()
In [3]: init_process.cmdline()
Out[3]: ['/usr/local/python38/bin/python3.8', '/usr/local/python38/bin/ipython']
In [4]: psutil.pids()[:5]
Out[4]: [1, 2, 3, 5, 7]
3)pid_exists
判断给点定的pid是否存在。
In [5]: psutil.pid_exists(1)
Out[5]: True
In [6]: psutil.pid_exists(10245)
Out[6]: False
4)process_iter
迭代当前正在运行的进程,返回的是每个进程的Process对象,而pids返回的是进程的列表。