psutil – Utilidades multiplataforma para procesos



Introducción

psutil es un módulo que provee una interfaz para obtener información de un determinado proceso y su utilización del sistema. Soporta Linux, Windows, OSX, FreeBSD y Sun Solaris (32 y 64 bits) y Python 2.6, 2.7 y 3.4 o mayores.

Instalación

El método recomendado es vía pip:

pip install psutil

Como alternativa, para instalarlo desde las fuentes, puedes encontrar las descargas de la última versión en este enlace. Las versiones anteriores (y obsoletas) se encuentran en esta página.

Para Windows, el paquete provee instaladores. En las demás plataformas, una vez descargadas las fuentes:

python setup.py install

Aplicación

Para una explicación completa véase la documentación oficial.

Abrir un proceso

La clase principal psutil.Process representa un proceso abierto del cual queramos extraer información. Una instancia de ésta se crea a partir de un ID (el del proceso) denominado pid, que debe ser pasado como parámetro. Por ejemplo, el siguiente código abre el proceso con ID 1500 e imprime su nombre:

import psutil

p = psutil.Process(1500)
print(p.name)

Si el pid no existe, la excepción NoSuchProcess es lanzada. Para obtener el pid de nuestro proceso, puede utilizarse:

import os
import psutil

p = psutil.Process(os.getpid())
print(p.name)

Imprime «python» o «python.exe» en Windows.

No hay una función en el módulo que tome el nombre de un proceso y retorne el pid. Sin embargo, sabiendo que pustil.process_iter() retorna un iterador con todos los procesos abiertos, podemos hacer la nuestra:

def get_process(process_name):
    for process in psutil.process_iter():
        if process.name == process_name:
            return process

De esta manera, podemos crear una instancia de la clase Process especificando el nombre del proceso en lugar de su ID.

p = get_process("firefox")  # firefox.exe en Windows
if p is not None:
    print(p.name)
else:
    print("No se ha encontrado el proceso.")

Obtener información

Hay gran cantidad de funciones en el módulo para obtener datos de un proceso abierto, no pretendo explicarlas todas pero sí las más importantes.

El siguiente código da a conocer varios atributos abriendo el proceso firefox e imprimiendo algo de información:

from psutil import Process
from datetime import datetime

p = Process(3356)
print("Proceso: " + p.name)
print("ID: %d" % p.pid)
print("ID del proceso padre: %d" % p.ppid)
print("Ruta del proceso: " + p.exe)
print("Llamado como: " + str(p.cmdline))
print("Llamado por el usuario: " + p.username)
print("Estado: " + str(p.status))
print(datetime.fromtimestamp(p.create_time).strftime(
    "Creado el %Y-%m-%d a las %H:%M:%S"))

Esto imprime algo como:

Proceso: firefox.exe
ID: 3356
ID del proceso padre: 1156
Ruta del proceso: C:\Archivos de programa\Mozilla Firefox\firefox.exe
Llamado como: ['C:\\Archivos de programa\\Mozilla Firefox\\firefox.exe']
Llamado por el usuario: Administrador
Estado: running
Creado el 2013-10-06 a las 13:13:47

El proceso padre es aquel que llamó, en este caso, a firefox.exe. Para obtener información sobre él:

print("Proceso padre: ID=%d Nombre=%s" % (p.parent.pid, p.parent.name))

En mi caso imprime:

Proceso padre: ID=1156 Nombre=explorer.exe

El atributo Process.parent es otra instancia de Process que contiene la información del proceso padre.

Process.cmdline contiene información sobre cómo fue llamado el proceso. Por ejemplo, si un script de python fue llamado como «python script.py -400 -500 -noconsole», el contenido del atributo es:

["ubicacion/python", "script.py", "-400", "-500", "-noconsole"]

Process.status retorna el estado del proceso. El valor es una de las constantes STATUS_*. Por ejemplo, psutil.STATUS_RUNNING, psutil.STATUS_ZOMBIE.

Process.create_time contiene el horario en el que ha sido creado. Sin embargo, es expresado como un número de coma flotante (1381076027.0, por ejemplo); por lo que es necesario el uso del módulo datetime para mostrarlo de una manera más legible.

Process.get_num_threads() retorna la cantidad de threads lanzados por el proceso. Por ejemplo:

from psutil import Process
from os import getpid
from time import sleep
from threading import Thread

p = Process(getpid())
print(p.get_num_threads())  # 1
Thread(target=sleep, args=(3,)).start()
print(p.get_num_threads())  # 2

Y para obtener información de cada uno de ellos:

for thread in p.get_threads():
    print(thread.id, thread.user_time, thread.system_time)

Por último, para medir el uso de cada proceso está, entre otras, la función Process.get_cpu_percent():

p = Process(getpid())
print("%f %%" % p.get_cpu_percent())

En mi caso imprime 0.0 %. Podemos probar su efectividad realizando alguna tarea que requiera de un procesamiento mayor:

from psutil import Process
from threading import Thread
from os import getpid

def worker():
    for i in range(1000):
        i**i

p = Process(getpid())
Thread(target=worker).start()
print("%f %%" % p.get_cpu_percent()) # 100.0 %

Comportamiento

Para controlar el comportamiento de un proceso existen las funciones send_signal(), suspend(), resume(), terminate() y kill(). Como ya habrás deducido, sirven para enviar un mensaje a un proceso, suspenderlo (pausar todas sus tareas), reactivar las tareas, terminarlo y matarlo, respectivamente. La diferencia entre terminate() y kill() es que la primera espera a que el proceso complete sus tareas y luego lo termina; la segunda cierra el proceso al instante. En Windows, terminate() es un alias para kill().

Ejemplo:

from psutil import Process
from time import sleep

p = Process(6060)
p.suspend()  # Pausar el proceso por 10 segundos.
sleep(10)
p.resume()  # Reactivar.
p.terminate()  # Terminar.

Las contantes de las señales para send_signal() se encuentran definidas en el módulo signal. Por ejemplo, la función terminate() es igual que:

from psutil import Process
from signal import SIGTERM

p = Process(6060)
p.send_signal(SIGTERM)

Más ejemplos

La carpeta examples provee varios scripts a modo de ejemplos. Para psutils 1.1.0 puedes descargarlos de aquí.



2 comentarios.

  1. Hola, muy buena esta guía, me surgió una duda, como le podría hacer si quiero mostrar todos los estados, por ejemplo si está detenido o si es zombie?

    • Recursos Python says:

      Hola, me alegro que te haya sido de utilidad. Con respecto a tu pregunta, verifica el apartado Obtener información, el método status contiene justamente la información que necesitas. En caso que el proceso sea un zombie el valor de retorno será psutil.STATUS_ZOMBIE.

      Un saludo.

Deja una respuesta