Programación de sistema
Presentación
1. Definición
La programación del sistema se define en oposición a la programación de la aplicación. No se trata de diseñar un software que utilizará el sistema y sus recursos para realizar una acción, sino de diseñar una de las piezas que se integrará en el sistema mismo. Se puede tratar del desarrollo de un controlador para un hardware, una interfaz de red o incluso la gestión de recursos.
Por extensión, crear un programa que use otros programas del sistema es la programación del sistema. El término programación del sistema se extiende a todo lo que puede permitir al administrador del sistema, resolver los problemas habituales relacionados con su área de competencia, a saber, la gestión de usuarios, periféricos, procesos, copias de seguridad, etc.
Por extensión, el uso de comandos bash es la programación del sistema. También lo es el uso de los comandos mysql y mysqldump, para realizar acciones de copia de seguridad diarias.
Un sistema operativo moderno está escrito principalmente en C, el resto es ensamblador específico de la máquina. Este mismo sistema operativo, moderno, tiene funcionalidades de alto nivel, como el administrador de paquetes que necesita, por ejemplo, realizar varias operaciones, como intercambios de red para descargar paquetes, verificar su integridad...
Comprender su sistema operativo
1. Advertencia
La ejecución de comandos externos está íntimamente relacionada con el sistema en el que se ha instalado Python. Por un lado, cada sistema operativo tiene sus propios comandos. Por ejemplo, para listar un directorio, se utilizará ls o dir.
Esta sección trata principalmente los comandos Unix.
Más allá de los comandos de sistema clásicos, algunos comandos como mysql o mysqldump solo se pueden usar si están instalados los programas adecuados, sea cual sea el sistema.
2. Sistema operativo
Python propone un módulo de bajo nivel, que permite gestionar la información del sistema operativo:
>>> import os
A continuación se muestran los dos principales medios para comprobar la naturaleza del sistema:
>>> os.name
'posix'
>>> os.uname()
('Linux', 'nombre_dado_al_host', '2.6.38-11-generic', '#50-Ubuntu
SMP Mon Sep 12 21:17:25 UTC 2011', 'x86_64')
El primer comando proporciona una estandarización del entorno y el segundo, los detalles sobre el nombre del sistema operativo, la máquina, el nombre del núcleo y su versión, así como la arquitectura de la máquina. Probar estos valores le permite tomar decisiones y adaptar una aplicación a un entorno específico para ciertas operaciones que lo requieran.
A continuación se muestra cómo encontrar la lista de variables de entorno:
>>> list(os.environ.keys())
Y a continuación se muestra cómo ir a buscar el valor de una de estas variables:
>>> os.getenv('LANGUAGE')
'es_ES:es'
La explotación de estas variables de entorno, también permite orientar elecciones que permiten la adaptación de la aplicación. En el caso que se acaba de ver, la elección de la configuración local se puede utilizar para producir una interfaz adaptada al idioma del usuario. Las variables de entorno se pueden leer como bytes con os.environb (útil cuando Python es unicode, pero no sistema).
Python también le permite modificar estas variables de entorno utilizando el método putenv. El entorno se ve afectado en todos los subprocesos.
3. Proceso actual
Python permite obtener información del proceso actual....
Gestión de un archivo
1. Cambiar los permisos de un archivo
Esta sección sigue estando relacionada con el manejo de un archivo, pero hay una diferencia fundamental con lo anterior. Aquí, ya no estamos en operaciones de entrada/salida en un archivo, sino en operaciones en el sistema de archivos.
Es obvio que esta funcionalidad está íntimamente vinculada al sistema operativo y hay dos categorías. Aquellas que se basan en la tecnología Unix, ya sea estrictamente idénticos o agregando un toque personal (GNU / Linux, FreeBSD y Mac, que depende de FreeBSD) y el resto del mundo, es decir Windows.
Por lo tanto, es perfectamente lógico encontrar estas funcionalidades en el módulo os. Además, también necesitaremos el módulo de estadísticas que contiene las diferentes constantes utilizadas para describir un derecho en un archivo.
>>> import os, stat
El sistema operativo Windows permite marcar un archivo como modo solo lectura o modo lectura y escritura.
A continuación, se muestra cómo marcar un archivo en modo solo lectura:
>>> os.chmod (ruta de archivo, stat.S_IREAD)
A continuación se muestra cómo ponerlo en modo lectura y escritura:
>>> os.chmod (ruta de archivo, stat.S_IWRITE)
Cualquier otra opción aprobada, simplemente no se tendrá en cuenta (sin errores).
Para Unix, la lista de opciones es más extensa (http://docs.python.org/library/os.html#os.chmod). Estas opciones le permiten establecer o eliminar los permisos de lectura, escritura, ejecución para usuario, grupo y otros. Las explicaciones de las opciones se analizan en la documentación oficial (http://docs.python.org/library/stat.html#stat.S_ISUID).
¿Cómo recordar esto? La idea, en realidad, es tener un nombre corto y constante. Debemos comenzar reteniendo R para lectura, W para escritura y X para ejecución. Cuando solo se proporciona una instrucción, tres letras designan el "qué" y si RWX se aplica simultáneamente, solo se usa una letra. Es U o USR para el propietario, G o GRP para el grupo y O u OTH para los demás.
Entonces, ¿cómo se usan estas constantes? La idea es describir exactamente los permisos otorgados a un archivo. Si se otorga un permiso está presente, si no se especifica, está ausente. Cada constante se posiciona...
Alternativas sencillas a los comandos bash usuales
1. Directorios
Python permite recorrer directorios, crearlos, eliminarlos y asignar permisos. Siempre se usa el mismo módulo:
>>> import os
A continuación se muestra cómo posicionarse en este directorio (equivalente a cd):
>>> os.chdir('test')
Y cómo saber en qué directorio estamos (equivalente a pwd):
>>> os.getcwd()
'/home/sch/Documents/Libro_Python/examples/14/test'
Crear un directorio es igual de sencillo (equivalente a mkdir):
>>> os.mkdir('dir1')
También podemos crear una arborescencia (equivalente a mkdir -p):
>>> os.mkdir('dir1', 'dir2', 'dir3')
Para crear una arborescencia de carpetas, también hay una solución:
>>> os.makedirs(os.sep.join(['dir2', 'test', '14']))
A continuación se muestra cómo recorrer la arborescencia creada (equivalente a ls -R):
>>> for cur, dirs, files in os.walk(os.curdir):
... for name in (os.path.join(cur, d) for d in dirs):
... print('%10d %s' % (os.path.getsize(name), name))
...
0 ./dir1
0 ./dir2
0 ./dir2/test
0 ./dir2/test/14
Esta parte merece un poco más de explicación. La función os.walk devuelve un generador que recorre la estructura de árbol de los archivos y carpetas:
>>> for cur, dirs, files in os.walk(os.curdir):
... print('En %s, hay %d carpetas y %d archivos' % (cur,
len(dirs), len(files)))
... if len(dirs) > 0:
... print('\t> Carpetas %s' % os.pathsep.join(dirs))
... if len(files) > 0:
... print('\t> Archivos %s' % os.pathsep.join(files))
...
En ., hay 2 carpetas y 0 archivos
> Carpetas dir1:dir2
En ./dir1, hay 0 carpetas y 0 archivos
En ./dir2, hay 1 carpetas y 0 archivos
> Carpetas test
En ./dir2/test, hay 1 carpetas y 0 archivos
...
Ejecutar comandos externos
1. Ejecutar y mostrar el resultado
Python ofrece varias formas de hacer llamadas a los sistemas de comandos y recuperar el resultado.
El módulo os incluye herramientas muy específicas de bajo nivel, pero destinadas a aquellos que ya conocen los principios fundamentales de la programación del sistema.
Para las necesidades de este capítulo, queremos poder lanzar en las consolas de Python, comandos que podríamos lanzar en una terminal.
El módulo utilizado es el siguiente:
>>> import subprocess
A continuación se muestra cómo proceder:
>>> retcode = subprocess.call('ls')
data.txt test.txt
El resultado del comando se muestra en la pantalla y el código de retorno se recupera con el retorno de la función call:
>>> print(retcode)
0
Todo programa que termina correctamente devuelve un 0. En caso contrario devuelve una cifra que se corresponde con un código de error.
Cuando queremos ver los argumentos, hay que diferenciar el comando y los argumentos:
>>> retcode = subprocess.call(['ls', '-l'])
total 128
-rw-r--r-- 1 sch sch 35099 2011-10-22 17:56 data.txt
-rw-r--r-- 1 sch sch 0 2011-10-22 17:38 test.txt
Los argumentos se deben pasar unos después de otros en la lista, como en este ejemplo:
>>> retcode = subprocess.call(['ls'...
IHM sistema
1. Presentación
En la programación del sistema, la aplicación interactúa principalmente con el sistema. Por lo tanto, utilizará las herramientas del sistema, lo cual es cierto en particular para la interfaz hombre-máquina, es decir, la forma de proporcionar información al usuario y solicitarla.
En este contexto, el usuario tiene acceso a un terminal. Lo primero que hará es iniciar la aplicación. Esto le permite pasar argumentos a la aplicación, como se muestra en nuestros ejemplos clásicos:
$ top
$ cp archivo1 archivo 2
$ cp -t directory archivo1 archivo2 archivo3
En Python, estos argumentos están disponibles en sys.argv y el primero de ellos es el nombre del ejecutable si estamos en la consola o el del archivo ejecutado. Se le puede ver escribiendo en un archivo:
import sys
for arg in sys.argv:
print(arg)
Y a continuación se muestra cómo se ejecuta este programa:
$ python3 argv.py -t dest f1 f2 f3 --test
argv.py
-t
dest
f1
f2
f3
--test
La gestión manual de estos argumentos podría ser extremadamente complicada sin la ayuda del módulo argparse de alto nivel. Este módulo también le permite generar documentación, administrar errores y posiblemente una lógica compleja detrás de los argumentos.
Una vez que finaliza la aplicación, debe devolver un número entero positivo: 0 si terminó correctamente o un número estrictamente positivo. El valor de este número no importa para el sistema, pero puede darle significado al asignar un valor a un error específico.
Para salir de un programa de Python, puede dejar ejecutando la última línea del programa y retomar el control con 0, o usar sys.exit (42) por ejemplo.
Para finalizar, durante el uso del programa, se puede comunicar con el usuario a través de los tres canales de comunicación que configura el sistema: entrada estándar, salida estándar y salida de error.
2. Trabajar con argumentos
Por lo general, cuando se lanza un programa desde una terminal, sea cual sea el tipo de este programa, es posible pasar argumentos. Estos se procesan y el programa debe analizarlos y darles significado.
Sin embargo, los programas complejos pueden...
Curses
Este capítulo se dirige en particular a los desarrolladores con experiencia en Python y acostumbrados a la programación de sistema.
1. Presentación
La librería Curses permite el desarrollo de interfaces textuales hombre-máquina, avanzadas de alto nivel.
Por lo tanto, se encarga de tener en cuenta los detalles de cada terminal (Linux, Unix, Xterm o rxvt) y ofrece todas las funcionalidades necesarias para controlar de manera fina pero sencilla, las interacciones entre la máquina y el usuario.
Como la mayoría de las librerías gráficas (Gtk, Qt, wxWidgets) y Pygame, Curses se encarga de inicializar la pantalla, realizar la visualización y ofrece un bucle de eventos que permite escuchar las acciones del usuario.
Haremos una presentación rápida del módulo, mostrando cómo dar los primeros pasos con esta librería.
2. Administrar el arranque y la parada
El terminal funciona de manera clásica. Muestra lo que el usuario introduce y reacciona a lo que solicita. Cuando arranca Curses, el terminal cambia su comportamiento para dejar de ofrecer algo lineal, sino una interfaz que ocupa toda la pantalla.
Del mismo modo, cuando finaliza la aplicación que usa Curses (de manera normal o con un error), se debe restaurar el terminal.
Esto es lo que hace curses.wrapper.wrapper.
curses.wrapper.wrapper(function_application, argumentos, ...)
La función application recibe como primer argumento el objeto que representa la pantalla, llamada por convención stdscr.
def function_application(stdscr, argumentos, ...):
pass
Sus otros argumentos son los que se pasaron a la función...
Acceso a los periféricos
Este capítulo está dirigido en particular a los desarrolladores que ya tienen experiencia con Python, acostumbrados a la programación del sistema y que ya conocen D-Bus.
1. Introducción
Hay muchas formas de acceder a los periféricos, muchas de las cuales están actualmente obsoletas. Hoy en día, los núcleos de Linux 2.6 utilizan el sistema de comunicación entre procesos llamado D-Bus (http://www.freedesktop.org/wiki/Software/dbus), que está activo con KDE4, GNOME, Enlightenment, Maemo o incluso Android. Su principal desarrollador es Red Hat, en el marco del proyecto freedesktop.org. Se puede portar a Windows.
Para acceder a los periféricos, es necesario acceder a operaciones de bajo nivel y conocer el API del proyecto (http://dbus.freedesktop.org/doc/api/html/index.html). Los objetos principales son DBusConnection y DBusMessage.
La documentación general también está disponible en el sitio web de freedesktop.org (http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html).
El lenguaje de programación Python tiene un módulo que permite usar D-Bus a bajo nivel. Por lo tanto, el desarrollador que desee hacerlo, debe conocer bien D-Bus o tener documentación a mano.
En esta parte, puede ver algunos usos concretos que se presentarán como recetas para aplicar y mejorar según nuestras necesidades, pero no detallaremos el funcionamiento del módulo, que es más propio del conocimiento de D-Bus que de Python.
2. Ver las particiones
El lenguaje Python le permite usar D-Bus a bajo nivel, para acceder al sistema de información. Este primer sistema mostrará lo importante que es conocer el API del proyecto D-Bus, ya que la parte de Python sigue siendo relativamente accesible.
Por lo tanto, a continuación se muestra un sistema que brinde información sobre las particiones presentes en la máquina host del sistema:
import dbus
bus = dbus.SystemBus()
proxy = bus.get_object(
"org.freedesktop.Udisks",
"/org/freedesktop/Udisks"
)
manager = dbus.Interface(proxy, 'org.freedesktop.Udisks')
for dev in manager.EnumerateDevices():
device_obj = bus.get_object("org.freedesktop.UDisks"...