Procfs son unas librerías de Python cuya función es simplemente extraer y parsear información del directorio /proc/ de nuestro sistema GNU/Linux, esto puede ser útil a la hora de desarrollar herramientas que requieran monitorización del sistema o acceder a información acerca del hardware de nuestra máquina, estado de la memoria, procesos…
Podemos realizar su instalación de varias formas, desde repositorio tenemos el paquete python-procfs que nos instalará las dependencias necesarias, con PIP el paquete se llama procfs, o podemos descargarnos el zip de su repositorio Git, descomprimirlo e importar la librería a mano. Su uso es bastante simple aunque he visto poca documentación salvo los ejemplos que muestran en el readme de su Git, al final buceando un poco en el código de la aplicación y usando de guía la pequeña ayuda ofrecida se puede deducir fácilmente las funciones y el cometido de cada una.
Para explicar su uso he realizado una serie de scripts de ejemplo. Para ejecutarlos usaremos el intérprete Python del sistema o un script para automatizar las funciones, debemos asegurarnos de tener las librerías instaladas.
Uso Básico
El uso básico de procfs es muy simple ya que guarda semejanza con la información extraída del directorio /proc/, así que podemos usar esta como guía. Comenzaremos usando el intérprete para mostrar como se importa el objeto y como se asigna y se llama, luego las funciones las puedes copiar y pegar en un script. Para importar la librería usamos la siguiente línea:
from procfs import Proc
Ahora sólo tenemos que crear un objeto proc y comenzar a visualizar la información:
proc = Proc() print proc.loadavg
Esto nos imprimirá por pantalla el diccionario de datos que devuelve la función loadavg los que incluye la carga del sistema, el contador de procesos y el último PID, para poder acceder a estos elementos por separado lo hacemos de la siguiente forma:
def infosystem(): p = Proc() avg = p.loadavg print avg.average
Ahora imprimimos el contenido que hay dentro de average, dentro del objeto loadavg. Para imprimir los elementos por pantalla debemos tratar el diccionario, yo he hecho esta función de ejemplo:
def cargasistema(): proc = Proc() load = proc.loadavg avg = load.average prs = load.entities try: print " ===> Carga del Sistema: "\ + str(avg[1]) + " / " + str(avg[5]) + " / " + str(avg[15]) print " ===> Procesos: "\ + str(prs.current) + " Activos y " + str(prs.total) + " en total" except Exception, e: print "========= ¡ ¡ ¡ ¡ E R R O R ! ! ! ! ==========\n"\ + "===> Se ha producido el siguiente error: \n" print "===> " + e
Como podemos ver, en la función almacenamos el contador de procesos y la carga del sistema, descartando la última parte del diccionario de «last pid». Una vez extraída la información necesaria la formateamos y la imprimimos por pantalla. También he añadido una excepción por si probando me cargaba el script, para que me muestre el error.
Otras de las funciones principales de esta librería es sacar información de meminfo, el fichero de la memoria de /proc/, para su uso he hecho una función de ejemplo, es bastante simple, la función nos devuelve los valores en kilobytes, yo simplemente los he recogido y los he pasado a megabytes, luego imprimo por pantalla la información recogida, en este caso sólo recojo la memoria libre, total y cacheada.
def memoria(): proc = Proc() fr = proc.meminfo.MemFree to = proc.meminfo.MemTotal ca = proc.meminfo.Cached frmb = fr / 1024 tomb = to / 1024 camb = ca / 1024 print " ===> Memoria libre: " + str(frmb) + " Mb de " + str(tomb) + " Mb" print " ===> Memoria cache: " + str(camb) + " Mb"
Para recoger información relacionada con el procesador he tenido que realizar una función que sea capaz contar uno varios núcleos ya que el diccionario nos separa sus items por cpu (como en el /proc/cpuinfo, vamos), en la siguiente función los muestro con un bucle for, en el bucle accede a cada parte de cada procesador y lo muestra por pantalla, en este caso sólo he cogido el id del procesador, el vendor_id, la velocidad del procesador y el nombre del modelo.
def procesador(): proc = Proc() cpus = proc.cpuinfo count = 0 #print cpus print " ===> Informacion del Procesador:" try: for key, val in cpus.items(): count += 1 print "---------------------------------------------------------" print " ===> Informacion de la CPU " + str(count) print " ======> " + val.model_name + " " + val.vendor_id print " ======> Velocidad: " + str(val.cpu_mhz) + " Mhz" print " ======> ID del Procesador: " + str(val.apicid) print " ======> Tamaño de Cache: " + str(val.cache_size) + " KB" print "=========================================================\n" except Exception, e: print "========= ¡ ¡ ¡ ¡ E R R O R ! ! ! ! ==========\n" + "===> Se ha producido el siguiente error: \n" print "===> " + e
Información de los procesos
A la hora de recoger información de varios procesos, la librería nos devuelve el listado de procesos y podemos acceder a su directorio en /proc/nomproceso y recoger la información que allí figura (de la que tengamos permisos, claro). La función tiene dos argumentos de entrada, con el primero indicamos el modo de lista, es decir, podemos consultar todos los procesos, o bien realizar búsquedas por usuario o por nombre de proceso. El segundo parámetro es el string que necesitamos buscar, ya sea usuario o proceso, también he configurado una excepción cuando en los campos de búsqueda se introduce un string vacío y otra excepción que recoge la excepción que genera la librería procfs cuando un usuario no está en el sistema.
def getProcessList(n, s=''): try: proc = Proc() if n == 't': procs = proc.processes return procs elif n == 'g': if s == '': raise ValueError('ERROR: Atributo string PROCESO vacio') else: pyt = proc.processes.cmdline(s) if pyt: return pyt else: re = {"Ningun proceso relacionado al nombre facilitado"} return re elif n == 'u': if s == '': raise ValueError('ERROR: Falta el atributo ' + 'string NOMBRE DE USUARIO') else: pyt = proc.processes.user(s) if pyt: return pyt else: re = {"Ningun proceso relacionado a ese nombre de usuario"} return re else: raise ValueError('ERROR: Valor del PARAMETRO INCORRECTO o nulo') except KeyError, e: lp = {"Usuario no encontrado en el sistema", e} return lp except Exception, e: return e
En este caso, la función no realiza prints, si no que devuelve el resultado por return, así que en el script la he usado de la siguiente forma:
try: pr = getProcessList('u', 'root') for val in pr: if not hasattr(val, 'comm'): print val else: nomcom = str(val.comm) nomcom = nomcom.replace('\\n', '') print str(val.id) + " " + nomcom + " STATUS: " +\ str(val.status.State) except Exception, e: print e
La función getProcessList() la podemos usar pasándole uno o dos parámetros, si queremos que nos devuelva todos los procesos tenemos que pasarle como argumento la letra t, en caso de usar el argumento u o g, nos pedirá un string adicional (la palabra a buscar).
Si echamos un vistazo al bucle for de más arriba, vemos un if preguntando si tiene el atributo comm, la explicación de esto es simple, si hay una excepción en la función nos devuelve el error, por tanto pregunto por un atributo que se devolverá en el caso que la función no arroje una excepción, si devuelve la excepción, el condicional if no encontrará el atributo comm (el nombre del proceso) y arrojará el valor conteniendo el string de la excepción.
Por ahora ya sabemos obtener listados de procesos, estado del procesador, memoria, carga del sistema y otras muchas funciones que podemos realizar con las librerías procfs, como por ejemplo obtener el estado de la red, estadísticas de envío y recepción de paquetes y todo lo que podamos recoger del directorio /proc/.
Un Saludo!