Python利用psutil實現獲取硬件,網絡和進程信息

楔子

Python 有一個第三方模塊叫 psutil,專門用來獲取操作系統以及硬件相關的信息,比如:CPU、磁盤、網絡、內存等等。下面來看一下它的用法,不過在使用之前需要先安裝,直接 pip install psutil 即可。

CPU 相關

獲取 CPU 的邏輯核心數量

import psutil
print(psutil.cpu_count())  # 12

# 或者使用 multiprocessing
import multiprocessing
print(multiprocessing.cpu_count())  # 12

獲取 CPU 的物理核心數量

import psutil
print(psutil.cpu_count(logical=False))  # 6

結果為 6,說明是 6 核超線程;如果 CPU 的物理核心數和邏輯核心數相等,也為 12,則說明是 12 核非超線程。

統計 CPU 的用戶/系統/空閑時間

import psutil

print(psutil.cpu_times())
"""
scputimes(user=84732.10937499999, 
          system=37132.85937500023, 
          idle=2003964.1249999998, 
          interrupt=3500.765625, 
          dpc=1089.6875)
"""

# 還有一個 psutil.cpu_times_percent() 
# 功能與之類似, 隻不過返回的是比例

該函數返回的是一個 namedtuple,後面凡是結構長的和這裡類似的,都是 namedtuple。補充一下,如果你的程序中需要創建大量的對象,並且該對象的屬性固定不變,那麼不妨使用 namedtuple,相比使用字典,能節省大量空間。

查看 CPU 的使用率

import psutil

for x in range(3):
    # interval:表示每隔 0.5s 刷新一次
    # percpu:為 True 表示查看所有的 cpu 使用率
    print(psutil.cpu_percent(interval=0.5, percpu=True))
"""
[9.1, 3.1, 12.5, 3.1, 15.6, 0.0, 6.2, 0.0, 12.5, 50.0, 9.4, 3.1]
[9.1, 6.2, 12.5, 6.2, 3.1, 0.0, 0.0, 3.1, 0.0, 15.6, 3.1, 0.0]
[0.0, 0.0, 15.6, 0.0, 6.2, 0.0, 6.2, 25.0, 3.1, 9.4, 6.2, 0.0]
"""
# 我這裡 cpu 的邏輯數量是 12
# 所以每個列表裡面有 12 個元素

查看 CPU 的統計信息

包括上下文切換、中斷、軟中斷,以及系統調用次數等等。

import psutil

print(psutil.cpu_stats())
"""
scpustats(ctx_switches=3346512902, 
          interrupts=2288572793, 
          soft_interrupts=0, 
          syscalls=3324041552)
"""

查看 CPU 的頻率

import psutil

print(psutil.cpu_freq())
"""
scpufreq(current=2208.0, min=0.0, max=2208.0)
"""

內存相關

查看內存使用情況

import psutil

print(psutil.virtual_memory())
"""
svmem(total=17029259264, 
      available=7698505728, 
      percent=54.8, 
      used=9330753536, 
      free=7698505728)
"""

total 表示總內存,available 表示可用內存,percent 表示內存使用率,used 表示已使用的內存,free 表示可用內存。

所以 available 加上 used 等於 total,used 除以 total 再乘以 100 等於 percent。

查看交換內存信息

import psutil

print(psutil.swap_memory())
"""
sswap(total=3087007744, 
      used=4509839360, 
      free=-1422831616, 
      percent=146.1, 
      sin=0, 
      sout=0)
"""

說到內存,有物理內存、交換內存、虛擬內存,這三者有什麼區別呢?用大白話解釋就是:

1)物理內存是實際的內存條提供的臨時數據存儲空間,在 Windows 上右鍵點擊計算機,再點擊屬性時,上面顯示的安裝內存(RAM)就是電腦的物理內存。這些內存是實際存在的,在你不給機器增加內存條的時候是不會改變的。

2)交換內存通常在頁面調度和交換進程數據時使用,相當於在進行內存整理的時候,會先把部分數據放在硬盤的某塊區域。類似我們整理衣櫃,衣服一多直接整理會很麻煩,因此會先把部分衣服拿出來放在其它地方,等衣櫃裡的衣服整理完瞭,再把放在其它地方的衣服拿回來。

這個其它地方在計算機中則代表硬盤的某塊區域,也就是我們所說的交換區。通常使用交換內存是因為物理內存不足導致的,正所謂衣櫃,如果足夠大的話就沒必要拿出部分衣服放在其它地方, 直接在衣櫃裡就能解決瞭。

3)最後是虛擬內存,當操作文件,可執行程序等等,那麼首先要把它們從磁盤讀取到內存中,因此 CPU 除瞭自己那一部分小小的空間外,要想操作數據,隻能操作內存裡的數據。

但是當內存不夠瞭,那麼會在硬盤上開辟一份虛擬內存,將物理內存裡的部分數據放在虛擬內存當中。硬盤的空間很大,即使普通電腦安裝的固態硬盤也有一百個 G,因此可以拿出一部分充當虛擬內存。

不過虛擬內存雖說是內存,但畢竟在硬盤上,速度和 CPU 直接從物理內存裡讀取數據相差甚遠。這也是為什麼要將經常被訪問的熱點數據放在 Redis 緩存裡,而不是放在硬盤或者數據庫上。

磁盤相關

查看磁盤分區、磁盤使用率和磁盤 IO 信息

import psutil

print(psutil.disk_partitions())
"""
[sdiskpart(device='C:\\', mountpoint='C:\\', fstype='NTFS', opts='rw,fixed', maxfile=255, maxpath=260),
 sdiskpart(device='D:\\', mountpoint='D:\\', fstype='NTFS', opts='rw,fixed', maxfile=255, maxpath=260),
 sdiskpart(device='E:\\', mountpoint='E:\\', fstype='NTFS', opts='rw,fixed', maxfile=255, maxpath=260)]
"""

可以看到一共有三個盤符,fstype 表示文件系統類型,這裡是 NTFS;opts 中的 rw 表示可讀寫。

該函數還可以接收一個參數 all,默認為 False。如果指定為 True,在 Linux 上返回的內容還會包含 /proc 等特殊文件系統的掛載信息。由於我這裡是 Windows,所以兩者沒區別。

查看某個磁盤使用情況

import psutil

print(psutil.disk_usage("C:\\"))
"""
sdiskusage(total=267117391872, 
           used=96894304256, 
           free=170223087616, 
           percent=36.3)
"""

查看磁盤 IO 統計信息

import psutil

print(psutil.disk_io_counters())
"""
sdiskio(read_count=1172461, 
        write_count=2153031, 
        read_bytes=36854982144, 
        write_bytes=52718300160, 
        read_time=551, 
        write_time=1437)
"""
  • read_count :讀次數
  • write_count:寫次數
  • read_bytes:讀的字節數
  • write_bytes:寫的字節數
  • read_time:讀時間
  • write_time:寫時間

以上返回的是所有磁盤加起來的統計信息,我們可以指定 perdisk=True,分別列出每一個分區的統計信息。

網絡相關

查看網卡的網絡 IO 統計信息

import psutil

print(psutil.net_io_counters())
"""
snetio(bytes_sent=175995567, 
       bytes_recv=2849015622, 
       packets_sent=1052206, 
       packets_recv=3050302, 
       errin=0, 
       errout=0, 
       dropin=3491, 
       dropout=0)
"""
# bytes_sent: 發送的字節數
# bytes_recv: 接收的字節數
# packets_sent: 發送的包數據量
# packets_recv: 接收的包數據量
# errin: 接收包時, 出錯的次數
# errout: 發送包時, 出錯的次數
# dropin: 接收包時, 丟棄的次數
# dropout: 發送包時, 丟棄的次數

# 裡面還有一個 pernic 參數
# 如果為 True, 則列出所有網卡的信息
print(psutil.net_io_counters(pernic=True))
"""
{'以太網': snetio(bytes_sent=178716616, 
                 bytes_recv=2866823348, 
                 packets_sent=1058190, 
                 packets_recv=3102852, 
                 errin=0, errout=0, 
                 dropin=3491, dropout=0), 
'WLAN': snetio(bytes_sent=0, bytes_recv=0, 
               packets_sent=0, packets_recv=0, 
               errin=0, errout=0, 
               dropin=0, dropout=0), 
'本地連接* 3': snetio(bytes_sent=0, bytes_recv=0, 
                    packets_sent=0, packets_recv=0, 
                    errin=0, errout=0, 
                    dropin=0, dropout=0), 
'本地連接* 4': snetio(bytes_sent=0, bytes_recv=0, 
                     packets_sent=0, packets_recv=0,
                     errin=0, errout=0, 
                     dropin=0, dropout=0), 
'藍牙網絡連接': snetio(bytes_sent=0, bytes_recv=0, 
                    packets_sent=0, packets_recv=0,
                    errin=0, errout=0, 
                    dropin=0, dropout=0), 
'Loopback Pseudo-Interface 1': snetio(bytes_sent=0, 
                                      bytes_recv=0, 
                                      packets_sent=0, 
                                      packets_recv=0, 
                                      errin=0, errout=0, 
                                      dropin=0, dropout=0)}
"""

查看網絡接口信息

import psutil

# 以字典的形式返回網卡的配置信息
# 包括 IP 地址、Mac地址、子網掩碼、廣播地址等等
print(psutil.net_if_addrs())
"""
{'以太網': [
    snicaddr(family=<AddressFamily.AF_LINK: -1>, 
             address='9C-7B-EF-15-FC-2F', netmask=None, 
             broadcast=None, ptp=None), 
    snicaddr(family=<AddressFamily.AF_INET: 2>, 
             address='192.168.4.150', netmask='255.255.240.0', 
             broadcast=None, ptp=None), 
    snicaddr(family=<AddressFamily.AF_INET6: 23>, 
             address='fe80::4826:a6a6:b5f4:3647', 
             netmask=None, broadcast=None, ptp=None)], 

'WLAN': [...], 
'本地連接* 3': [...], 
'本地連接* 4': [...], 
'藍牙網絡連接': [...], 
'Loopback Pseudo-Interface 1': [...]}
"""

# 返回網卡的詳細信息, 包括是否啟動、通信類型、傳輸速度、mtu
print(psutil.net_if_stats())
"""
{'以太網': snicstats(isup=True, 
                    duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, 
                    speed=1000, mtu=1500), 
'藍牙網絡連接': snicstats(isup=False, 
                        duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, 
                        speed=3, mtu=1500), 
'Loopback Pseudo-Interface 1': snicstats(isup=True, 
                                         duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, 
                                         speed=1073, mtu=1500), 
'WLAN': snicstats(isup=False, 
                  duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, 
                  speed=0, mtu=1500), 
'本地連接* 3': snicstats(isup=False, 
                        duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, 
                        speed=0, mtu=1500), 
'本地連接* 4': snicstats(isup=False, 
                        duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, 
                        speed=0, mtu=1500)}
"""

查看當前機器的網絡連接

import psutil

# 以列表的形式返回每個網絡連接的詳細信息
# 裡面接受一個參數, 默認是 "inet"
# 當然我們也可以指定為其它, 比如 "tcp"
print(psutil.net_connections())
"""
[sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, 
       type=<SocketKind.SOCK_DGRAM: 2>, 
       laddr=addr(ip='192.168.4.150', port=137), 
       raddr=(), status='NONE', pid=4),
 sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, 
       type=<SocketKind.SOCK_DGRAM: 2>, 
       laddr=addr(ip='127.0.0.1', port=54872), 
       raddr=(), status='NONE', pid=11652),
 sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, 
       type=<SocketKind.SOCK_STREAM: 1>, 
       laddr=addr(ip='192.168.4.150', port=11253), 
       raddr=addr(ip='117.50.19.136', port=80), 
       status='CLOSE_WAIT', pid=11568),
 ....
 ....
 ....
]
"""

是不是很方便呢?在 Linux 中有兩個命令可以做到這一點,分別是 netstat 和 ss。另外該函數會返回所有的連接信息,所以當連接數很多的時候,會占用較高的內存。

查看當前登錄的用戶信息

import psutil

print(psutil.users())
"""
[suser(name='satori', terminal=None, host='0.0.0.0', 
       started=1609841661.0, pid=None)]
"""

查看系統的啟動時間

import psutil
from datetime import datetime

print(psutil.boot_time())  # 1654012221.2945454
print(
    datetime.fromtimestamp(psutil.boot_time())
)  # 2022-05-31 23:50:21.294545

進程管理

psutil 還提供瞭很多和進程管理相關的功能函數,非常的豐富,我們來看一下。

查看當前存在的所有進程的 pid

import psutil

print(psutil.pids())
"""
[0, 4, 148, 532, 668, 796, 904, 912, 976, ...]
"""

查看某個進程是否存在

import psutil

print(psutil.pid_exists(22333))  # False
print(psutil.pid_exists(532))  # True

返回所有進程(Process)對象組成的迭代器

import psutil

print(psutil.process_iter())
"""
<generator object process_iter at 0x000...>
"""

# 遍歷的話,會得到每一個進程對象
# 進程對象在 psutil 裡面的類型是 Process

根據 pid 獲取一個進程對應的 Process 對象

import psutil

print(psutil.Process(14124))
"""
psutil.Process(pid=14124, name='WeChat.exe', 
               status='running', started='10:54:43')
"""
# 進程名稱是 WeChat.exe,狀態為運行中
# 啟動時間是早上 10 點 54 分

進程管理操作

我們說根據 pid 可以獲取一個進程對應的 Process 對象,而這個對象裡面包含瞭該進程的全部信息。

import psutil

p = psutil.Process(14124)

# 進程名稱
print(p.name())
"""
WeChat.exe
"""

# 進程的exe路徑
print(p.exe())
"""
D:\WeChat\WeChat.exe
"""

# 進程的工作目錄
print(p.cwd())
"""
D:\WeChat
"""

# 進程啟動的命令行
print(p.cmdline())
"""
['D:\\WeChat\\WeChat.exe']
"""

# 當前進程id
print(p.pid)
"""
14124
"""

# 父進程id
print(p.ppid())
"""
8860
"""

# 父進程
print(p.parent())
"""
psutil.Process(pid=8860, name='explorer.exe', 
               status='running', started='10:53:58')
"""

# 子進程列表
print(p.children())
"""
[psutil.Process(pid=6852, name='WechatBrowser.exe', 
                status='running', started='10:54:59'), 
 psutil.Process(pid=1960, name='WeChatPlayer.exe',
                status='running', started='10:54:59'), 
 psutil.Process(pid=10432, name='WeChatApp.exe', 
                status='running', started='10:55:33')]
"""

# 進程狀態
print(p.status())
"""
running
"""

# 進程用戶名
print(p.username())
"""
LAPTOP-264ORES3\satori
"""

# 進程創建時間,返回時間戳
print(p.create_time())
"""
1654570483.2370846
"""

# 進程終端
# 在windows上無法使用
try:
    print(p.terminal())
except Exception as e:
    print(e)
"""
'Process' object has no attribute 'terminal'
"""

# 進程使用的cpu時間
print(p.cpu_times())
"""
pcputimes(user=27.8125, system=13.484375,
          children_user=0.0, children_system=0.0)
"""

# 進程所使用的的內存
print(p.memory_info())
"""
pmem(rss=110141440, vms=116899840, 
     num_page_faults=661356, peak_wset=221048832, 
     wset=110141440, peak_paged_pool=834824, 
     paged_pool=806216, peak_nonpaged_pool=144584, 
     nonpaged_pool=78560, pagefile=116899840, 
     peak_pagefile=197505024, private=116899840)
"""

# 進程打開的文件
print(p.open_files())

# 進程相關的網絡連接
print(p.connections())
"""
[pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, 
       type=<SocketKind.SOCK_STREAM: 1>, 
       laddr=addr(ip='192.168.4.150', port=7693), 
       raddr=addr(ip='58.251.111.106', port=8080), 
       status='ESTABLISHED'), 
 pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, 
       type=<SocketKind.SOCK_STREAM: 1>, 
       laddr=addr(ip='127.0.0.1', port=8680), 
       raddr=(), status='LISTEN')]
"""

# 進程內的線程數量,這個進程開啟瞭多少個線程
print(p.num_threads())  # 58

# 這個進程內的所有線程信息
print(p.threads())
"""
[pthread(id=14128, user_time=11.3125, system_time=7.578125), 
 pthread(id=13428, user_time=0.0, system_time=0.0), 
 pthread(id=13616, user_time=0.0, system_time=0.0), 
 pthread(id=13600, user_time=0.015625, system_time=0.328125),
 pthread(id=7364, user_time=0.078125, system_time=0.015625),
 ... 
 ]
"""

# 進程的環境變量
print(p.environ())

# 結束進程
# 執行之後微信就會被強制關閉, 這裡就不試瞭
# p.terminal()

我們還可以調用 psutil.test 來模擬 ps 命令。

import psutil

psutil.test()

輸出如下:

它是怎麼做的呢?還記得我們之前說的 process_iter 嗎?會返回所有進程的 Process 對象,直接依次輸出裡面的信息即可。同理,我們也可以通過 process_iter 找到某一個進程對應的進程 id。

import psutil

for prcs in psutil.process_iter():
    if prcs.name().lower() == "wechat.exe":
        print(prcs)
"""
psutil.Process(pid=14124, name='WeChat.exe', 
               status='running', started='10:54:43')
"""

有瞭這個操作之後,我們便可以找到對應的進程,然後借助操作系統的 kernal 修改進程內部的數據。

到此這篇關於Python利用psutil實現獲取硬件,網絡和進程信息的文章就介紹到這瞭,更多相關Python psutil獲取信息內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: