Python監控Linux系統(2)

1、使用開源庫監控Linux

在這一小節,咱們將介紹一個在Python生態中普遍使用的開源項目,即psutil。隨後,咱們將使用psutil重構前一小節編寫的監控程序。另外,還會簡單介紹psutil提供的進程管理功能。node

一、psutil介紹

psutil = process and system utilitiespython

  • 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安裝:ios

[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、磁盤、內存、網絡等。類用來實現進程的管理功能。shell

二、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的個數。centos

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的統計信息,包括上下文切換,中斷,軟中斷和系統調用次數。app

In [10]: psutil.cpu_stats()                                      
Out[10]: scpustats(ctx_switches=403960, interrupts=214204, soft_interrupts=217258, syscalls=0)

5)cpu_freq

返回cpu頻率。ionic

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
分析一下

Python監控Linux系統(2)<psutil,Process,pids,pid_exists>

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=<AddressFamily.AF_INET6: 10>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::', port=111), raddr=(), status='LISTEN', pid=6558),
 sconn(fd=7, family=<AddressFamily.AF_INET6: 10>, type=<SocketKind.SOCK_DGRAM: 2>, laddr=addr(ip='::', port=111), raddr=(), status='NONE', pid=6558),
 sconn(fd=8, family=<AddressFamily.AF_INET6: 10>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=6010), raddr=(), status='LISTEN', pid=9047),
 sconn(fd=6, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>,
......

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=<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)],
 'ens37': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.1.131', netmask='255.255.255.255', broadcast='192.168.1.131', ptp=None),
  snicaddr(family=<AddressFamily.AF_INET6: 10>, 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=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=1000, mtu=1500),
 'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536),
 'virbr0-nic': snicstats(isup=False, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=10, mtu=1500),
 'virbr0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=1500),
 'ens33': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=1000, mtu=1500)}

Python監控Linux系統(2)<psutil,Process,pids,pid_exists>

(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'

三、綜合案例:使用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'}

四、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返回的是進程的列表。

相關文章
相關標籤/搜索