原文地址: Python 自动化运维快速入门 (第2版)
系统信息监控 (psutil)
在 Python 中获取系统信息最便捷的模块是 psutil
(Process and System Utilities)。通过几行代码就可以获取系统的相关信息,而且还是跨平台库。psutil
不属于标准库,需要手动安装。
1 | pip install psutil |
下面使用示例演示 psutil
的相关使用方法:
监控 CPU 信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27In [1]: import psutil # 导入 psutil 模块
In [2]: psutil.cpu_times() # 获取 CPU 时间占用的详细信息
Out[2]: scputimes(user=7397.35, nice=0.0, system=2314.96, idle=33723.1)
In [3]: psutil.cpu_times(percpu=True) # 获取每个 CPU 的时间占用详细信息
Out[3]:
[scputimes(user=1454.19, nice=0.0, system=605.03, idle=3411.11),
scputimes(user=534.58, nice=0.0, system=124.16, idle=4810.5),
scputimes(user=1322.29, nice=0.0, system=493.12, idle=3653.84),
scputimes(user=533.39, nice=0.0, system=112.36, idle=4823.5),
scputimes(user=1254.76, nice=0.0, system=417.31, idle=3797.18),
scputimes(user=553.47, nice=0.0, system=104.82, idle=4810.96),
scputimes(user=1193.63, nice=0.0, system=365.73, idle=3909.89),
scputimes(user=566.68, nice=0.0, system=98.98, idle=4803.58)]
In [4]: psutil.cpu_count() # CPU 的逻辑核数(线程数)
Out[4]: 8
In [5]: psutil.cpu_count(logical=False) # CPU 的物理核数
Out[5]: 4
In [6]: psutil.cpu_percent() # CPU 使用率
Out[6]: 7.3
In [7]: psutil.cpu_percent(percpu=True) # 每个 CPU 的使用率
Out[7]: [23.0, 1.2, 14.1, 1.0, 11.2, 1.0, 9.4, 1.1]监控内存信息
1
2In [8]: psutil.virtual_memory()
Out[8]: svmem(total=8589934592, available=2413162496, percent=71.9, used=4671184896, free=124600320, active=2470084608, inactive=2282106880, wired=2201100288)这里的数值是以字节为单位显示的
监控磁盘信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15In [9]: psutil.disk_partitions() # 磁盘分区信息
Out[9]:
[sdiskpart(device='/dev/disk1s5s1', mountpoint='/', fstype='apfs', opts='ro,local,rootfs,dovolfs,journaled,multilabel', maxfile=255, maxpath=1024),
sdiskpart(device='/dev/disk1s4', mountpoint='/System/Volumes/VM', fstype='apfs', opts='rw,noexec,local,dovolfs,dontbrowse,journaled,multilabel,noatime', maxfile=255, maxpath=1024),
sdiskpart(device='/dev/disk1s2', mountpoint='/System/Volumes/Preboot', fstype='apfs', opts='rw,local,dovolfs,dontbrowse,journaled,multilabel', maxfile=255, maxpath=1024),
sdiskpart(device='/dev/disk1s6', mountpoint='/System/Volumes/Update', fstype='apfs', opts='rw,local,dovolfs,dontbrowse,journaled,multilabel', maxfile=255, maxpath=1024),
sdiskpart(device='/dev/disk1s1', mountpoint='/System/Volumes/Data', fstype='apfs', opts='rw,local,dovolfs,dontbrowse,journaled,multilabel', maxfile=255, maxpath=1024),
sdiskpart(device='/dev/disk0s2', mountpoint='/Volumes/Win 10 Pro x64', fstype='ntfs', opts='ro,local,ignore-ownership,multilabel', maxfile=255, maxpath=1024),
sdiskpart(device='/dev/disk1s5', mountpoint='/System/Volumes/Update/mnt1', fstype='apfs', opts='rw,local,dovolfs,dontbrowse,journaled,multilabel', maxfile=255, maxpath=1024)]
In [10]: psutil.disk_usage('/') # 磁盘使用情况
Out[10]: sdiskusage(total=404417220608, used=30545170432, free=286423461888, percent=9.6)
In [12]: psutil.disk_io_counters() # 磁盘的 IO 信息
Out[12]: sdiskio(read_count=541099, write_count=197892, read_bytes=15135201792, write_bytes=6088396800, read_time=247429, write_time=17301)监控网络信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23In [2]: psutil.net_if_addrs() # 查看网络接口 IP 地址
Out[2]:
{'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None),
snicaddr(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
snicaddr(family=<AddressFamily.AF_PACKET: 17>, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)],
'ens192': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.55.19', netmask='255.255.255.0', broadcast='192.168.55.255', ptp=None),
snicaddr(family=<AddressFamily.AF_INET6: 10>, address='fe80::17e7:3cb7:24f5:cf13%ens192', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
snicaddr(family=<AddressFamily.AF_INET6: 10>, address='fe80::b167:e2b3:3c10:bf66%ens192', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
snicaddr(family=<AddressFamily.AF_PACKET: 17>, address='00:0c:29:71:ce:dd', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
In [3]: psutil.net_if_stats() # 查看网络接口状态
Out[3]:
{'ens192': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=10000, mtu=1500),
'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
In [4]: psutil.net_connections() # 此方法需要 root 权限
Out[4]:
[sconn(fd=47, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='192.168.55.19', port=4505), raddr=addr(ip='192.168.55.66', port=18668), status='ESTABLISHED', pid=1357),
sconn(fd=23, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='192.168.55.19', port=4505), raddr=addr(ip='192.168.55.46', port=28864), status='ESTABLISHED', pid=1357),
sconn(fd=31, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='192.168.55.19', port=4505), raddr=addr(ip='192.168.55.70', port=12650), status='ESTABLISHED', pid=1357),
sconn(fd=48, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='192.168.55.19', port=4505), raddr=addr(ip='192.168.55.55', port=35596), status='ESTABLISHED', pid=1357),
sconn(fd=30, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='192.168.55.19', port=4505), raddr=addr(ip='192.168.55.111', port=58854), status='ESTABLISHED', pid=1357),
...获取进程信息
1
2
3
4
5
6
7
8
9
10In [20]: for pid in psutil.pids(): # 获取所有进程的 PID
...: print(pid, end=',')
...:
0,1,74,75,78,79,81,83,86,88,89,90,91,92,95,97,98,101,102,104,108,109,110,111,114,117,118,120,121,122,123,124,125,126,128,129,130,131,132,134,135,138,139,140,142,143,145,146,147,148,149,150,151,152,153,154,155,176,183,184,188,192,193,195,196,204,205,206,208,209,214,215,219,227,228,229,230,232,233,235,237,239,240,241,242,243,245,246,247,248,249,250,256,257,259,265,266,267,269,271,272,291,292,297,300,303,306,307,308,309,310,314,315,316,317,318,319,321,322,323,324,325,326,328,329,333,334,336,338,339,340,341,345,346,347,355,356,357,359,361,363,366,371,372,373,374,376,377,378,380,381,383,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,413,414,415,417,418,419,420,422,423,424,425,426,427,428,430,431,433,434,436,437,438,439,440,441,442,443,444,447,448,449,450,451,452,453,455,456,459,460,461,462,464,465,467,468,469,470,471,472,473,474,475,476,477,478,480,482,483,484,485,487,490,493,494,496,497,503,520,537,539,547,548,579,580,607,613,618,623,628,658,659,753,755,756,788,964,987,989,1021,1022,1026,1086,1104,1157,1173,1242,1321,1340,1347,1349,1353,1354,1357,1359,1360,1361,1364,1365,1368,1369,1370,1371,1372,1374,1376,1377,1379,1395,1399,1406,1410,1416,1417,1419,1420,1422,1428,1430,1431,1434,1435,1436,1437,1438,1441,1443,1444,1445,1448,1449,1450,1451,1452,1453,1475,1476,1484,1485,1487,1490,1503,1505,1510,1535,1536,1557,1558,2338,2344,2345,2346,2347,2348,2349,2350,2351,2352,2354,2355,2366,2367,2477,2478,2749,2753,2754,2758,2759,2760,2778,2781,2782,2783,2784,2785,2793,3585,3586,3587,3588,3589,3754,3755,3757,3759,3760,3762,3763,3764,3765,3766,3767,3768,3769,3771,3777,3791,3830,4218,4313,4689,4696,4728,5042,5099,5110,5127,5169,5170,5171,5182,5183,5218,5245,5255,5447,5448,5449,5450,5451,5452,5828,5829,5830,5832,5833,6241,6245,6246,6247,6248,6251,6255,6256,6257,6258,6260,6268,6269,6271,6273,6276,6278,6279,6280,6281,6285,6287,6302,6304,6372,6373,6374,6376,6377,6378,6379,6380,6381,6382,6383,6384,6385,6402,7237,7777,7959,8028,8029,8030,8031,8040,
In [21]: for proc in psutil.process_iter(attrs=['pid', 'name', 'username']):
...: if proc.info['name'].startswith('WeChat'): # 获取微信程序相关的信息
...: print(proc.info)
...:
{'name': 'WeChat', 'username': 'wanwu', 'pid': 438}前面使用了 psutil.process_iter 获取了进程相关的信息,返回结果是一个可迭代对象,每个元素的 info 是一个字典,通过字典可以获取我们关心的信息。
获取进程的其他信息如 CPU 占用,内存占用,进程的线程数等,还可以使用以下方式:
1
2
3
4
5
6
7
8
9
10
11In [22]: psutil.Process(438).cpu_times() # 获取 CPU 占用时间
Out[22]: pcputimes(user=63.759470592, system=47.152463872, children_user=0.0, children_system=0.0)
In [23]: psutil.Process(438).memory_info() # 获取内存信息,rss 就是实际占用的内存
Out[23]: pmem(rss=96104448, vms=7012143104, pfaults=507062, pageins=29026)
In [25]: psutil.Process(438).num_threads() # 获取线程数
Out[25]: 35
In [26]: psutil.Process(438).memory_percent() # 获取内存使用率
Out[26]: 1.1195659637451172下面是几种常见的使用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68import os
import psutil
import signal
# 按名称查找进程相关的信息
def find_process_by_name(name):
""" Return a list of processes matching 'name'. """
proc_list = []
for p in psutil.process_iter(attrs=['name']):
if p.info['name'] == 'name':
proc_list.append(p)
return proc_list
# 按名称查找进程相关的信息2
def find_process_by_name_2(name):
""" Return a list of processes matching 'name'."""
proc_list = []
for p in psutil.process_iter(attrs=['name', 'exe', 'cmdline']):
if name == p.info['name'] or p.info['exe'] and \
os.path.basename(p.info['exe']) == name or \
p.info['cmdline'] and p.info['cmdline'][0] == name:
proc_list.append(p)
return proc_list
# 杀掉进程树
def kill_proc_tree(pid, sig=signal.SIGTERM, include_parent=True, timeout=None, on_terminate=None):
"""
Kill a Process tree (including grandchildren) with signal "sig" and return a tuple.
"on terminate", if specified,is a callback function which is called as soon as a child terminates.
"""
if pid == os.getgid():
raise RuntimeError("I refuse to kill myself")
parent = psutil.Process(pid)
children = parent.children(recursive=True)
if include_parent:
children.append(parent)
for p in children:
p.send_signal(sig)
gone, alive = psutil.wait_procs(children, timeout=timeout, callback=on_terminate)
return (gone, alive)
# 杀掉子进程
def reap_children(timeout=3):
"Tries hard to terminate and ultimately kill all the children of this process."
def on_terminate(proc):
print("process {} terminated with exit code {}".format(proc, proc.returncode))
procs = psutil.Process().children()
# Send SIGTERM
for p in procs:
p.terminate()
gone, alive = psutil.wait_procs(procs, timeout=timeout, callback=on_terminate)
if alive:
# Send SIGKILL
for p in alive:
print("process {} survived SIGTERM; tring SIGKILL" % p)
p.kill()
gone, alive = psutil.wait_procs(alive, timeout=timeout, callback=on_terminate)
if alive:
# give up
for p in alive:
print("process {} survived SIGKILL; give up" % p)
文件系统监控 (watchdog)
在 Python 中监控文件系统信息的模块是 watchdog
,watchdog
不是标准库,需要手动安装
1 | pip install watchdog |
watchdog 用来监控指定目录/文件的变化,如添加文件或目录,修改文件内容,重命名文件或目录等,每种变化都会产生一个事件,且有一个特定的事件类与之对应,然后通过事件处理类来处理对应的事件,怎么样处理事件完全可以自定义,只需要集成事件处理类的基类并重写对应实例方法。
以下示例是 watchdog 的使用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48import time
from watchdog.observers import Observer
from watchdog.events import *
class FileEventHandler(FileSystemEventHandler):
def __init__(self):
FileSystemEventHandler.__init__(self)
def on_moved(self, event):
now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
if event.is_directory:
print(f"{now} 文件夹由 {event.src_path} 移动至 {event.dest_path}")
else:
print(f"{now} 文件由 {event.src_path} 移动至 {event.dest_path}")
def on_created(self, event):
now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
if event.is_directory:
print(f"{now} 文件夹 {event.src_path} 创建")
else:
print(f"{now} 文件 {event.src_path} 创建")
def on_deleted(self, event):
now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
if event.is_directory:
print(f"{now} 文件夹 {event.src_path} 删除")
else:
print(f"{now} 文件 {event.src_path} 删除")
def on_modified(self, event):
now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
if event.is_directory:
print(f"{now} 文件夹 {event.src_path} 修改")
else:
print(f"{now} 文件 {event.src_path} 修改")
if __name__ == '__main__':
observer = Observer()
obj_path = r'/Users/wanwu/pyscripts'
event_handler = FileEventHandler()
observer.schedule(event_handler, obj_path, True) # True 表示递归子目录
print(f"监控目录 {obj_path}")
observer.start()
observer.join()运行结果如下
1
2
3
4
5
6
7
8
9
10
11监控目录 /Users/wanwu/pyscripts
2021-10-22 16:37:25 文件 /Users/wanwu/pyscripts/test/test1.txt 创建
2021-10-22 16:37:25 文件夹 /Users/wanwu/pyscripts/test 修改
2021-10-22 16:37:54 文件 /Users/wanwu/pyscripts/test/test1.txt 删除
2021-10-22 16:37:54 文件夹 /Users/wanwu/pyscripts/test 修改
2021-10-22 16:38:20 文件 /Users/wanwu/pyscripts/.idea/workspace.xml 修改
2021-10-22 16:38:20 文件 /Users/wanwu/pyscripts/.idea/workspace.xml~ 创建
2021-10-22 16:38:20 文件夹 /Users/wanwu/pyscripts/.idea 修改
2021-10-22 16:38:20 文件 /Users/wanwu/pyscripts/.idea/workspace.xml~ 修改
2021-10-22 16:38:20 文件 /Users/wanwu/pyscripts/.idea/workspace.xml~ 删除
2021-10-22 16:38:20 文件夹 /Users/wanwu/pyscripts/.idea 修改
运维中以下场景十分适合使用 watchdog
- 监控文件系统中文件或目录的增、删、改情况;
- 当特定文件被创建、删除、修改、移动时执行相应的任务。