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í.

Curso online 👨‍💻

¡Ya lanzamos el curso oficial de Recursos Python en Udemy! Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter en 2024.

Consultoría 💡

Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.

4 comentarios.

  1. Hola, estuve intentando usar la función que pusiste para obtener el PID basándome en un nombre de proceso pero me encuentro que aunque ponga el nombre de un proceso existente la función me devuelve «None».

    (Comprobé con psutil.process_iter() que el proceso en cuestión se esta ejecutando y que tiene ese nombre)

    • Recursos Python says:

      Hola. ¿Probaste con otros procesos? La función simplemenete recorre la lista de procesos y compara sus nombres con el nombre provisto como argumento. En tu caso debe haber algo particular con el nombre del proceso, pero no puedo decirte mucho más sin ver el código. Te invito a que te registres en el foro y comentes el problema con más detalle.

      Saludos

  2. 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