Читать книгу Raspberry Pi® a fondo para desarrolladores - Derek Molloy - Страница 12
ОглавлениеCapítulo
3
Exploración de sistemas Linux empotrados
Este capítulo presenta los conceptos, comandos y herramientas esenciales para manejar con eficiencia el sistema Linux empotrado en el Raspberry Pi. La primera parte de este capítulo es descriptiva. Explica los aspectos básicos de Linux en sistemas empotrados y del proceso de inicio de Linux. Después aprenderemos a gestionar los sistemas Linux paso a paso. Para este ejercicio es muy recomendable abrir una conexión terminal con nuestro Raspberry Pi, o bien una ventana de terminal en el propio RPi, antes de continuar. Luego, en el capítulo se describe el sistema de gestión de versiones de código fuente Git. Se trata de un tema importante porque los ejemplos de código fuente de este libro se distribuyen a través de GitHub. La virtualización de escritorios se describe igualmente, puesto que resultará útil para el desarrollo multiplataforma en posteriores capítulos. El capítulo finaliza describiendo cómo descargar los ejemplos de código fuente para este libro.
Materiales necesarios para este capítulo:
❏Cualquier modelo de Raspberry Pi con una conexión terminal (véase el capítulo 2, "El software del Raspberry Pi") o una ventana de terminal, preferiblemente ejecutando Raspbian.
Puede encontrar más detalles sobre este capítulo en la dirección:
www.exploringrpi.com/chapter3/.
Introducción a Linux para sistemas empotrados
Lo primero es lo primero: aun cuando la expresión "Linux empotrado" (embedded Linux) está presente ya en el título del capítulo, ¡no hay nada que pueda denominarse "Linux empotrado"! No existe una versión especial del núcleo de Linux para sistemas empotrados: es el mismo núcleo estándar de Linux el que se ejecuta en dichos sistemas. Dicho esto, lo cierto es que la expresión Linux empotrado resulta de uso común y está ampliamente aceptada. Así pues, este libro utiliza "Linux empotrado" en lugar de "Linux ejecutándose en un sistema empotrado", que sería más correcta.
La palabra "empotrado" (embedded) sirve para indicar la presencia de un sistema hardware empotrado (embedded system), concepto que se puede definir libremente como un tipo de hardware con software integrado, diseñado específicamente para una determinada aplicación. Es un concepto que contrasta vivamente con el de ordenador personal (PC), que es una plataforma de computación de propósito general diseñada para ejecutar una infinidad de aplicaciones diferentes, tales como navegadores de Internet, procesadores de texto o videojuegos. Ahora bien, la línea que separa unos sistemas de otros se va difuminando progresivamente. Sin ir más lejos, el Raspberry Pi puede actuar de las dos maneras, y son muchos los usuarios que deciden emplearlo exclusivamente como ordenador personal bastante capaz, o bien como dispositivo multimedia. Sin embargo, los sistemas empotrados presentan algunas características diferenciadoras:
❏Suelen incluir aplicaciones específicas y dedicadas a tareas concretas.
❏Su capacidad de procesamiento, memoria y almacenamiento son a menudo limitadas.
❏En general forman parte de sistemas más grandes que suelen estar conectados a sensores y actuadores externos.
❏A menudo desempeñan su labor en sistemas para los cuales la fiabilidad es un elemento crucial, como controles en automóviles, aviones y equipos médicos.
❏Operan muchas veces en tiempo real, es decir, donde sus salidas están relacionadas directamente con las entradas presentes, por ejemplo en sistemas de control.
Los sistemas empotrados se encuentran en todas partes. Como ejemplos podemos citar: máquinas de vending, aplicaciones para el hogar, teléfonos, smartphones, cadenas de fabricación y ensamblaje, televisiones, consolas de videojuegos, automóviles (por ejemplo, la dirección asistida o los sensores de aparcamiento), elementos de red (como switches, routers o puntos de acceso inalámbricos), sistemas de sonido, equipos médicos, impresoras, controles de acceso a edificios, parquímetros, sistemas de control de agua y energía, relojes, herramientas de construcción, cámaras digitales, monitores, tabletas, lectores de libros digitales, robots, sistemas de pago inteligentes, etc.
La gran proliferación de dispositivos con Linux empotrado se debe, en parte, a la rápida evolución de la tecnología smartphone, que ha presionado a la baja los precios de los microprocesadores con tecnología ARM. ARM Holdings PLC es la compañía británica que comercializa, para los modelos del RPi, las licencias de uso de los diseños ARMv6 y ARMv7 por una cantidad aproximada de entre el 1 y el 2% del precio real de venta del procesador. Avago Technologies Ltd., la propietaria de Broadcom Corporation desde mayo de 2015, no comercializa en la actualidad procesadores directamente a minoristas, pero sus microprocesadores similares a los BCM2835/6/7 tienen un precio de venta de entre 5 y 10 dólares.
Ventajas y desventajas de Linux para sistemas empotrados
Hay muchos tipos de plataformas empotradas, cada una con sus propias ventajas e inconvenientes. Las hay con un coste muy bajo para pedidos de gran volumen, por debajo de 1 euro, por ejemplo Amtel AVR (8/16 bits), Microchip PIC o TI Stellaris, pero también muy caras y especializadas que pueden llegar hasta los 150 euros, como microprocesadores de señales digitales (DSP, Digital Signal Processor) multinúcleo. Generalmente, dichas plataformas se programan en lenguajes C o ensamblador (assembly language), lo que exige un conocimiento muy solvente de los detalles hardware de más bajo nivel de las arquitecturas antes de empezar siquiera a plantearse el desarrollo de aplicaciones útiles. Linux empotrado ofrece una alternativa a tales plataformas en el sentido de que elimina la exigencia de conocer al detalle la arquitectura subyacente del sistema para emprender el desarrollo de aplicaciones. Ahora bien, si nuestras aplicaciones van a tener que comunicarse a bajo nivel con dispositivos electrónicos, sí que será necesario un cierto conocimiento de las arquitecturas.
Veamos algunas de las razones por las que Linux empotrado ha experimentado un crecimiento tan reseñable:
❏Linux es un sistema operativo (SO) eficiente y escalable que se ejecuta sobre una amplia variedad de dispositivos, desde los de coste más bajo, orientados al gran consumo, hasta los servidores más grandes y caros. Asimismo, ha evolucionado con el correr del tiempo desde la época en que los ordenadores tenían una mínima fracción de la potencia que ofrecen ahora, pero conservando casi intacta su eficiencia.
❏Existe un inmenso conjunto de aplicaciones y herramientas de código abierto, listas para su instalación y uso en un sistema empotrado. Si necesitamos, por ejemplo, un servidor web para nuestra aplicación empotrada, podremos usar el mismo que instalaríamos en un servidor Linux.
❏Existen excelentes controladores de código abierto para una gran variedad de periféricos y dispositivos, desde adaptadores de red hasta monitores.
❏Linux es un sistema de código abierto y no hay que pagar nada por usarlo.
❏Su núcleo y sus aplicaciones se ejecutan a diario en una cantidad tal de sistemas por todo el mundo que los errores son extremadamente infrecuentes y además se detectan y corrigen rápidamente.
Una desventaja de Linux empotrado es su falta de idoneidad para las aplicaciones en tiempo real debido a la sobrecarga, fundamentalmente de tiempo en este caso, del propio SO. Así pues, para aplicaciones de gran precisión y mínima latencia, como el procesamiento de señales analógicas, Linux empotrado podría no ser la solución perfecta. Sin embargo, incluso en aplicaciones de tiempo real, se emplea a menudo como cerebro e interfaz de control dentro de un conjunto interconectado de sensores (véase el capítulo 12). Además, el desarrollo de las capacidades de Linux RTOS (Real Time Operating Systems, sistemas operativos de tiempo real) continúa avanzando y progresa en su objetivo de agilizar las interrupciones del SO para su uso en tiempo real.
¿Es Linux gratuito y de código abierto?
Linux se publica bajo licencia GNU GPL (General Public License, licencia pública general), que garantiza a los usuarios el derecho de uso y modificación del código fuente sin límites. Así pues, la palabra free hace aquí más referencia al concepto de "libre" que al de "gratuito" (ambos son significados del término en inglés). De hecho, algunas de las distribuciones de Linux más caras son precisamente las destinadas a las arquitecturas empotradas. Podrá encontrar una guía rápida para familiarizarse con la licencia GPLv3 en la web www.gnu.org, que relaciona nuestros derechos como usuarios (Smith, 2013):
Derecho a utilizar el software para cualquier propósito.
Derecho a modificar el software para que se ajuste a sus necesidades.
Derecho a compartir el software con amigos y vecinos.
Y derecho a compartir todos aquellos cambios que introduzca en él.
Aun si estamos utilizando una distribución descargada de forma gratuita, puede que nos cueste un esfuerzo significativo modificar las librerías y los controladores de dispositivo para adaptar los componentes y módulos particulares que deseemos utilizar en el desarrollo de los productos.
Cómo iniciar el Raspberry Pi
Lo primero que deberíamos ver cuando arrancamos un ordenador es la UEFI (Unified Extensible Firmware Interface, interfaz de firmware ampliable unificada), que brinda compatibilidad con las rutinas de servicio de la antigua BIOS (Basic Input/Output System, sitema básico de entrada/salida). La pantalla de inicio muestra información del sistema y nos invita a pulsar una tecla para modificar estos ajustes. La UEFI comprueba los componentes del hardware, como la memoria, y, luego, carga el SO, generalmente desde una unidad de disco duro SSD (Solid State Disk, disco de estado sólido). Así pues, cuando encendemos un ordenador, la UEFI, o la BIOS, ejecutan las tareas siguientes:
1. Toma el control del procesador del equipo.
2. Inicializa y comprueba los componentes hardware.
3. Carga el SO desde la unidad SSD o disco duro convencional.
La UEFI/BIOS ofrece una capa de abstracción que permite al SO interaccionar con la pantalla y otros periféricos de entrada/salida del sistema, como ratón, teclado y almacenamiento externo. Sus ajustes se guardan en una memoria flash NAND alimentada por una pila de botón (que se puede ver en las placas madre de los ordenadores) que alimenta también el reloj del sistema.
Los gestores de arranque del Raspberry Pi
Como la mayoría de los dispositivos Linux empotrados, el RPi carece de BIOS y de batería de alimentación, al menos de forma predeterminada (veremos cómo se añade un reloj en tiempo real al RPi en el capítulo 9). En lugar de eso, utiliza una combinación de gestores de arranque (bootloaders). Generalmente, los gestores de arranque son pequeños programas que realizan la función crítica de vincular el hardware concreto de cada placa con Linux:
❏Inicializan los controladores (memoria, gráficos, E/S).
❏Preparan y asignan la memoria del sistema para el SO.
❏Localizan el SO y preparan su carga.
❏Cargan el SO y le pasan el control.
El gestor de arranque de Linux empotrado es un programa personalizado para cada tipo de placa, incluido el RPi. Existen gestores de arranque de código abierto para Linux, tales como Das U-Boot (el cargador universal de referencia), que se puede personalizar, siempre que se cuente con un conocimiento detallado del hardware de la plataforma de Linux empotrado, mediante parches de software específicos de cada plataforma (diríjase a tiny.cc/erpi301). El RPi utiliza un enfoque distinto: utiliza unos gestores de arranque muy eficientes pero de código cerrado, desarrollados específicamente para el RPi por Broadcom. Tanto el gestor de arranque como los archivos de configuración se encuentran en el directorio /boot de la imagen en el RPi:
pi@erpi /boot $ ls -l *.bin start.elf *.txt *.img fixup.dat
-rwxr-xr-x 1 root root 17900 Jun 16 01:57 bootcode.bin
-rwxr-xr-x 1 root root 120 May 6 23:23 cmdline.txt
-rwxr-xr-x 1 root root 1581 May 30 14:49 config.txt
-rwxr-xr-x 1 root root 6174 Jun 16 01:57 fixup.dat
-rwxr-xr-x 1 root root 137 May 7 00:31 issue.txt
-rwxr-xr-x 1 root root 3943888 Jun 16 01:57 kernel7.img
-rwxr-xr-x 1 root root 3987132 Jun 16 01:57 kernel.img
-rwxr-xr-x 1 root root 2684312 Jun 16 01:57 start.elf
La figura 3-1 ilustra el proceso de arranque en el RPi, donde cada etapa del mismo se carga e invoca por la precedente. Los archivos bootcode.bin y tart.elf son gestores de arranque de código cerrado que están en código binario y se ejecutan en la GPU del RPi, y no en la propia CPU. El archivo de licencia que podemos encontrar en github.com/raspberrypi/firmware/tree/master/boot indica que se autoriza la redistribución "en código binario, sin modificaciones" y que "solo se puede utilizar con propósitos de desarrollo y ejecución en un dispositivo Raspberry Pi". Podemos encontrar una imagen comprimida del núcleo en /boot/kernel.img. Por descontado, es código abierto.
La salida que sigue es una secuencia de arranque típica, capturada con el cable serie USB a TTL de 3,3 V que vimos en el capítulo 1. El cable estaba fijo a los pines 6 (GND), 8 (UART_TXD) y 10 (UART_RXD) de la cabecera del RPi, y los datos se capturaron a 115.200 baudios. A diferencia de los gestores U-boot, que se ejecutan en la CPU, los gestores tempranos del RPi no ofrecen salida por consola, aunque sí hacen parpadear los LED de la placa con patrones específicos si surgen problemas. Lo que sigue es un extracto de la salida por consola durante el arranque del RPi 3. Muestra una importante información del sistema, como el mapeo de la memoria:
Figura 3-1: La secuencia de arranque completa en el RPi.
Uncompressing Linux... done, booting the kernel.
[ 0.000000] Booting Linux on physical CPU 0x0
...
[ 0.000000] Linux version 4.1.18-v7+ (dc4@dc4-XPS13-9333) (gcc version
4.9.3 (crosstool-NG crosstool-ng-1.22.0-88-g8460611) ) #846 SMP Thu Feb
25 14:22:53 GMT 2016
[ 0.000000] CPU: ARMv7 Processor [410fd034] revision 4 (ARMv7) ...
[ 0.000000] Machine model: Raspberry Pi 3 Model B Rev 1.2
[ 0.000000] cma: Reserved 8 MiB at 0x36400000 ...
[ 0.000000] Kernel command line: 8250.nr_uarts=1 dma.dmachans=0x7f35
bcm2708_fb.fbwidth=656 bcm2708_fb.fbheight=416 bcm2709.boardrev=0xa02082
bcm2709.serial=0xbbffd b2c smsc95xx.macaddr=B8:27:EB:FF:DB:2C
bcm2708_fb.fbswap=1 bcm2709.uart_clock=48000000 vc_mem.mem_base=0x3dc00000
vc_mem.mem_size=0x3f000000 dwc_otg.lpm_enable=0 console=ttyS0,115200
root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
[ 0.000000] Memory: 874456K/901120K available (6024K kernel code, 534K
rwdata, 1660K rodata, 448K init, 757K bss, 18472K reserved, 8192K cma-
reserved)
[ 0.000000] Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xffc00000 - 0xfff00000 (3072 kB)
vmalloc : 0xb7800000 - 0xff000000 (1144 MB)
lowmem : 0x80000000 - 0xb7000000 ( 880 MB)
modules : 0x7f000000 - 0x80000000 ( 16 MB)
.text : 0x80008000 - 0x807895a0 (7686 kB)
.init : 0x8078a000 - 0x807fa000 ( 448 kB)
.data : 0x807fa000 - 0x8087fac0 ( 535 kB)
.bss : 0x80882000 - 0x8093f79c ( 758 kB)
...
[ 0.052103] Brought up 4 CPUs
[ 0.052201] SMP: Total of 4 processors activated (153.60 BogoMIPS).
[ 0.052231] CPU: All CPU(s) started in HYP mode. ...
[ 1.467927] console [ttyS0] enabled
...
[ 3.307558] systemd[1]: Detected architecture 'arm'.
[ 3.321650] smsc95xx 1-1.1:1.0 eth0: register 'smsc95xx' at
usb-3f980000.usb-1.1, smsc95xx USB 2.0 Ethernet, b8:27:eb:ff:db:2c
[ 3.488061] NET: Registered protocol family 10
[ 3.498204] systemd[1]: Inserted module 'ipv6'
[ 3.510056] systemd[1]: Set hostname to <erpi> ...
[ 5.450070] spi spi0.0: setting up native-CS0 as GPIO 8
[ 5.450453] spi spi0.1: setting up native-CS1 as GPIO 7 ...
...
Raspbian GNU/Linux 8 erpi ttyS0
erpi login:
Obtenemos la misma información si escribimos dmesg|more en una ventana de terminal. No es difícil ver cómo se configura el estado inicial del hardware, pero la mayoría de las entradas se antojan enigmáticas por el momento. Hay algunos puntos importantes que observar, como se pueden ver destacados en la salida anterior:
❏El núcleo de Linux se descomprime en memoria y, luego, se inicia. El ARMv7 del RPi 2/3 (kernel7.img) y el ARMv6 del RPi/RPi B+ (kernel.img) utilizan una versión ligeramente diferente del núcleo.
❏Se identifica la versión del núcleo de Linux, por ejemplo 4.1.18-v7+.
❏El modelo de la máquina se identifica de manera que se pueda cargar el árbol de código binario correcto.
❏La dirección de red MAC (Medium Access Control, control de acceso al medio (físico)), una dirección hardware única que identifica el dispositivo en la red a nivel físico) se pasa como un argumento del núcleo en la línea de comandos. La dirección MAC se establece automáticamente en el RPi mediante los tres últimos bytes del número de serie de la CPU, que se fija durante la fabricación. Ejecute cat /proc/cpuinfo para mostrar el número de serie de la placa. En este caso, el número es 00000000bbffdb2c, donde ffdb2c sirve para facilitar un número de dirección MAC único.
❏El usuario puede configurar algunos de los argumentos del núcleo restantes editando el archivo cmdline.txt, por ejemplo ejecutando sudo nano cmdline.txt del siguiente modo:
pi@erpi /boot $ more cmdline.txt
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2
rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
❏Se muestra el formato de la memoria virtual del núcleo. La entrada de los módulos es particularmente importante y se utilizará en el capítulo 8.
El principal archivo de configuración del RPi es /boot/config.txt. Los cambios que introducimos mediante la herramienta raspi-config se reflejan en este archivo. Es posible editarlo ejecutando sudo nano /boot/config.txt para habilitar y deshabilitar el bus hardware, elevar la frecuencia del procesador, etc.
pi@erpi /boot $ more config.txt
# For more options and information see
# http://www.raspberrypi.org/documentation/configuration/config-txt.md ...
# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=on
...
# Additional overlays and parameters are documented /boot/overlays/README
El gestor de arranque del RPi utiliza un archivo de configuración para la tarjeta llamado device tree o árbol de dispositivos (también llamado device tree binary, árbol de dispositivos binario) que contiene la información específica requerida por el núcleo para arrancar el RPi. Este archivo contiene toda la información necesaria para describir el tamaño de la memoria, la velocidad del reloj, los dispositivos de la placa, etc. Este árbol de dispositivos binario o DTB, donde "binario" hace aquí referencia al archivo compilado, se crea a partir del árbol de dispositivos fuente o DTS (Device Tree Source), que es, obviamente, el archivo de código fuente a partir del que se obtiene dicho binario compilando con el DTC (Device Tree Compiler). Este tema se trata en profundidad en el capítulo 8. El directorio /boot contiene los binarios del árbol de dispositivos para los distintos modelos de RPi:
pi@erpi /boot $ ls -l *.dtb
-rwxr-xr-x 1 root root 10841 Feb 25 23:22 bcm2708-rpi-b.dtb
-rwxr-xr-x 1 root root 11120 Feb 25 23:22 bcm2708-rpi-b-plus.dtb
-rwxr-xr-x 1 root root 10871 Feb 25 23:22 bcm2708-rpi-cm.dtb
-rwxr-xr-x 1 root root 12108 Feb 25 23:22 bcm2709-rpi-2-b.dtb
-rwxr-xr-x 1 root root 12575 Feb 25 23:22 bcm2710-rpi-3-b.dtb
El código fuente de estos DTB se encuentra disponible en formato DTS. Los archivos DTS de cada modelo de RPi presentan una sintaxis similar al extracto siguiente, que detalla el hardware de los pines de los LED incorporados a la placa y de uno de los dos buses I2C del RPi 2:
&i2c1 {
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
};
&leds {
act_led: act {
label = "led0";
linux,default-trigger = "mmc0";
gpios = <&gpio 47 0>;
};
pwr_led: pwr {
label = "led1";
linux,default-trigger = "input";
gpios = <&gpio 35 0>;
};
};
El código fuente completo del archivo DTS para el RPi 2 (bcm2709-rpi-2-b.dts) está disponible en: tiny.cc/erpi302. Se pueden añadir al RPi archivos binarios del árbol de dispositivos adicionales, como sensores, placas HAT y pantallas LCD:
pi@erpi /boot/overlays $ ls
ads7846-overlay.dtb i2s-mmap-overlay.dtb pps-gpio-overlay.dtb ...
hifiberry-amp-overlay.dtb mcp2515-can0-overlay.dtb rpi-proto-overlay.dtb
hy28b-overlay.dtb piscreen-overlay.dtb w1-gpio-pullup-overlay.dtb
i2c-rtc-overlay.dtb pitft28-resistive-overlay.dtb
La descripción completa del código fuente del árbol de dispositivos para la distribución del RPi está en el código fuente del resto del libro en el directorio /chp03/dts.
Espacio del núcleo y espacio de usuario
El núcleo de Linux se ejecuta en una área de la memoria del sistema llamada espacio del núcleo (kernel space), mientras que, por su parte, las aplicaciones normales se ejecutan en el espacio de usuario (user space). Una separación estricta entre ambos espacios en memoria evita que las aplicaciones de usuario accedan al espacio y los recursos requeridos para el funcionamiento del núcleo de Linux. Esto ayuda a evitar que el núcleo se cuelgue a causa del código de usuario mal desarrollado, así como que las aplicaciones de un usuario interfieran en la ejecución de las aplicaciones de otro e invadan su espacio. También proporcionan un grado más elevado de seguridad.
El núcleo de Linux es "propietario" y tiene acceso pleno a toda la memoria física y al resto de los recursos del RPi. Así pues, debemos ser muy cuidadosos y solo permitir que el código más estable y comprobado se ejecute en el espacio del núcleo. Puede observar las arquitecturas e interfaces ilustradas en la figura 3-2, donde las aplicaciones de usuario emplean la librería GNU C (glibc) para comunicarse con la interfaz de llamadas del núcleo del sistema. Luego, los servicios del núcleo se ponen a disposición del espacio de usuario de manera controlada mediante el uso de llamadas del sistema.
Figura 3-2: Las arquitecturas del espacio de usuario y del espacio del núcleo de Linux.
Un módulo del núcleo (kernel module) es un archivo objeto que contiene código binario y que se puede cargar y descargar del núcleo bajo demanda. En muchos casos, el núcleo puede incluso cargar y descargar módulos mientras se está ejecutando, es decir, "en caliente", sin necesidad de reiniciar el RPi. Por ejemplo, si enchufamos un adaptador WiFi USB en el RPi, es posible que el núcleo utilice un LKM (Loadable Kernel Module o módulo cargable del núcleo) para hacerlo funcionar. Sin esta capacidad modular, el núcleo de Linux sería extremadamente grande, puesto que tendría que dar soporte a todos los controladores que el RPi pudiera llegar a necesitar. Asimismo, tendríamos que recompilar el núcleo cada vez que quisiéramos añadir un nuevo hardware. Una desventaja de los LKM es que los archivos de controlador se deben mantener para cada dispositivo. La interacción con los LKM se describe a lo largo de todo el libro, y en el capítulo 16 veremos cómo escribir nuestros propios LKM.
Como se indica en la figura 3-1, las etapas del gestor de arranque (bootloader stages) ceden el control al núcleo después de que este haya finalizado su descompresión en la memoria. Después, el núcleo monta el sistema de archivos raíz. El último paso del núcleo durante el proceso de arranque es la llamada a systemd init (/sbin/init en un RPi con Raspbian Jessie), que es el primer proceso de usuario que se inicia, y también el siguiente tema que vamos a tratar.
El gestor de sistema y servicios systemd
Un gestor de sistema y servicios (system and service manager) inicia y detiene servicios (por ejemplo, servidores web, servidor SSH) dependiendo del estado actual del RPi, es decir, si se está iniciando, apagando, etc. El gestor de sistema
y servicios systemd se ha añadido recientemente, y no sin controversia, a Linux y tiene como objetivo sustituir al gestor System V (SysV) init, al tiempo que mantiene la compatibilidad hacia atrás. Una desventaja grave de SysV init es que inicia los servicios en serie, es decir, que espera a que una tarea finalice antes de comenzar la siguiente, lo que puede alargar el tiempo de arranque del dispositivo. El gestor systemd está activado de forma predeterminada en Debian 8/Raspbian 8 (Jessie) y permite iniciar los servicios del sistema en paralelo, lo que reduce, obviamente, los tiempos de arranque, sobre todo en procesadores multinúcleo como los de los RPi 2/3. De hecho, podemos mostrar la duración de dicho proceso del siguiente modo:
pi@erpi ~ $ systemctl --version
systemd 215 +PAM +AUDIT +SELINUX +IMA +SYSVINIT +LIBCRYPTSETUP +GCRYPT
+ACL +XZ -SECCOMP -APPARMOR
pi@erpi ~ $ systemd-analyze time
Startup finished in 2.230s (kernel) + 6.779s (userspace) = 9.009s
ADVERTENCIA Si observa el mensaje “command not found” (comando no encontrado) en este punto, lo más probable es que esté usando una distribución Raspbian 7, que emplea SysV init. Para más información, visite la página web de este capítulo: www.exploringrpi.com/chapter3/.
Además de ser un gestor de sistema y servicios, systemd incluye software para gestión de inicios de sesión, archivos de log, gestión de dispositivos, sincronización de tiempos, etc. Los críticos de systemd insisten en que su proyecto de desarrollo ha traspasado sus propios límites y ha ido invadiendo áreas alejadas de su propósito fundamental. Hasta cierto punto, esta invasión de otras áreas ha supuesto que systemd resulte ahora básico para el futuro del propio Linux, hasta el punto de eliminar las posibilidades de elegir de los propios usuarios. Sin embargo, parece claro que systemd goza de una amplia aceptación por parte de las distribuciones Linux y que está aquí para quedarse.
Podemos utilizar el comando systemctl para inspeccionar y controlar el estado de systemd. Si lo invocamos sin argumentos, nos ofrecerá una lista completa de servicios en ejecución en el RPi (para pasar a otra pantalla, pulse la barra espaciadora, y para salir, Q):
pi@erpi ~ $ systemctl
networking.service loaded active exited LSB: Raise network interfaces
ntp.service loaded active running LSB: Start NTP daemon
serial-getty@ttyAMA0 loaded active running Serial Getty on ttyAMA0
ssh.service loaded active running OpenBSD Secure Shell server
getty.target loaded active active Login Prompts ...
El comando systemd uses archivos de servicio (service files), que presentan una extensión service, para definir el comportamiento de los diferentes servicios durante su inicio, apagado, recarga, etc. Véase el directorio /lib/systemd/system.
El servicio del protocolo NTP (Network Time Protocol, protocolo de tiempo de red) se ejecuta por defecto con la propia instalación. El gestor systemd sirve para manejar tales servicios en el RPi. Por ejemplo, podemos identificar el número exacto del servicio y obtener su estado actual del siguiente modo:
pi@erpi:~$ systemctl list-units -t service | grep ntp
ntp.service loaded active running LSB: Start NTP daemon
pi@erpi:~$ systemctl status ntp.service
• ntp.service - LSB: Start NTP daemon
Loaded: loaded (/etc/init.d/ntp)
Active: active (running) since Mon 2016-01-02 13:00:48 GMT; 2h 21min ago
Process: 502 ExecStart=/etc/init.d/ntp start (code=exited, status=0/ SUCCESS)
CGroup: /system.slice/ntp.service
├─552 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 107:112
└─559 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 107:112
Podemos detener el servicio ntp con el comando systemctl, momento a partir del que dejará de actualizar el reloj con el tiempo de la red.
pi@erpi:~$ sudo systemctl stop ntp
pi@erpi:~$ systemctl status ntp
• ntp.service - LSB: Start NTP daemon
Loaded: loaded (/etc/init.d/ntp)
Active: inactive (dead) since Mon 2017-01-02 17:42:26 GMT; 6s ago
Process: 1031 ExecStop=/etc/init.d/ntp stop (code=exited, status=0/SUCCESS)
Process: 502 ExecStart=/etc/init.d/ntp start (code=exited, status=0/SUCCESS)
Para reiniciar el servicio, ejecutamos:
pi@erpi ~ $ sudo systemctl start ntp
La tabla 3-1 ofrece un resumen de los comandos de systemd con la sintaxis del servicio ntp como ejemplo. Muchos de estos comandos exigen elevación a permisos de superusuario con el uso de sudo, como veremos en la sección siguiente.
Tabla 3-1: Comandos habituales de systemd.
Comando | Descripción |
systemctl | Lista todos los servicios en ejecución. |
systemctl start ntp | Inicia un servicio. No persiste después de reiniciar. |
systemctl stop ntp | Detiene un servicio. No persiste después de reiniciar. |
systemctl status ntp | Muestra el estado del servicio. |
systemctl enable ntp | Habilita un servicio durante el arranque. |
systemctl disable ntp | Impide que un servicio se inicie durante el arranque. |
systemctl is-enabled ssh | Muestra si un servicio se inicia durante el arranque. |
systemctl restart ntp | Reinicia un servicio (lo detiene y lo vuelve a iniciar). |
systemctl condrestart ntp | Reinicia un servicio solo si está en ejecución. |
systemctl reload ntp | Carga los archivos de configuración de un servicio sin detenerlo. |
journalctl –f | Siga el archivo de registro systemd. Pulsamos Control+C para salir. |
hostnamectl --static set-hostname ERPi | Cambia el nombre del host. |
timedatectl | Muestra fecha y hora, así como la información de la zona horaria. |
systemd-analyze time | Muestra la duración del arranque. |
El nivel de ejecución (runlevel) describe el estado actual del RPi y se puede utilizar para controlar los procesos o servicios iniciados por el sistema init. Bajo SysV hay diferentes niveles de ejecución, identificados como 0 (halt), 1 (single-user mode), 2 a 5 (multi-user modes), 6 (reboot) y S (start-up). Cuando el proceso init se inicia, el nivel de ejecución se inicia en N (none). Luego, entra en el nivel de ejecución S para inicializar el sistema en modo monousuario y, finalmente, pasa a uno de los modos multiusuario. Para averiguar el nivel de ejecución actual, escriba lo siguiente:
pi@erpi ~ $ who -r
run-level 5 2016-01-02 03:23
En este caso, el RPi presenta un nivel de ejecución 5. Podemos cambiar el nivel de ejecución escribiendo init seguido del número del nivel. Por ejemplo, podemos reiniciar nuestro RPi escribiendo:
pi@erpi ~ $ sudo init 6
Como vemos, systemd conserva cierta compatibilidad hacia atrás con los niveles de ejecución de SysV y sus números, puesto que los antiguos comandos de SysV funcionan correctamente en systemd. No obstante, utilizar niveles de ejecución en systemd es una práctica considerada obsoleta. En su lugar, systemd utiliza objetivos con identificador (named target units), algunos de los cuales se listan en la tabla 3-2, que muestra las equivalencias con los niveles de ejecución SysV. Para identificar el objetivo con identificador predeterminado en cada momento en el RPi, escribimos:
pi@erpi ~ $ systemctl get-default
graphical.target
Indica que la configuración actual exige que el RPi tenga un monitor para interfaz con ventanas. El comando siguiente le permite observar también la lista de unidades que el objetivo carga:
pi@erpi ~ $ systemctl list-units --type=target
UNIT LOAD ACTIVE SUB DESCRIPTION
basic.target loaded active active Basic System
cryptsetup.target loaded active active Encrypted Volumes
getty.target loaded active active Login Prompts
graphical.target loaded active active Graphical Interface
multi-user.target loaded active active Multi-User System
...
Tabla 3-2: Objetivos systemd junto con los niveles de ejecución de SysV.
Nombres de objetivo | SysV | Descripción y ejemplo de uso |
poweroff.target | 0 | Detiene el sistema: estado de apagado para todos los servicios. |
rescue.target | 1,S | Modo monousuario administrador: para funciones administrativas, por ejemplo comprobar el sistema de archivos. |
multi-user.target | 2-4 | Modos estándar multiusuario sin monitor para interfaz con ventanas. |
graphical.target | 5 | Modo estándar multiusuario con monitor para interfaz con ventanas. |
reboot.target | 6 | Reinicia el sistema: estado de reinicio para todos los servicios. |
emergency.target | — | Shell de emergencia solo para la consola principal. |
Si utilizamos el RPi como dispositivo de red, sin monitor, mantener activos los servicios de interfaz de ventanas parece un derroche de recursos de CPU y memoria. Podemos pasar a un modo objetivo sin monitor usando la llamada siguiente, donde la interfaz gráfica LXDE dejará de estar presente y la entrada graphical.target desaparecerá de la lista de unidades:
pi@erpi ~ $ sudo systemctl isolate multi-user.target
pi@erpi ~ $ systemctl list-units --type=target | grep graphical
Análogamente, podemos reactivar el modo objetivo con monitor e interfaz gráfica de este modo:
pi@erpi ~ $ sudo systemctl isolate graphical.target
Por último, para configurar el RPi de manera que utilice un nivel de ejecución distinto de forma predeterminada durante el arranque, por ejemplo sin monitor, usaremos:
pi@erpi ~ $ sudo systemctl set-default multi-user.target
Created symlink from /etc/systemd/system/default.target to /lib/systemd/sys
tem/multi-user.target.
pi@erpi ~ $ systemctl get-default
multi-user.target
Tras reiniciar, los servicios de interfaz gráfica con ventanas no arrancarán, y el nivel de ejecución SysV equivalente se mostrará con el número 3.
Gestión de sistemas Linux
En esta sección examinaremos más de cerca el sistema de archivos de Linux, a partir de los comandos y herramientas descritas en el capítulo 2. De este modo adquiriremos un control administrativo completo del RPi.
El superusuario
En los sistemas Linux, la cuenta de administrador del sistema tiene el nivel de acceso con la seguridad más alta para todos los comandos y archivos. Esta cuenta se suele conocer como root o superuser, superusuario. En Raspbian/Debian, esta cuenta tiene el nombre de usuario root, pero suele estar deshabilitada de forma predeterminada. Sin embargo, la podemos habilitar escribiendo sudo passwd root desde un intérprete de comandos en el que haya iniciado sesión la cuenta pi (username: pi, password: raspberry):
pi@erpi ~ $ sudo passwd root
Enter new UNIX password: mySuperSecretPassword
Retype new UNIX password: mySuperSecretPassword
passwd: password updated successfully
NOTA Llamar a la cuenta “root” está relacionado con el hecho de que es la única cuenta de usuario con permiso para alterar el directorio raíz (root) de más alto nivel (/). Para más información diríjase a www.linfo.org/root.htm.
Se recomienda no realizar el manejo cotidiano del sistema Linux desde la cuenta de superusuario. Sin embargo, tampoco debemos olvidar que manejar el RPi no es lo mismo que gestionar un servidor con miles de cuentas de usuario. En muchas aplicaciones, una sola cuenta de superusuario con una contraseña no estándar es más que suficiente. Sin embargo, utilizar una cuenta estándar para los trabajos de desarrollo puede protegernos de errores catastróficos, como eliminar accidentalmente el sistema de archivos. La cuenta de usuario pi en Raspbian ha sido configurada cuidadosamente para simplificar la interacción con el hardware. De este modo, está lista para ser usada para la mayoría de las tareas descritas en este libro. Debemos, no obstante, comprender bien cómo está configurada la cuenta y por qué funciona tan bien.
Bajo muchas distribuciones de Linux, incluida Raspbian, se emplea sudo siempre que se hace necesario realizar una labor con privilegios de administrador. Normalmente, la herramienta nos pide la contraseña del administrador y seguidamente nos permite operar con esos privilegios durante un cierto periodo de tiempo. La cuenta de usuario pi de Raspbian se ha configurado de manera que no necesita que escribamos la contraseña para los privilegios de administrador.
La sección siguiente describe la gestión de cuentas de usuario, pero si creamos una nueva cuenta de usuario y deseamos que pueda usar la herramienta sudo, su nombre se debe añadir al archivo sudoers file, /etc/sudoers, utilizando la herramienta visudo. Para ello, inicie sesión como root y escriba visudo, o bien escriba directamente sudo visudo como pi. Las últimas líneas del archivo /etc/sudoers proporcionan la configuración de la cuenta de usuario pi, lo que explica por qué no hace falta contraseña para que este usuario ejecute la herramienta sudo:
#User privilege specification
Root ALL=(ALL:ALL) ALL
#username hostnames=(users permitted to run commands as) permitted commands
pi ALL=(ALL) NOPASSWD: ALL
En esta configuración, el usuario pi recibe privilegios sobre todos (el primer ALL) los nombres de host para ejecutar comandos como cualquier usuario (el segundo ALL), así como para ejecutar todos los comandos del sistema (el tercer ALL) sin tener que escribir contraseña alguna. La herramienta (sudo) funciona bien, pero puede complicar la redirección de la salida de un comando, lo que veremos claramente más adelante en este capítulo.
Existe otro comando en Linux que nos permite ejecutar un intérprete de comandos con un usuario sustituto: su. Escribir su - (lo mismo que su - root) abre un nuevo intérprete de comandos con permisos de acceso de superusuario completos. Asimismo, se puede utilizar como sigue (una vez hayamos habilitado el inicio de sesión de root):
pi@erpi ~ $ su -
Password: mySuperSecretPassword
root@erpi:~# whoami
root
root@erpi:~# exit
logout
pi@erpi ~ $ whoami
pi
El símbolo del sistema, #, indica que hemos iniciado sesión con la cuenta de superusuario. Para volver a deshabilitar el acceso como root en el RPi, podemos escribir sudo passwd -l root.
Administración del sistema
El sistema de archivos de Linux consiste en una jerarquía de directorios que sirven para organizar los archivos en un sistema Linux. Esta sección examina la propiedad de los archivos, el uso de los enlaces simbólicos y el concepto de permisos de acceso del sistema.
El sistema de archivos de Linux
Linux utiliza estructuras de datos, llamadas "inodos" (inodes, nodos-i o nodos índice) para representar los objetos del sistema de archivos, como archivos y directorios. Cuando se crea un sistema de archivos ampliado (ext) de Linux, como ext3 o ext4, en un disco físico, se configura simultáneamente una tabla de inodos (inode table). Esta tabla comprende los vínculos a la estructura de datos inodo para cada archivo y directorio en un disco físico. La estructura de datos inodo para cada archivo y directorio almacena información como permisos, punteros a sectores físicos de almacenamiento, marcas de tiempo (time stamps) o número de enlaces (link counts). Podemos ver un ejemplo de ello realizando un listado ls -ail del directorio raíz, donde -i haga que ls muestre los índices inodo. Veremos lo siguiente para el directorio /tmp :
pi@erpi ~ $ cd /
pi@erpi / $ ls -ail | grep tmp
269 drwxrwxrwt 7 root root 4096 Jun 18 01:17 tmp
Por lo tanto, 269 es el índice inodo de /tmp. Si pasamos al directorio /tmp usando cd, creamos un archivo temporal (a.txt ) y ejecutamos ls -ail, veremos que el directorio actual (.) tiene exactamente el mismo índice inodo:
pi@erpi / $ cd tmp
pi@erpi /tmp $ touch a.txt
pi@erpi /tmp $ ls -ail
269 drwxrwxrwt 7 root root 4096 Jun 18 01:41 .
2 drwxr-xr-x 22 root root 4096 Jun 16 01:57 ..
4338 -rw-r--r-- 1 pi pi 0 Jun 18 01:41 a.txt
También podemos ver que el directorio raíz (..) tiene un índice inodo de 2, y que un archivo de texto (a.txt) tiene un índice inodo de 4338. Por lo tanto, no podemos cambiar de directorio (con cd) directamente a un índice inodo, porque el índice inodo podría no referirse a un directorio.
La figura 3-3 muestra el listado de un directorio de Linux, con los permisos que nos permiten trabajar con los archivos en Linux. La primera letra indica el tipo de archivo, por ejemplo: un directorio (d), un enlace (l) o un archivo estándar (-). También podemos encontrar tipos de archivos más raros: (c) carácter especial, (b) bloque especial, (p) fifo y (s) socket. Los directorios y los archivos estándar no necesitan más explicación, pero los enlaces sí, como vemos aquí:
Figura 3-3: Listado de un directorio de Linux con los permisos de cada archivo.
Enlaces a archivos y directorios
Hay dos tipos de enlaces en Linux: los enlaces simbólicos (soft links o symbolic links) y los enlaces duros (hard links). Un enlace simbólico es un archivo que hace referencia a la ubicación de otro archivo o directorio. Por contra, los enlaces duros se vinculan directamente con el índice inodo, pero no se pueden vincular con un directorio. Podemos crear un enlace usando ln /path/to/file.txt linkname. Se crea un enlace simbólico añadiendo -s a la llamada. Para ilustrar esto, el ejemplo siguiente creará un enlace simbólico y un enlace duro al archivo /tmp/test.txt:
pi@erpi ~ $ cd /tmp
pi@erpi /tmp $ touch test.txt
pi@erpi /tmp $ ln -s /tmp/test.txt softlink
pi@erpi /tmp $ ln /tmp/test.txt hardlink
pi@erpi /tmp $ ls -al
total 8
drwxrwxrwt 2 root root 4096 Jun 18 01:55 .
drwxr-xr-x 22 root root 4096 Jun 16 01:57 ..
-rw-r--r-- 2 pi pi 0 Jun 18 01:55 hardlink
lrwxrwxrwx 1 pi pi 13 Jun 18 01:55 softlink -> /tmp/test.txt
-rw-r--r-- 2 pi pi 0 Jun 18 01:55 test.txt
Puede observar un número 2 delante del archivo test.txt (después de los permisos del archivo). Este es el número de enlaces duros asociados al archivo. Es una cuenta que se incrementó en 1 cuando se creó el enlace duro, llamado hardlink. Si tuviéramos que eliminar el enlace duro, por ejemplo mediante rm hardlink, esta cuenta volvería al valor 1. Para ilustrar la diferencia entre enlaces simbólicos y enlaces duros, añadimos un fragmento de texto al archivo test.txt:
pi@erpi /tmp $ echo "testing links on the RPi" >> test.txt
pi@erpi /tmp $ more hardlink
testing links on the RPi
pi@erpi /tmp $ more softlink
testing links on the RPi
pi@erpi /tmp $ mkdir subdirectory
pi@erpi /tmp $ mv test.txt subdirectory/
pi@erpi /tmp $ more hardlink
testing links on the RPi
pi@erpi /tmp $ more softlink
softlink: No such file or directory
Podemos observar que cuando el archivo test.txt se mueve al subdirectorio, se rompe el enlace simbólico, pero el enlace duro sigue funcionando perfectamente. Así pues, comprobamos que los enlaces simbólicos no se actualizan cuando se mueve el archivo enlazado, mientras que los enlaces duros siempre hacen referencia al origen, aunque se haya movido o eliminado. Para ilustrar el último punto, podemos eliminar el archivo test.txt escribiendo lo siguiente:
pi@erpi /tmp $ rm subdirectory/test.txt
pi@erpi /tmp $ more hardlink
testing links on the RPi
Mas el archivo todavía existe, y no se borrará hasta que eliminemos el enlace duro llamado hardlink, con lo que la cuenta bajará hasta cero. Así pues, si un archivo tiene una cuenta de enlaces duros igual a cero, y no lo utiliza ningún proceso, será borrado. En efecto, el propio nombre, test.txt, no es más que un enlace duro. Observe que no se pueden establecer enlaces duros a través de distintos sistemas de archivos, puesto que cada sistema posee su propia tabla de índices inodo que empieza en 1. Por lo tanto, el inodo 269, que es el índice inodo del directorio /tmp, seguramente describa una cosa completamente diferente en otro sistema de archivos. Escriba el comando man ln para acceder a una guía especialmente útil sobre los enlaces a archivos.
NOTA Puede escribir history para obtener una lista de todos los comandos escritos anteriormente. Asimismo, puede pulsar Control+R para acceder a una búsqueda interactiva del historial de comandos y localizar uno que haya usado recientemente. Pulsar Intro activa el comando, mientras que pulsar Tab lo sitúa en nuestra línea de comandos para que podamos modificarlo.
Usuarios y grupos
Linux es un sistema operativo multiusuario que utiliza las siguientes tres clases diferenciadas para gestionar los permisos de acceso:
❏Usuario (user): podemos crear diferentes cuentas de usuario en nuestro RPi. Esto resulta útil si deseamos limitar el acceso a determinados procesos y áreas del sistema de archivos. La cuenta de usuario root es el superusuario del RPi y tiene acceso a todos y cada uno de los archivos. Por tanto, no sería seguro ejecutar un servidor web público desde esta cuenta, ni tampoco desde pi, si el servidor soporta scripts locales.
❏Grupo (group): las cuentas de usuario se pueden marcar (flag) para indicar que pertenecen a uno o varios grupos. Grupos que pueden contar con diferentes niveles de acceso a los distintos recursos del sistema, por ejemplo dispositivos UART o buses I2C.
❏Otros (others): todos los usuarios del RPi aparte del propietario del archivo, o de un miembro de un grupo listado en los permisos.
Podemos crear usuarios desde una ventana de terminal de Linux. Podemos ver la lista completa de grupos escribiendo more /etc/group. El ejemplo siguiente demuestra cómo crear una nueva cuenta de usuario en el RPi y modificar sus propiedades según nos convenga.
Ejemplo: crear una nueva cuenta de usuario en el RPI
Este ejemplo muestra cómo crear una cuenta de usuario y, luego, cambiar sus propiedades:
1. Creamos una nueva cuenta de usuario llamada molloyd en el RPi
2. Añadimos la cuenta a un nuevo grupo diseñado por nosotros mismos.
3. Añadimos la cuenta de usuario a los grupos de interfaz estándar del RPi.
4. Cambiamos la contraseña de la nueva cuenta de usuario.
5. Comprobamos que la cuenta funcione correctamente.
Paso 1: Crear el usuario molloyd del siguiente modo:
pi@erpi ~ $ sudo adduser molloyd
Adding user 'molloyd' ...
Adding new group 'molloyd' (1002) ...
Adding new user 'molloyd' (1001) with group 'molloyd' ...
Creating home directory '/home/molloyd' ...
Copying files from '/etc/skel' ...
Enter new UNIX password: ThePassword
Retype new UNIX password: ThePassword
passwd: password updated successfully
Changing the user information for molloyd
Enter the new value, or press ENTER for the default
Full Name []: Derek Molloy
Room Number []: Home
Work Phone []: XXXX
Home Phone []: XXXX
Other []: XXXX
Is the information correct? [Y/n] Y
Paso 2: Añadir el usuario a un nuevo grupo diseñado por nosotros mismos.
pi@erpi ~ $ sudo groupadd newgroup
pi@erpi ~ $ sudo adduser molloyd newgroup
Adding user 'molloyd' to group 'newgroup' ...
Adding user molloyd to group newgroup
Done.
pi@erpi ~ $ groups molloyd
molloyd : molloyd newgroup
Paso 3: Añadir el usuario a los grupos de usuarios estándar y de interfaz del RPi:
pi@erpi ~ $ sudo usermod -a -G pi,adm,dialout,cdrom,sudo,audio,video,
plugdev,users,games,netdev,gpio,i2c,spi,input molloyd
pi@erpi ~ $ groups molloyd
molloyd : molloyd adm dialout cdrom sudo audio video plugdev games users pi
netdev input spi i2c gpio newgroup
Paso 4: Cambiar la contraseña si es preciso:
pi@erpi ~ $ sudo passwd molloyd
Enter new UNIX password: ABetterPassword
Retype new UNIX password: ABetterPassword
passwd: password updated successfully
pi@erpi ~ $ sudo chage -d 0 molloyd
Para practicar los temas presentados hasta ahora en este capítulo, los siguientes ejemplos utilizan la cuenta de usuario molloyd. El primer ejemplo muestra el comando chown (change ownership) para cambiar la propiedad de un archivo, y para cambiar la propiedad de grupo del archivo usamos chgrp (change group).
Para poder invocar correctamente la herramienta sudo en el ejemplo, el usuario molloyd debe figurar en el archivo sudoers, lo que se hace desde el usuario pi ejecutando el comando visudo. Se puede modificar el archivo para que incluya una entrada molloyd del siguiente modo:
pi@erpi ~ $ sudo visudo
pi@erpi ~ $ sudo tail -n 2 /etc/sudoers
pi ALL=(ALL) NOPASSWD: ALL
molloyd ALL=(ALL) ALL
La cuenta de usuario molloyd puede ejecutar a partir de ahora el comando sudo, pero deberá escribir su contraseña para ello.
Permisos del sistema de archivos
Los permisos del sistema de archivos (file system permissions) indican qué niveles de acceso a un archivo o directorio tiene cada una de las clases de permisos. El comando chmod (change mode, cambiar modo) permite a un usuario cambiar los permisos de acceso a los objetos del sistema de archivos. Es posible especificar los permisos de una forma relativa. Por ejemplo, chmod a+w test.txt proporciona a todos los usuarios acceso de escritura al archivo test.txt, pero no cambia ningún otro permiso.
Por otra parte, también es posible especificar los permisos de una forma absoluta. Por ejemplo, chmod a=r test.txt establece que todos los usuarios tendrán únicamente acceso de lectura al archivo test.txt. El ejemplo siguiente muestra cómo modificar los permisos del sistema de archivos para un archivo utilizando el comando chmod.
La tabla 3-3 muestra la estructura de comandos para chown y chgrp. Asimismo, lista algunos comandos de ejemplo para trabajar con usuarios, grupos y permisos.
Tabla 3-3: Comandos para trabajar con usuarios, grupos y permisos.
Comando | Descripción |
chown molloyd a.txtchown molloyd:users a.txtchown -Rh molloyd /tmp/test | Cambia el propietario del archivo.Cambia propietario y grupo al mismo tiempo.Cambia la propiedad del directorio /tmp/test de forma recursiva. -h afecta a los enlaces simbólicos en lugar de a los archivos referenciados. |
chgrp users a.txtchgrp -Rh users /tmp/test | Cambia la propiedad de grupo del archivo.Cambia recursivamente con la misma -h que chown. |
chmod 600 a.txtchmod ugo+rw a.txtchmod a-w a.txt | Cambia permisos, como en la figura 3-3, para que el usuario tenga permisos de lectura/escritura al archivo. Tanto el grupo como otros no tienen acceso.Otorga a los usuarios, grupo y otros permiso de lectura/escritura a a.txt.Elimina el acceso de escritura para todos los usuarios de a, lo que quiere decir todos (usuarios, grupos y otros). |
chmod ugo=rw a.txt | Establece los permisos de lectura/escritura para todos. |
umaskumask -S | Lista la configuración de permisos por defecto. Con -S se muestra umask de una forma más legible. |
umask 022umask u=rwx,g=rx,o=rx | Modifica los permisos predeterminados en todos los archivos y directorios de nueva creación. Los dos comandos umask mostrados aquí son equivalentes. Si establecemos este valor de máscara y creamos un archivo o directorio, será drwxr-xr-x para el directorio, y -rw-r--r-- para el archivo. Es posible establecer una umask específica de usuario en el archivo .login de la cuenta. |
chmod u+s myexechmod g+s myexe | Establece un bit especial, llamado setuid bit (set user ID on execute, establecer ID de usuario al ejecutar) y setgid bit (set group ID on execute, establecer ID de grupo al ejecutar), que permite que un programa se ejecute como si lo hiciera otro usuario, pero con los permisos del propietario o grupo del archivo. Por ejemplo, esto permitiría que un programa concreto se ejecutase como si fuera el superusuario quien lo iniciase. Si el archivo no es ejecutable, se muestra una S mayúscula, en lugar de una s minúscula. |
chmod 6750 myexechmod u=rwxs,g=rxs,o= myexe | Establece el valor del bit setuid de forma absoluta. Ambos otorgarán a myexe los permisos rwsr-s---, con ambos bits, setuid y setgid, configurados (observe el espacio antes de myexe).Por razones de seguridad no se puede aplicar el bit setuid a un script del intérprete de comandos. |
stat /tmp/test.txt | Ofrece información útil sobre el estado del sistema de archivos para un archivo o directorio, por ejemplo el dispositivo físico en que se encuentra, información de su inodo, el último acceso o las últimas modificaciones o cambios. |
Veamos un ejemplo de la última entrada de la tabla 3-3, el comando stat:
molloyd@erpi:/tmp$ stat test.txt
File: 'test.txt'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: b302h/45826d Inode: 6723 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2015-06-19 00:06:28.551326384 +0000
Modify: 2015-06-19 00:06:28.551326384 +0000
Change: 2015-06-19 0:07:13.151016841 +0000
Birth: -
Observe que cada archivo de Linux conserva las marcas de tiempo de acceso, modificación y cambio. Podemos cambiar las marcas temporales de acceso y modificación artificialmente usando touch -a text.txt y touch -m test.txt, respectivamente (el tiempo de cambio se ve afectado en ambos casos). El tiempo de cambio se ve afectado también por las operaciones del sistema como chmod; el tiempo de modificación se ve afectado por una operación de escritura en el archivo, y el tiempo de acceso, en teoría, se ve afectado por una operación de lectura. Sin embargo, este comportamiento operacional significa que la lectura de un archivo ocasiona una escritura. Esta característica de Linux ocasiona un desgaste significativo en la tarjeta SD del RPi y un rendimiento deficiente en las operaciones de E/S. Por tanto, la funcionalidad de marca de tiempo de acceso a los archivos suele estar deshabilitada por defecto en la SD de arranque del RPi, mediante la opción de montaje (mount option) noatime en el archivo de configuración /etc/fstab que veremos en la sección siguiente. Observe que hay una opción similar, nodiratime, específica para directorios. No obstante, la opción noatime impide la actualización de la marca de tiempo para los accesos tanto a archivos como a directorios.
Para finalizar el análisis de la figura 3-3, decir que el ejemplo de la figura tiene 22 enlaces duros al archivo. Para un directorio, esto representa el número de subdirectorios, su directorio padre (..) y a sí mismo (.). El propietario de la entrada es el root y está en el grupo del root. La entrada siguiente, 4096, es el tamaño exigido para guardar los metadatos sobre los archivos contenidos en ese directorio (el tamaño mínimo es un sector, generalmente 4.096 bytes).
Para finalizar: si pedimos un listado de directorios ls -ld en el directorio root, veremos un bit t en los permisos del directorio /tmp. Dicho bit se conoce como sticky bit (literalmente, bit pegajoso), e indica que solo el permiso de escritura no basta para borrar archivos. Así pues, en el directorio /tmp cualquier usuario puede crear archivos, pero nadie puede borrar los archivos de otro usuario.
molloyd@erpi:/tmp$ cd /
molloyd@erpi:/$ ls -dhl tmp
drwxrwxrwt 7 root root 4.0K Jun 19 00:18 tmp
El comando ls -dhl lista los nombres de directorio, el parámetro d, y no su contenido, en formato legible para las personas (al menos para algunas), el parámetro h (de human readable) y en su versión larga, parámetro d.
El directorio raíz (root) de Linux
Explorar el sistema de archivos de Linux resulta aterrador para los principiantes. Si vamos al nivel superior con el comando cd / en el RPi y escribimos ls, veremos un listado del nivel más alto de la estructura de directorios del siguiente modo:
molloyd@erpi:/$ ls
bin boot.bak etc lib media opt root sbin sys usr
boot dev home lost+found mnt proc run srv tmp var
¿Qué significa todo esto? Cada uno de estos directorios tiene su papel y, si los entendemos bien, nos permitirán saber por dónde empezar a buscar archivos de configuración o los archivos binarios que necesitemos. En la tabla 3-4 tenemos una descripción breve de cada subdirectorio de nivel más alto de Linux.
Tabla 3-4: Estructura de directorios de nivel superior en Linux.
Directorio | Descripción |
bin | Contiene los binarios ejecutables utilizados por todos los usuarios y está en la variable de entorno PATH de forma predeterminada. Otro directorio, /usr/bin, contiene ejecutables que no resultan básicos para el arranque o reparación del sistema. |
boot | Contiene los archivos para arrancar el RPi. |
boot.bak | Contiene una copia de /boot generada tras una actualización del sistema. |
dev | Contiene los nodos de dispositivo, enlazados a sus controladores. |
etc | Archivos de configuración para el sistema local. |
home | Contiene los directorios raíz (home) de las cuentas de usuario (/home/pi es el directorio raíz de la cuenta de usuario pi). |
lib | Contiene las librerías estándar del sistema. |
lost+found | Aquí figuran los archivos sin vínculos que aparezcan tras ejecutar fsck (file system check and repair, comprobación y reparación del sistema de archivos). El comando mklost+found vuelve a crear el directorio lost+found si se ha eliminado. |
media | Utilizado para montar dispositivos removibles, como tarjetas micro-SD. |
mnt | Su uso típico es para montar sistemas de archivos temporales. |
opt | Un buen lugar para instalar software opcional de terceros. |
proc | Un archivo virtual que representa los procesos en ejecución en el RPi. Por ejemplo, si pasamos al directorio cd /proc y escribimos cat iomem, veremos algunos mapeos de asignaciones de memoria. |
root | El directorio raíz de la cuenta root (superusuario) en las distribuciones Raspbian y Debian Linux. En muchas otras distribuciones, es el directorio /home/root. |
run | Proporciona información acerca de la ejecución del sistema desde la última vez que arrancó. |
sbin | Contiene ejecutables para la gestión del root. |
srv | Almacena datos relacionados con ftp, servidores web, rsync, etc. |
sys | Contiene un sistema de archivos virtual que describe el sistema. |
tmp | Contiene archivos temporales. |
usr | Contiene programas para todos los usuarios, junto con muchos subdirectorios como: /usr/include (archivos de cabecera C/C++), /usr/lib (librerías C/C++), /usr/src (código fuente del núcleo de Linux), /usr/bin (ejecutables del usuario), /usr/local (similar a /usr, pero para usuarios locales) y /usr/share (archivos compartidos entre los usuarios). |
var | Contiene archivos variables como los log del sistema. |
Comandos para sistemas de archivos
Además de los comandos para trabajar con archivos y directorios en los sistemas de archivos, existen comandos para trabajar con el propio sistema de archivos. Los primeros comandos que deberíamos examinar son: df (disk free) y mount. El comando df ofrece un resumen de los sistemas de archivos en el RPi. El parámetro -T lista los tipos de sistemas de archivos:
pi@erpi / $ df -T
Filesystem Type 1K-blocks Used Available Use% Mounted on
/dev/root ext4 15186900 3353712 11165852 24% /
devtmpfs devtmpfs 470400 0 470400 0% /dev
tmpfs tmpfs 474688 0 474688 0% /dev/shm
tmpfs tmpfs 474688 0 474688 0% /sys/fs/cgroup
/dev/mmcblk0p1 vfat 57288 19824 37464 35% /boot
...
El comando df resulta útil para determinar si nos estamos quedando sin espacio en disco. Observamos que el sistema de archivos del root, /dev/root, está ocupado al 24% en este caso, con 11,2 GB de un tarjeta SD de 16 disponibles para instalar nuevo software. Asimismo, se listan las entradas de varios sistemas de archivos temporales (tmpfs) que en realidad se refieren a sistemas de archivos virtuales y que están mapeados en la DDR RAM del RPi. Las entradas /sys/fs/* se discuten con detalle en el capítulo 8. Además, la entrada /dev/mmcblk0p1 cuenta con un sistema de archivos vfat (virtual file allocation table, tabla de asignación de archivos virtuales), que se presentó con Windows 95, en una partición de 57 MB en la tarjeta SD. Esta partición vfat es un requisito de los gestores de arranque y para las actualizaciones de firmware.
NOTA Si nos estamos quedando sin espacio en el sistema de archivos root de la tarjeta SD del RPi, comprobaremos los log del sistema: /var/log. Unos archivos de log (o simplemente "logs") excesivamente grandes suelen ser sinónimo de problemas en el sistema, así que debemos revisarlos. Cuando hayamos resuelto cualquier problema, nos libraremos de ellos escribiendo: cat /dev/null > /var/log/messages con privilegios de root (véase también kern.log, dpkg.log y syslog). Por ejemplo, para vaciar el log dpkg.log desde la cuenta pi, sin eliminar el archivo ni reiniciar sus permisos, escribimos:
pi@erpi /var/log $ sudo sh -c "cat /dev/null > dpkg.log"
La llamada sh -c ejecuta todo el comando incluido en las comillas con permisos de superusuario. Esto es obligatorio porque, en el comando sudo cat /dev/null > dpkg.log, sudo por sí solo no efectúa la redirección de la salida >, sino que esta se intenta llevar a cabo como el usuario pi y, por tanto, falla porque sus permisos son insuficientes. Este es el problema con la redirección que presenta sudo, al que nos referimos anteriormente en el capítulo.
El comando lsblk (list block devices, listar dispositivos de bloques) presenta una lista concisa, estructurada en forma de árbol, con dispositivos tales como tarjetas SD, lápices de memoria USB y lectores de tarjetas (si los hubiera) conectados al RPi. Como se muestra en la salida siguiente, podemos ver que mmcblk0 (la tarjeta SD de arranque) se divide en dos particiones: p1, fijada a /boot, y p2, fijada a la raíz del sistema de archivos /. En este ejemplo, hay un lector USB para tarjetas micro-SD con una tarjeta de 32 GB (véase la figura 1-8(b)) conectado a uno de los puertos USB. Se muestra como el dispositivo de bloques sda con una sola partición, sda1:
pi@erpi ~ $ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 1 29.8G 0 disk
└─sda1 8:1 1 29.8G 0 part
mmcblk0 179:0 0 14.9G 0 disk
├─mmcblk0p1 179:1 0 56M 0 part /boot
└─mmcblk0p2 179:2 0 14.8G 0 part /
Claramente, los puertos USB sirven para conectar almacenamiento adicional, lo que resulta útil si capturamos datos de vídeo y la capacidad de la tarjeta
SD del sistema no es suficiente. Podemos probar el rendimiento de las tarjetas SD para asegurarnos de que se ajustan a las necesidades de nuestras aplicaciones mediante el ejemplo de la página siguiente.
Utilizar el comando mount sin argumentos nos facilita más información del sistema de archivos en el RPi.
pi@erpi ~ $ mount
/dev/mmcblk0p2 on / type ext4 (rw,noatime,data=ordered)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,relatime) ...
Como ya vimos, el sistema de archivos se organiza como un árbol único fijado al directorio raíz: /. Escribir cd / nos lleva directamente a la raíz del sistema. El comando mount se puede emplear para montar el sistema de archivos propio de un dispositivo de almacenamiento en este árbol. Los sistemas de archivos en dispositivos físicos separados se pueden montar en puntos definidos (named points) en ubicaciones del árbol. La tabla 3-5 describe algunos de los comandos del sistema de archivos que podemos utilizar para gestionarlo. Después se exponen dos ejemplos que demuestran cómo utilizar el comando mount para realizar importantes tareas de administración del sistema en el RPi.
Tabla 3-5: Comandos útiles para los sistemas de archivos.
Comando | Descripción |
du -h /optdu -hs /opt/*du -hc *.jpg | Utilización de disco: muestra el espacio ocupado por un árbol de directorios. Opciones: (-h) formato legible para las personas, (-s) resumen, (-c) total. El último comando encuentra el tamaño total del los archivos JPG en el directorio actual. |
df -h | Muestra el espacio del disco del sistema en formato legible (-h). |
lsblk | Lista los dispositivos de bloques. |
dd if=test.img of=/dev/sdXdd if=/dev/sdX of=test.img | dd convierte y copia un archivo, donde if es el archivo de entrada (input file) y of es el de salida (output file). Use este comando en Linux para escribir una imagen en una tarjeta SD. En Linux de escritorio, se suele utilizar así:sudo dd if=./RPi*.img of=/dev/sdXdonde /dev/sdX es el dispositivo de lectura/escritura de la tarjeta SD. |
cat /proc/partitions | Lista todas las particiones registradas. |
mkfs /dev/sdX | Crea un sistema de archivos Linux. También mkfs.ext4, mkfs.vfat. Se debe usar con precaución porque destruye todos los datos del dispositivo. ¡Téngalo presente! |
fdisk -l | Observe que fdisk se puede usar para gestionar discos, crear y eliminar particiones, etc. El comando fdisk -l muestra todas las particiones existentes. |
badblocks /dev/mmcblkX | Localiza sectores erróneos en la tarjeta SD. Las tarjetas SD incorporan circuitos para nivelación de desgaste. Si se presentan errores, es hora de adquirir una tarjeta nueva. Es mejor no grabarlos con fsck. Ejecutamos este comando con privilegios de root, siempre teniendo en cuenta que tarda un tiempo en completarse. |
mount /media/store | Monta una partición si aparece en /etc/fstab. |
umount /media/store | Desmonta una partición. Se nos informa antes si hay algún archivo abierto en esta partición. |
sudo apt install treetree ~/exploringrpi | Instalamos el comando tree y lo utilizamos para mostrar el repositorio de código de este libro con estructura de árbol de directorios. |
Los comandos find y whereis
El comando find resulta útil para buscar un archivo concreto en una estructura de directorios. Tiene un alcance enormemente amplio. Escriba man find para obtener un listado completo de las opciones. Por ejemplo, utilice la llamada siguiente para encontrar el archivo de cabecera C++ iostream en el sistema de archivos RPi; usar sudo evita problemas con los permisos de acceso:
pi@erpi / $ sudo find . -name iostream*
./usr/include/c++/4.9/iostream
./usr/include/c++/4.6/iostream
Usar -iname en lugar de name no discrimina mayúsculas y minúsculas en la búsqueda.
En el ejemplo siguiente se buscan los archivos en el directorio /home que hayan sido modificados en las últimas 24 y antes de las últimas 24 horas, respectivamente:
pi@erpi ~ $ echo "RPiTest File" >> new.txt
pi@erpi ~ $ sudo find /home -mtime -1
/home/pi
/home/pi/.bash_history
/home/pi/new.txt
pi@erpi ~ $ sudo find /home -mtime +1
/home/pi/.profile
/home/pi/.bashrc ...
Otra posibilidad es utilizar las opciones momento de acceso (-atime), tamaño (-size), propietario (-user), grupo (-group) y permiso (-perm).
NOTA Use el comando grep para buscar en un directorio de forma recursiva archivos que contengan una cadena de texto concreta, donde -r especifica que la búsqueda será recursiva, -n muestra el número de línea de la ubicación y -e viene seguido de un patrón de búsqueda:
pi@erpi ~ $ sudo grep -rn /home -e "RPiTest"
/home/pi/new.txt:1:RPiTest File
Para consultar más opciones, utilice man grep.
El comando whereis es distinto en el sentido de que puede buscar binarios ejecutables, código fuente y páginas de manual (man pages) de un programa concreto:
pi@erpi ~ $ whereis find
find: /usr/bin/find /usr/share/man/man1/find.1.gz
En este caso, los comandos binarios están en /usr/bin y la página de manual está en /usr/share/man/man1 (almacenada en formato gzip para ahorrar espacio).
Los comandos more y less
El comando more (más, en inglés) ya se ha utilizado varias veces, y seguramente el lector habrá ya deducido para qué sirve. Nos permite ver un archivo grande, o una corriente de salida (output stream) muy larga, página a página. Así pues, para ver un archivo largo podemos escribir more filename. Por ejemplo, el archivo de log /var/log/dmesg contiene todos los mensajes de salida del núcleo. Podemos revisar el archivo página a página con el comando more /var/log/dmesg. Sin embargo, si queremos que la información en la pantalla sea concisa, podemos utilizar el valor -5 para que solo se muestren cinco líneas en cada página.
pi@erpi ~ $ more -5 /var/log/dmesg
[ 0.000000] Booting Linux on physical CPU 0xf00
[ 0.000000] Initializing cgroup subsys cpu
[ 0.000000] Initializing cgroup subsys cpuacct
[ 0.000000] Linux version 3.18.11-v7+ (dc4@dc4-XPS13-9333)(gcc version 4.8.3
20140303 (prerelease)(crosstool-NG linaro-1.13.1+bzr2650-Linaro GCC 2014.03)
--More--(2%)
Con la barra espaciadora pasamos las páginas y pulsando Q (Quit) salimos del archivo. Existe un comando, less (menos, en inglés), que resulta aún más potente:
pi@erpi ~ $ less /var/log/dmesg
El comando less nos proporciona una vista completamente interactiva que podemos manejar desde el teclado. Tiene, no obstante, demasiadas opciones como para listarlas aquí. Por ejemplo, podemos utilizar las teclas de cursor para movernos arriba y abajo, pasar de página con la barra espaciadora, buscar una cadena de texto escribiendo / (por ejemplo: /usb, para encontrar mensajes relacionados con dispositivos USB) y, luego, pulsar la tecla N para ir a la siguiente ocurrencia de la cadena buscada, o bien Mayús+N para ir a la ocurrencia anterior.
La fiabilidad de los sistemas de archivos en las tarjetas SD
Uno de los puntos críticos del RPi es su tarjeta SD, o como se la conoce más genéricamente, su MMC (MultiMedia Card, tarjeta multimedia). La memoria flash NAND, como la de las MMC, ofrece gran espacio de almacenamiento con bajo coste, pero presenta una cierta tendencia al desgaste que puede resultar en errores del sistema de archivos.
La amplia capacidad de las MMC se debe en gran medida al desarrollo de las memorias MLC (Multi-Level Cell) o de celda multinivel. A diferencia de la memoria SLC (Single-Level Cell) o de celda mononivel, las MLC son capaces de guardar más de 1 bit en una sola celda de memoria. Los altos niveles de voltaje necesarios para borrar una celda perjudican a las celdas adyacentes, así que la memoria flash NAND se borra en bloques de 1 a 4 KB. Con el paso del tiempo, el proceso de escritura en la memoria flash NAND hace que los electrones queden atrapados y que se reduzca la diferencia de conductividad entre los estados "guardado" y "borrado" (disponible) del sustrato semiconductor de la memoria. (El lector interesado podrá encontrar un análisis (en inglés) más profundo de las diferencias entre SLC y MLC para aplicaciones de elevada fiabilidad en la dirección web tiny.cc/erpi305.) Las MLC utilizan niveles de carga diferentes y voltajes superiores para almacenar más estados en cada celda individual. Los productos comerciales MLC suelen ofrecer entre 4 y 16 estados por celda. Como las SLC solo guardan un estado, gozan de ventaja en cuanto a fiabilidad, con unos 60.000 a 100.000 ciclos de borrado/escritura frente a los 10.000 que suelen ofrecer las memorias flash MLC. Las tarjetas MMC son perfectas para un uso cotidiano en aplicaciones como fotografía digital, donde 10.000 ciclos de escritura/borrado deberían garantizar más de 25 años de vida útil (con un ciclo de escritura/borrado de la tarjeta completa cada día, lo que tampoco suele ocurrir).
Sin embargo, los dispositivos Linux empotrados realizan constantemente tareas de escritura en sus MMC, como el registro de eventos del sistema en /var/log. Conque el RPi escriba en un archivo de log unas 20 veces diarias, la vida útil de una tarjeta SD sería de unos ocho meses. Tales cifras son conservadoras, no obstante, y gracias a la tecnología ya mencionada con algoritmos de nivelación de desgaste (wear leveling algorithms) la vida útil puede ser mucho más larga. Las MMC usan los algoritmos de nivelación de desgaste durante la escritura de datos para asegurar que las reescrituras se distribuyen uniformemente en todo el espacio de almacenamiento. De este modo se logran evitar los fallos de sistema de Linux ocasionados por una excesiva concentración de modificaciones, tales como cambios en los mismos archivos de log.
En el caso de nuestro RPi, debemos asegurarnos de adquirir una tarjeta SD de la mejor calidad posible, y mejor si es de una marca reconocida. Además, debe ser de gran capacidad. La razón es sencilla: cuanto más espacio quede sin usar en la SD, mejor funcionarán los algoritmos de nivelación de desgaste, siquiera por motivos físicos. Como nota al margen, decir que otras placas con Linux empotrado, como la BeagleBone Black, utilizan almacenamiento eMMC (embedded MMC, MMC empotrado); básicamente, una MMC en un chip soldado en la placa. Estas memorias eMMC suelen ser también MLC y ofrecen una fiabilidad análoga a la de las tarjetas SD. Sin embargo, una de sus ventajas es que el fabricante posee el control sobre la calidad y las especificaciones de la eMMC que desea incluir en su placa. Por último, la mayoría de los discos duros de estado sólido SSD (Solid State Disk) también incorporan tecnología MLC, mientras que los más caros y profesionales emplean la (más fiable) SLC.
Para aplicaciones en el RPi que requieran mayor fiabilidad, se puede utilizar un sistema de archivos basado en RAM (tmpfs) en los directorios /tmp y /var/cache, y para los archivos de log, especialmente /var/log/apt. Lograremos esto editando el archivo /etc/fstab para que los directorios necesarios se monten en memoria. Por ejemplo, si tenemos procesos que requieran compartir datos de archivos entre ellos para intercambiar datos, podríamos usar el directorio /tmp como sistema de archivos RAM (tmpfs) editando el archivo /etc/fstab del siguiente modo:
pi@erpi /etc $ sudo nano fstab
pi@erpi /etc $ more fstab
proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat defaults 0 2
/dev/mmcblk0p2 / ext4 defaults,noatime 0 1
tempfs /tmp tmpfs size=100M 0 0
Seguidamente, aplicaríamos estos ajustes utilizando el comando mount.
pi@erpi /etc $ sudo mount -a
Y, luego, comprobamos que dichos ajustes se han añadido a la configuración:
pi@erpi /etc $ mount
...
tempfs on /tmp type tmpfs (rw,relatime,size=102400k)
El directorio raíz se monta de forma predeterminada con el atributo noatime activo, lo cual reduce drásticamente el número de accesos de escritura e incrementa el rendimiento de E/S, como ya vimos anteriormente en el capítulo. Siempre que sea posible debemos aplicar este atributo a todos los dispositivos de almacenamiento de estado sólido, como lápices USB, pero no es necesario para el almacenamiento basado en RAM.
Recuerde que cualquier dato que escribamos en un sistema de archivos temporal, tempfs, se perderá al reiniciar. Así pues, si utilizamos un tmpfs para /var/log, cualquier error del sistema que haga colgarse a la tarjeta no será visible en el reinicio. Podemos comprobarlo creando un archivo en el directorio /tmp con la configuración anterior y reiniciando acto seguido.
La asignación real de la RAM crece y mengua dependiendo del uso que el archivo haga del disco tmpfs. Así pues, podemos ser razonablemente generosos con la asignación de memoria. Por ejemplo, con los 100 MB del directorio /tmp montados en tmpfs:
pi@erpi /tmp $ cat /proc/meminfo | grep MemFree:
MemFree: 824368 kB
pi@erpi /tmp $ fallocate -l 75000000 test.txt
pi@erpi /tmp $ ls -l test.txt
-rw-r--r-- 1 pi pi 75000000 Jul 17 00:04 test.txt
pi@erpi /tmp $ cat /proc/meminfo | grep MemFree:
MemFree: 750788 kB
Ciertas distribuciones para el RPi emplean un sistema de archivos de solo lectura para mejorar la vida útil de la tarjeta SD, así como la estabilidad del propio sistema. Como ejemplo citaremos OpenELEC y el sistema de archivos comprimido SquashFS. No obstante, esto requiere mucho esfuerzo y no resulta práctico para el tipo de trabajo de prototipado que exponemos en este libro. Sin embargo, parece conveniente no perder esto de vista porque nos puede venir muy bien para el despliegue final del proyecto, donde la estabilidad del sistema resulta de extrema importancia.
Comandos de Linux
Cuando trabajamos en el terminal de Linux y escribimos comandos como date, el resultado de los mismos se envía a la "salida estándar" (standard output). Y vemos dicho resultado en nuestra ventana de terminal.
Redirección de entrada y salida (>, >> y <)
Es posible redirigir la salida a un archivo usando los operadores de redirección > y >>. El símbolo >> se usó anteriormente en este capítulo para añadir texto a archivos temporales. El operador > se puede emplear para enviar la salida a un nuevo archivo. Por ejemplo:
pi@erpi ~ $ cd /tmp
pi@erpi /tmp $ date > a.txt
pi@erpi /tmp $ more a.txt
Sat 20 Jun 12:59:43 UTC 2015
pi@erpi /tmp $ date > a.txt
pi@erpi /tmp $ more a.txt
Sat 20 Jun 12:59:57 UTC 2015
El operador >> manifiesta nuestro deseo de añadir contenido al archivo. El ejemplo siguiente ilustra el uso del operador >> con el nuevo archivo a.txt:
pi@erpi /tmp $ date >> a.txt
pi@erpi /tmp $ more a.txt
Sat 20 Jun 12:59:57 UTC 2015
Sat 20 Jun 13:00:17 UTC 2015
La salida estándar usando el símbolo < funciona de forma muy parecida. La inclusión de -e facilita el análisis de los caracteres de escape, como el de retorno (\n), que sitúa cada especie animal en una nueva línea:
pi@erpi /tmp $ echo -e "dog\ncat\nyak\ncow" > animals.txt
pi@erpi /tmp $ sort < animals.txt
cat
cow
dog
yak
Podemos combinar los operadores de redirección de entrada y de salida. Usando el archivo animals.txt podemos realizar operaciones como la siguiente:
pi@erpi /tmp $ sort < animals.txt > sorted.txt
pi@erpi /tmp $ more sorted.txt
cat
cow
dog
yak
Tuberías (| y tee)
Dicho simplemente: las tuberías (pipes) ,|, nos permiten conectar comandos de Linux. Al igual que redirigimos la salida a un archivo, podemos redirigir la salida de un comando para que sirva de entrada a otro comando. Por ejemplo, para listar el directorio raíz desde cualquier lugar del sistema y enviar (o "canalizar" como por una "tubería", pipe) la salida al comando sort, donde se colocará en orden inverso (reverse) merced al parámetro -r, escribiremos lo siguiente:
pi@erpi ~ $ ls / | sort -r
var
usr
...
bin
Podemos identificar qué instalaciones del usuario en el directorio /opt ocupan más espacio en disco: du nos facilita el tamaño de disco usado. Pasar el argumento -d1 significa que solo listaremos los tamaños 1 nivel por debajo del nivel del directorio actual, mientras que -h hace que los valores se muestren en formato legible. Podemos canalizar esta salida hacia el comando de filtro sort para realizar un ordenamiento numérico en orden inverso, es decir, colocando el más grande en el lugar más alto. De este modo, el comando es:
pi@erpi ~ $ du -d1 -h /opt | sort -nr
113M /opt
69M /opt/sonic-pi
41M /opt/vc
4.4M /opt/minecraft-pi
Otra herramienta útil, tee, nos permite al mismo tiempo redirigir una salida a un archivo y pasarlo al siguiente comando en la tubería; por ejemplo, guardar y ver. Usando el ejemplo anterior, si deseamos enviar la salida sin ordenar del comando du a un archivo, y mostrar una salida ordenada por la consola, escribiremos:
pi@erpi ~ $ du -d1 -h /opt | tee /tmp/unsorted.txt | sort -nr
113M /opt
69M /opt/sonic-pi
41M /opt/vc
4.4M /opt/minecraft-pi
pi@erpi ~ $ more /tmp/unsorted.txt
4.4M /opt/minecraft-pi
69M /opt/sonic-pi
41M /opt/vc
113M /opt
Asimismo, podemos utilizar tee para escribir la salida de varios archivos:
pi@erpi ~ $ du -d1 -h /opt | tee /tmp/1.txt /tmp/2.txt /tmp/3.txt
Comandos de filtro (de sort a xargs)
Cada uno de los comandos de filtro ofrece una función útil:
❏sort: este comando presenta varias opciones, por ejemplo: -r aplica un orden inverso, -f ignora mayúsculas y minúsculas, -d ordena siguiendo un diccionario e ignora la puntuación, -n ordenación numérica, -b ignora espacios en blanco, -i ignora los caracteres de control, -u muestra las líneas duplicadas una sola vez y -m une varias entradas en una sola salida.
❏wc (contar palabras): calcula el número de palabras, líneas o caracteres en una corriente (stream). Por ejemplo:
pi@erpi /tmp $ wc < animals.txt
4 4 16
Indica que hay 4 líneas, 4 palabras y 16 caracteres. Podemos seleccionar los valores independientemente usando -l para contar líneas, -w palabras, o -m caracteres, mientras que con -c imprimimos la cuenta de bytes (que en este caso también son 16).
❏head: muestra las primeras líneas de la salida, lo que resulta útil si tenemos un archivo o corriente de información y deseamos examinar solo las primeras líneas. De manera predeterminada, muestra las primeras diez líneas. Podemos especificar el número de líneas con la opción -n. Por ejemplo, para obtener las dos primeras líneas de salida del comando dmesg (display message, mostrar mensaje, o driver message, mensaje del controlador), que muestra el buffer de mensajes del núcleo, escribimos:
pi@erpi ~ $ dmesg | head -n2
[ 0.000000] Booting Linux on physical CPU 0xf00
[ 0.000000] Initializing cgroup subsys cpu
❏tail: funciona igual que head salvo porque muestra las últimas líneas de un archivo o corriente de datos. Usándolo en combinación con dmesg aporta unas salidas muy útiles, por ejemplo:
pi@erpi ~ $ dmesg | tail -n2
[ 8.896654] smsc95xx 1-1.1:1.0 eth0:link up,100Mbps,full-duplex...
[ 9.340019] Adding 102396k swap on /var/swap.
❏grep: analiza líneas utilizando texto y expresiones regulares. Podemos utilizar este comando para filtrar la salida con distintas opciones: -i ignora mayúsculas y minúsculas, -m 5 se detiene tras dar con cinco ocurrencias, -q realiza la búsqueda "en silencio" y la detiene y devuelve el estado 0 si encuentra alguna ocurrencia, -e para especificar algún patrón, -c muestra una cuenta de ocurrencias, -o solo muestra el texto que se corresponde con la búsqueda y -l muestra el nombre del archivo que contenga el texto buscado. Por ejemplo, el siguiente caso examina la salida dmesg hasta dar con las primeras tres ocurrencias de la cadena usb, usando el parámetro -i para ignorar mayúsculas y minúsculas:
pi@erpi ~ $ dmesg | grep -i -m3 usb
[ 1.280089] usbcore: registered new interface driver usbfs
[ 1.285762] usbcore: registered new interface driver hub
[ 1.291220] usbcore: registered new device driver usb
Las tuberías se pueden combinar. Por ejemplo, obtenemos exactamente la misma salida utilizando head y mostrando únicamente las tres primeras líneas de la salida de grep:
pi@erpi ~ $ dmesg | grep -i usb | head -n3
[ 1.280089] usbcore: registered new interface driver usbfs
[ 1.285762] usbcore: registered new interface driver hub
[ 1.291220] usbcore: registered new device driver usb
❏xargs: nos permite construir una lista de argumentos que utilizaremos para llamar a otro comando o herramienta. En el ejemplo siguiente, el archivo de texto args.txt, que contiene tres cadenas, se utiliza para crear tres nuevos archivos. La salida de cat se canaliza a xargs, donde pasa las tres cadenas como argumentos del comando touch, que crea tres nuevos archivos: a.txt, b.txt y c.txt:
pi@erpi /tmp $ echo "a.txt b.txt c.txt" > args.txt
pi@erpi /tmp $ cat args.txt | xargs touch
pi@erpi /tmp $ ls
args.txt a.txt b.txt c.txt
Existen más comandos de filtro muy útiles, entre ellos: awk, que programa cualquier tipo de filtro; fmt, que da formato al texto; uniq, que encuentra líneas únicas, y sed, para manipular una corriente de datos. Estos comandos van mucho más allá del ámbito de este libro. Por ejemplo, awk es un lenguaje de programación completo en sí mismo. La tabla 3-6 describe comandos útiles para emplear con tuberías y ofrece buenas ideas para usarlos.
Tabla 3-6: Comandos útiles para usar con tuberías
Comando | Descripción |
apt list --installed | grep camera | Lista los paquetes instalados y busca uno que contenga la cadena de caracteres "camera". Cada comando en esta tabla se escribe en una sola línea. |
ls -lt | head | Muestra los archivos del directorio actual por orden de antigüedad. |
cat urls.txt | xargs wget | Descarga los archivos listados en URL dentro de un archivo de texto urls.txt. |
dmesg | grep -c usb | Cuenta el número de veces que aparece la cadena usb en la salida de dmesg. |
find . -name "*.mp3" | grep -vi "effects" > /tmp/playlist.txt | Busca en el RPi (es decir, se ejecuta desde /con sudo) archivos mp3, ignorando cualquier archivo de efectos de sonido, para crear una lista de reproducción en /tmp. |
Los comandos echo y cat
El comando echo se limita a reproducir (como el eco) una cadena de texto, la salida de un comando o un valor determinado por la salida estándar. Veamos unos ejemplos:
pi@erpi /tmp $ echo 'hello'
hello
pi@erpi /tmp $ echo "Today's date is $(date)"
Today's date is Sat 20 Jun 14:31:21 UTC 2015
pi@erpi /tmp $ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
En el primer caso se reproduce una cadena de texto sencilla. En el segundo caso, las comillas," ", representan un comando emitido dentro de la llamada echo, y en el último caso se repite (echo) la variable de entorno PATH.
El comando echo nos permite también contemplar el estado de la salida de un comando usando $?. Por ejemplo:
pi@erpi ~ $ ls /tmp
args.txt a.txt b.txt c.txt playlist playlist.txt
pi@erpi ~ $ echo $?
0
pi@erpi ~ $ ls /nosuchdirectory
ls: cannot access /nosuchdirectory: No such file or directory
pi@erpi ~ $ echo $?
2
Claramente, el estado de salida para ls es 0 para una llamada con éxito, y 2 para un argumento no válido. Esto puede resultar útil cuando escribimos scripts y programas propios que devuelven un valor desde la función main().
El comando cat (concatenation, concatenar) nos facilita la unión de dos archivos desde la línea de comandos. El ejemplo siguiente usa echo para crear dos archivos, a.txt y b.txt. Luego, cat los concatena para crear uno nuevo, c.txt. Debemos utilizar el parámetro -e si queremos habilitar la interpretación de los caracteres de escape en la cadena que se pase a echo
pi@erpi ~ $ cd /tmp
pi@erpi /tmp $ echo "hello" > a.txt
pi@erpi /tmp $ echo -e "from\nthe\nRPi" > b.txt
pi@erpi /tmp $ cat a.txt b.txt > c.txt
pi@erpi /tmp $ more c.txt
hello
from
the
RPi
diff
El comando diff nos facilita la búsqueda de diferencias entre dos archivos. Ofrece una salida básica:
pi@erpi /tmp $ echo -e "dog\ncat\nbird" > list1.txt
pi@erpi /tmp $ echo -e "dog\ncow\nbird" > list2.txt
pi@erpi /tmp $ diff list1.txt list2.txt
2c2
< cat
---
> cow
El valor 2c2 en la salida indica que la línea 2 del primer archivo ha cambiado (changed) a la línea 2 del segundo, y que el cambio es cat a cow. El carácter a significa añadido o anexado (appended), y el d, borrado (deleted). Para una comparación lado a lado podemos usar lo siguiente:
pi@erpi /tmp $ diff -y -W70 list1.txt list2.txt
dog dog
cat | cow
bird bird
donde -y habilita la vista lado a lado y -W70 establece la anchura del texto que se muestra en la pantalla en 70 caracteres (columnas).
Si queremos utilizar una vista para las diferencias entre dos archivos más intuitiva, pero exigente, podemos usar el comando vimdiff (instalado mediante sudo apt install vim), que muestra una comparación lado a lado de los archivos usando el editor de texto vim (Vi IMproved). Escribimos vimdiff list1.txt list2.txt y usamos la secuencia de teclas vim: Escape : q ! dos veces para salir, o bien Escape : w q para guardar los cambios y salir. Vim requiere mucha práctica para dominar las secuencias de teclas.
tar
El comando tar es una utilidad para combinar archivos y directorios en un solo archivo (como un archivo zip descomprimido). Este archivo puede ser comprimido para ahorrar espacio. Para archivar y comprimir directorios o archivos, como /tmp, escribimos:
pi@erpi ~ $ tar cvfz tmp_backup.tar.gz /tmp
donde c significa nuevo archivo, v mostrar (verbose) el listado de archivos, z comprimir con gzip y f indica que lo que sigue es el nombre de archivo. También podemos encontrarnos la extensión .tar.gz representada como .tgz. Véase la tabla 3-7 para más ejemplos.
Tabla 3-7: Comandos útiles de tar.
Comando | Descripción |
tar cvfz name.tar.gz /tmp | Comprimir en formato gzip. |
tar cvfj name.tar.bz2 /tmp | Comprimir en formato bzip2, que generalmente tarda más pero genera archivos más pequeños. Introduzca cada comando de esta tabla en una sola línea. |
tar cvfJ name.tar.xz /tmp | Comprimir en formato xz (usado en los archivos de paquete .deb). |
tar xvf name.tar.* | Descomprimir archivo comprimido (x indica extraer). Detectará automáticamente el tipo de compresión, como gzip o bz2. |
tar xvf name.tar.* /dir/file | Extrae un archivo de otro. También funciona para un solo directorio. |
tar rvf name.tar filename | Añade otro archivo. |
tar cfz name-$(date +%m%d%y).tar.gz /dir/filename | Crea un archivo con la fecha actual, opción útil para los scripts y para jobs de copia de respaldo. Observe que debe hacer un espacio entre date y +%m%d%y. |
md5sum
El comando md5sum nos permite verificar el código hash que garantiza la integridad de los archivos y que no se han corrompido ni maliciosa ni accidentalmente. En el ejemplo siguiente descargamos la herramienta wavemon como paquete .deb, pero no la instalamos. El comando md5sum sirve para generar la suma de verificación (checksum) md5:
pi@erpi ~ $ sudo apt-get download wavemon
Get:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main
wavemon armhf 0.7.6-2 [48.2 kB] Fetched 48.2 kB in 0s (71.4 kB/s)
pi@erpi ~ $ ls -l *.deb
-rw-r--r-- 1 root root 48248 Mar 28 2014 wavemon_0.7.6-2_armhf.deb
pi@erpi ~ $ md5sum wavemon_0.7.6-2_armhf.deb
1dffa011736e25b63a054f1515d18b3e wavemon_0.7.6-2_armhf.deb
Ahora podemos comprobar la suma de verificación obtenida frente a la oficial para asegurarnos de que tenemos un archivo válido. Desgraciadamente, puede resultar difícil encontrar las sumas de verificación de cada paquete en línea. Si tenemos instalado wavemon, las sumas de verificación estarán en /var/ lib/dpkg/info/wavemon.md5sums. Podemos instalar una utilidad en Debian llamada debsums para comprobar la integridad del archivo, así como de todas sus partes.
pi@erpi ~ $ sudo apt install debsums wavemon
pi@erpi ~ $ debsums wavemon_0.7.6-2_armhf.deb
/usr/bin/wavemon OK
/usr/share/doc/wavemon/AUTHORS OK
/usr/share/doc/wavemon/NEWS.gz OK
...
Si compilamos nuestros propios paquetes para distribuirlos, resultaría útil distribuir los archivos con la suma de verificación correspondiente para que los usuarios puedan comprobar su integridad. Existe una alternativa a md5sum ,sha256sum, que se puede utilizar del mismo modo.
Procesos de Linux
Un proceso es una instancia de un programa que se está ejecutando en el SO. Debemos ser capaces de gestionar los procesos en ejecución en nuestro RPi, comprender los procesos de primer y de segundo plano y cerrar inmediatamente (kill, matar en inglés, pero también es el nombre literal de un comando Unix y Linux) cualquier proceso que se cuelgue.
Cómo controlar los procesos en Linux
El comando ps lista los procesos en ejecución en cada momento en el RPi. Tras escribir ps, vemos que el siguiente RPi está ejecutando dos procesos de usuario, el intérprete de comandos bash, con PID (Process ID, identificador de proceso) 912, y el propio comando ps, con PID 25481. El PID de ps será diferente siempre que lo ejecutemos porque se cierra cada vez que da los resultados:
pi@erpi ~ $ ps
PID TTY TIME CMD
912 pts/0 00:00:05 bash
25481 pts/0 00:00:00 ps
Para ver todos los procesos en ejecución, escribimos ps ax. En el ejemplo siguiente se aplica un filtro con los caracteres "ntp" para descubrir información acerca de los procesos ntp en ejecución en el RPi.
pi@erpi ~ $ ps ax | grep ntp
1069 ? Ss 0:00 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 107:112
1077 ? S 0:00 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 107:112
1132 ttyAMA0 S+ 0:00 grep --color=auto ntp
Está claro que se ejecutan tres procesos diferentes para el servicio, lo que le permite manejar múltiples conexiones simultáneas. En este ejemplo, todos los hilos están a la espera de que se complete un evento (S), PID 1069 es el líder de la sesión (Ss), 1077 es su clon (S) y el proceso grep, PID 1132, está en el grupo de primer plano (S+). Como vimos anteriormente, una llamada a systemctl status ntp proporciona información sobre los servicios en ejecución en el RPi, es decir, si ejecutamos la llamada veremos que los PID de los procesos se corresponden con los que muestra el comando ps.
Procesos en primer y en segundo plano
Linux es un sistema operativo multitarea que nos permite ejecutar múltiples procesos en segundo plano (background) mientras usamos un programa que se ejecuta en primer plano (foreground). Este concepto es muy similar al comportamiento que presenta una interfaz gráfica basada en ventanas, como las de Windows, Mac OS X, etc. Por ejemplo, el reloj del escritorio continúa actualizando la hora al mismo tiempo que navegamos por Internet.
Lo mismo es válido para aplicaciones que se ejecuten en una ventana de terminal. Para demostrar esto, veamos un pequeño fragmento de código fuente en C que muestra la frase "Hello World!" cada cinco segundos en una ventana de terminal de Linux. El funcionamiento preciso del código se verá en el capítulo 5, pero por el momento basta con que el lector lo escriba literalmente en un archivo, que llamará HelloRPiSleep.c, mediante el editor nano trabajando en la cuenta de usuario pi; para ello:
pi@erpi ~ $ cd ~/
pi@erpi ~ $ nano HelloRPiSleep.c
pi@erpi ~ $ more HelloRPiSleep.c
#include<unistd.h>
#include<stdio.h>
int main(){
int x=0;
do{
printf("Hello Raspberry Pi!\n");
sleep(5);
}while(x++<50);
return 0;
}
El programa consiste en un bucle que realiza 50 iteraciones, en cada una de las cuales muestra un mensaje y espera 5 segundos. Después de guardar el archivo como HelloRPiSleep.c, lo compilaremos como un ejecutable mediante el comando siguiente (-o especifica el nombre del archivo ejecutable):
pi@erpi ~ $ gcc HelloRPiSleep.c -o helloRPiSleep
pi@erpi ~ $ ls -l helloRPiSleep
-rwxr-xr-x 1 pi pi 5864 Jun 20 16:40 helloRPiSleep
Si todo ha funcionado, tendremos tanto el archivo de código fuente como el ejecutable llamado helloRPiSleep. Observe cómo este muestra la marca, x, de archivo ejecutable. Podemos, ahora, ejecutarlo sin más:
pi@erpi ~ $ ./helloRPiSleep
Hello Raspberry Pi!
Hello Raspberry Pi! ...
Continuará mostrando este mensaje cada 5 segundos. Podemos terminar su ejecución con Control+C. Sin embargo, para ejecutarlo en segundo plano tenemos dos opciones.
Primero podemos, en lugar de pulsar Control+C para terminar el proceso, pulsar Control+Z y, luego, en el símbolo del sistema, escribir el comando bg (por background, como ya hemos visto).
pi@erpi ~ $ ./helloRPiSleep
Hello Raspberry Pi!
^Z
[1]+ Stopped ./helloRPiSleep
pi@erpi ~ $ bg
[1]+ ./helloRPiSleep &
pi@erpi ~ $ Hello Raspberry Pi!
Hello Raspberry Pi!
Hello Raspberry Pi!
Cuando pulsamos Control+Z, ^Z aparece en la salida. Cuando escribimos bg, el proceso se sitúa en segundo plano, donde continúa su ejecución. De hecho, podemos continuar usando el terminal, pero resultará frustrante porque cada cinco segundos aparecerá el mensaje "Hello Raspberry Pi!" El comando fg nos permite devolver este proceso al primer plano.
pi@erpi ~ $ fg
./helloRPiSleep
Hello Raspberry Pi!
^C
pi@erpi:~$
Cuando pulsamos Control+C, que aparece como ^C, la aplicación se detiene.
La otra manera de poner la aplicación en segundo plano consiste en añadir el símbolo ampersand, &, al final de su nombre:
pi@erpi ~ $ ./helloRPiSleep &
[1] 30965
pi@erpi ~ $ Hello Raspberry Pi!
Hello Raspberry Pi!
El proceso se ha situado en segundo plano y su PID es 30965, en este caso. Para detener el proceso, usamos ps para encontrar el PID:
pi@erpi ~ $ ps aux|grep hello
pi 30965 0.0 0.0 1612 304 pts/0 S 20:14 0:00 ./helloRPiSleep
pi 30978 0.0 0.1 4208 1828 pts/0 S+ 20:15 0:00 grep hello
Para terminar el proceso, use el comando kill:
pi@erpi ~ $ kill 30965
[1]+ Terminated ./helloRPiSleep
Podemos confirmar la terminación del proceso usando ps de nuevo. Si el proceso no termina de inmediato, usaremos el argumento -9 para asegurarnos de que lo haga. Por ejemplo: kill -9 30965. Un comando diferente, pkill, termina un proceso a partir de su nombre. Así, en este caso:
pi@erpi ~ $ pkill helloRPiSleep
Otro comando que merece la pena mencionar es watch. Este ejecuta un comando a intervalos regulares y muestra el resultado en terminal a pantalla completa. Por ejemplo, para ver el log de mensajes del núcleo, escribimos:
pi@erpi ~ $ watch dmesg
Podemos concretar la duración del intervalo entre ejecuciones mediante -n seguido del número de segundos. Una buena manera de comprender el funcionamiento de watch consiste en ejecutarlo del siguiente modo:
pi@erpi ~ $ watch -n 1 ps a
Every 1.0s: ps a Sat Jun 20 20:22:39 2015
PID TTY STAT TIME COMMAND
912 pts/0 Ss 0:06 -bash
31184 pts/0 S+ 0:01 watch -n 1 ps a
31257 pts/0 S+ 0:00 watch -n 1 ps a
31258 pts/0 S+ 0:00 sh -c ps a
31259 pts/0 R+ 0:00 ps a
Veremos que el PID de ps, sh y watch cambia cada (1) segundo, lo que deja claro que watch está realmente ejecutando el comando (ps) al pasarlo a un nuevo intérprete de comandos mediante sh -c. La razón por la que watch aparece dos veces en la lista es que se reproduce a sí mismo temporalmente en el momento exacto en el que ejecuta ps a.
Otros temas de Linux
En este punto del libro, el lector ha podido revisar los principales comandos para trabajar con Linux en el RPi. Sin embargo, el tema de la gestión de sistemas Linux da mucho más de sí. Por ejemplo: ¿cómo configurar un adaptador WiFi? ¿Cómo usar cron para planificar (schedule) trabajos con el RPi? Estos temas se analizarán en los capítulos restantes del libro. Sin ir más lejos: la programación de trabajos con cron se trata en el capítulo 12, en el contexto de los dispositivos IoT.
Cómo utilizar Git para el control de versiones
De manera sencilla podemos decir que Git es un sistema que nos permite registrar cualquier cambio que se introduzca en el contenido de un proyecto de software a medida que avanza su desarrollo. Git, diseñado también por Linus Torvalds, se emplea hoy en la línea principal de desarrollo del núcleo de Linux. Git es un sistema increíblemente útil, que debemos entender bien por dos motivos principales: podemos utilizar Git en el desarrollo de nuestro propio software y además nos permite aprender a trabajar con distribuciones de código fuente del núcleo de Linux.
Git es un sistema distribuido de control de versiones, o DVCS (Distributed Version Contol System) que facilita el control del código fuente de un proyecto. Un sistema de control de versiones, VCS, registra y gestiona cualquier cambio introducido en documentos de cualquier tipo. Normalmente, los documentos que cambiemos se destacarán con números de revisión y marcas de tiempo. Se pueden comparar revisiones y hasta regresar a versiones antiguas de los documentos. Hay dos tipos de sistemas VCS:
❏Centralizados (CVCS): estos sistemas, como Apache Subversion (SVN), funcionan con la premisa de que existe una copia "maestra" única del proyecto. El flujo de trabajo es simple y directo: bajamos los cambios desde un servidor central, introducimos los nuestros propios y los volvemos a subir como definitivos a la copia maestra (esta subida definitiva de los cambios se denomina "commit", término típico también de las bases de datos; en español su uso como sustantivo ("hacer commit") es abrumadoramente mayoritario en el entorno informático, y es el que usaremos aquí).
❏Distribuidos (DVCS): usando estos sistemas, por ejemplo Git y Selenic Mercurial, no bajamos cambios, sino que clonamos el repositorio completo, incluido todo el histórico de cambios. El clonado del repositorio produce una copia tan exacta como una copia maestra, que hasta puede llegar a actuar como tal si fuera preciso. Por fortuna, los estándares actuales hacen que los documentos de texto plano y los archivos de código fuente no ocupen mucho espacio en disco. Una precisión importante: el modelo DVCS no excluye el uso de un repositorio maestro central para todos los usuarios. Véase, por ejemplo, git.kernel.org.
La ventaja principal de un DVCS frente a un CVCS es la posibilidad de hacer
commit rápidamente y probar las modificaciones localmente, en nuestro propio sistema, sin tener que subirlas antes a ninguna copia maestra. Esto permite la flexibilidad de poder subir los cambios solo cuando alcancen un nivel apropiado de calidad. La única desventaja significativa es el espacio en disco que exige el almacenamiento de todo el proyecto con su histórico de cambios, que va creciendo con el paso del tiempo.
Git es un DVCS centrado en el control y gestión de código fuente. Nos permite crear desarrollos paralelos que no afecten al original. Podemos regresar a una versión anterior de alguno de los archivos de código fuente, o bien a una de todo el proyecto. El proyecto, con sus archivos asociados e histórico de cambios, se denomina "repositorio" (repository). Esta capacidad resulta particularmente útil en proyectos de programación a gran escala, en los que es posible optar por una dirección para el desarrollo que, finalmente, acabe por resultar infructuosa. También es importante la facilidad para el desarrollo en paralelo cuando se tiene a varias personas trabajando en el mismo proyecto.
Git está escrito en C y, aunque se creó para cubrir la necesidad de control de versiones en el desarrollo del núcleo de Linux, se utiliza ampliamente en otros proyectos de código abierto como Eclipse o Android.
La manera más fácil de entender el manejo de Git es usarlo. Por tanto, hemos estructurado la sección siguiente en forma de guía paso a paso. Si todavía no lo tiene, Git se instala con facilidad mediante el comando sudo apt install git, así que no debería tener problemas para seguir los pasos directamente en el terminal. Este libro emplea GitHub como repositorio remoto de los ejemplos de código fuente. Salvo realizar commit de código fuente en el servidor, el lector podrá hacer todo lo expuesto en esta guía sin crear una cuenta en GitHub. No obstante, GitHub permite crear cuentas de repositorio públicas de forma gratuita, pero si queremos un repositorio privado, por ejemplo para desarrollos que deban salvaguardar derechos de propiedad intelectual, tendremos que pagar una cuota.
NOTA Si el lector planea embarcarse en el desarrollo de un proyecto grande y prefiere que no esté públicamente disponible en www.github.com ni pagar cuota de suscripción alguna, es posible hospedar repositorios privados de pequeña escala en sitios como bitbucket.org y gitlab.com. Con un poco más de trabajo, hasta es posible configurar GitLab en nuestro propio servidor, ya que existe una versión de código abierto de la plataforma.
Una introducción práctica
Para esta guía, hemos creado un repositorio llamado "test" en GitHub. En principio solo contiene un archivo, README.md, con una descripción breve del proyecto "test".
Como podemos ver en la figura 3-4, casi todas las operaciones son locales. Git realiza una suma de verificación en cada archivo antes de almacenarlo. Suma que garantiza que Git detecte cualquier modificación realizada fuera del propio repositorio Git, incluidas las que pudieran derivarse de la corrupción del sistema. Git emplea códigos hash de 40 caracteres para las sumas de verificación. De este modo, es capaz de registrar los cambios entre repositorios locales y remotos, lo que, a su vez, hace posible toda la funcionalidad de operaciones locales.
Figura 3-4: El flujo de trabajo básico de Git.
Cómo clonar un repositorio (git clone)
Clonar un repositorio supone realizar una copia de todos los archivos que contenga el proyecto, junto con el histórico de cambios completo, y guardarla en nuestro disco duro. Haremos esta operación una sola vez. Para clonar el repositorio envíe el comando git clone seguido del nombre completo del repositorio:
pi@erpi / $ cd ~/
pi@erpi ~ $ git clone https://github.com/derekmolloy/test.git
Cloning into 'test'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 14 (delta 1), reused 0 (delta 0), pack-reused 9
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
Ahora tenemos una copia completa del repositorio "test" en el directorio /test. Nuestro repositorio es igual de completo que la versión en el servidor de GitHub. Si fuera necesario, este repositorio podría quedar disponible a través de una red, en un sistema de archivos o en otra cuenta de GitHub y serviría perfectamente como versión principal del repositorio. Aunque no sea necesario contar con un servidor central, generalmente lo hay, ya que de este modo múltiples usuarios pueden insertar (check in) código fuente en un repositorio maestro conocido. El repositorio se crea en el directorio /test y en este momento contiene lo siguiente:
pi@erpi ~/test $ ls -al
total 20
drwxr-xr-x 3 pi pi 4096 Jun 20 22:00 .
drwxr-xr-x 6 pi pi 4096 Jun 20 22:00 ..
drwxr-xr-x 8 pi pi 4096 Jun 20 22:00 .git
-rw-r--r-- 1 pi pi 59 Jun 20 22:00 README.md
Podemos ver el archivo README.md que se creó cuando el proyecto se inicializó en GitHub. Podemos usar more para ver el contenido del mismo. El directorio contiene un subdirectorio .git oculto, con los siguientes archivos y directorios:
pi@erpi ~/test/.git $ ls
branches description hooks info objects refs
config HEAD index logs packed-refs
El directorio oculto .git contiene toda la información acerca del repositorio, como mensajes de commit, archivos de log y los objetos de datos. Por ejemplo, la ubicación del repositorio remoto se encuentra en el archivo config.
pi@erpi ~/test/.git $ more config | grep url
url = https://github.com/derekmolloy/test.git
La sección "Otras lecturas", al final del capítulo, incluye una referencia a un magnífico libro sobre Git que está disponible en la red de forma gratuita. En él se describe con detalle la estructura del directorio .git. Por fortuna, ahora no debemos introducir ningún cambio en el directorio .git, puesto que tenemos comandos Git que lo harán por nosotros.
NOTA Esta guía paso a paso utiliza el propio repositorio "test" del autor. No obstante, no le resultará difícil al lector crear su propio repositorio en GitHub. Después de configurar una cuenta gratuita en GitHub, diríjase a "Create New" (crear nuevo) y, luego, "New repository" (nuevo repositorio). Dé un nombre y una descripción al repositorio y hágalo disponible públicamente, seleccione la opción para inicializarlo con un README y, luego, seleccione "Create Repository" (crear repositorio). A partir de aquí podrá seguir estas instrucciones con el repositorio de su propia cuenta, y, en consecuencia, podrá comunicarse desde su RPi con su propio repositorio en GitHub.
Cómo obtener información de estado (git status)
Ahora que contamos con nuestro repositorio, el paso siguiente es añadir un nuevo archivo de texto al directorio de trabajo, donde estará en estado untracked (sin seguimiento). Cuando ejecutamos el comando git status, observamos un mensaje que indica que hay untracked files, es decir, archivos sin seguimiento:
pi@erpi ~/test $ echo "Just some text" > newfile.txt
pi@erpi ~/test $ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
newfile.txt
nothing to commit, untracked files present (use "git add" to track)
El paso siguiente consiste en añadir cualquier archivo sin seguimiento (untracked) al área de preparación (staging area). No obstante, si no desea añadir un conjunto de archivos, también puede crear uno llamado gitignore para obviarlos. Por ejemplo, esto resultaría útil si en un proyecto C/C++ decidimos que no queremos archivos objeto, .o, intermedios. He aquí un ejemplo de cómo crear un archivo .gitignore destinado a ignorar archivos objeto, .o, de C/C++:
pi@erpi ~/test $ echo "*.o" > .gitignore
pi@erpi ~/test $ more .gitignore
*.o
pi@erpi ~/test $ touch testobject.o
pi@erpi ~/test $ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
newfile.txt
nothing to commit, untracked files present (use "git add" to track)
En este caso, hay dos archivos sin seguimiento, pero no existe mención al archivo testobject.o, lo que indica que se está ignorando tal como queríamos. Observe que el archivo .gitignore forma parte también del repositorio y que se conservará en cualquier clonación del mismo, junto con su histórico de cambios, etc.
Cómo añadir archivos al área de preparación (git add)
Los archivos del directorio de trabajo se pueden añadir ahora al área de preparación con el comando git add . (comando que incluye todos los archivos en el directorio de trabajo, con la excepción de los archivos ignorados). En este ejemplo, dos archivos se agregan desde el directorio de trabajo al área de preparación. Luego, podemos mostrar el estado del repositorio del siguiente modo:
pi@erpi ~/test $ git add .
pi@erpi ~/test $ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitignore
new file: newfile.txt
Para eliminar (remove) un archivo del área de preparación, usamos git rm somefile.ext.
Cómo hacer commit en el repositorio local (git commit)
Después de añadir archivos al área de preparación, podemos hacer commit y convertir en definitivos los cambios desde aquella en el repositorio Git. Primero, podríamos querer añadir nuestro nombre y dirección de correo electrónico para indicar quién realiza el commit con los cambios.
pi@erpi ~/test $ git config --global user.name "Derek Molloy"
pi@erpi ~/test $ git config --global user.email derek@my.email.com
Estos valores se configuran a partir de los de nuestra cuenta de usuario Linux, de manera que persisten en el siguiente inicio de sesión. Para verlos, podemos escribir more ~/.gitconfig.
El commit permanente de los cambios de los archivos en el repositorio Git local se hace con el comando git commit:
pi@erpi ~/test $ git commit -m "Testing the repository"
[master 3eea9a2] Testing the repository
2 files changed, 2 insertions(+)
create mode 100644 .gitignore
create mode 100644 newfile.txt
Los cambios se marcan con el nombre de usuario, pero también requieren la inclusión de un mensaje. Si queremos detallar el mensaje en la línea, usamos -m para establecerlo.
NOTA El atajo git commit -a realiza un commit de los archivos modificados directamente en el repositorio local, sin necesidad de invocar add. No añade archivos nuevos. Observe la figura 3-4 en este mismo capítulo.
Envío al repositorio remoto (git push)
Para realizar este paso, debemos poseer una cuenta propia en GitHub. El comando git push envía cualquier cambio en el código al repositorio remoto. Para que tales cambios se apliquen, debemos estar registrados como usuarios que pueden, en efecto, realizar modificaciones. En Git 2.0 se ha implementado un nuevo método llamado simple, más conservador, para enviar a repositorios remotos. Está seleccionado de forma predeterminada, pero puede suprimir algún mensaje de advertencia, y el envío se puede realizar del siguiente modo (sustituya los detalles de usuario y repositorio por los suyos propios):
pi@erpi ~/test $ git config --global push.default simple
pi@erpi ~/test $ git push
Username for 'https://github.com': derekmolloy
Password for 'https://derekmolloy@github.com': mySuperSecretPassword
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 350 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 0 (delta 0)
To https://github.com/derekmolloy/test.git
f5c45f4..3eea9a2 master -> master
Después de que el código haya sido enviado (push) al repositorio remoto, podemos traer (pull) los cambios al repositorio local de cualquier máquina gracias al comando git pull desde el interior del directorio del repositorio local:
pi@erpi ~/test $ git pull
Already up-to-date.
En este caso, todo está actualizado.
Ramas de desarrollo de Git
Git contempla el concepto de ramas de desarrollo (branching), lo que nos permite trabajar en múltiples versiones diferentes del conjunto de archivos de nuestro proyecto. Por ejemplo, para desarrollar una nueva característica en nuestro proyecto (versión 2) pero conservar el código de la versión actual (versión 1), crearíamos una nueva rama (versión 2). Ninguna de las nuevas características o cambios introducidos en la versión 2 afectará al código de la versión 1. Después podremos pasar de una rama a otra con facilidad.
Cómo crear una rama de desarrollo (git branch)
Suponga, por ejemplo, que desea crear una nueva rama llamada mybranch (acción denominada "ramificar"). Puede hacerlo con el comando git branch mybranch y, luego, pasar a trabajar con ella usando git checkout mybranch del siguiente modo:
pi@erpi ~/test $ git branch mybranch
pi@erpi ~/test $ git checkout mybranch
Switched to branch 'mybranch'
Ahora, para demostrar el funcionamiento de todo esto, suponga que un archivo temporal llamado testmybranch.txt se añade al repositorio. Este podría ser un nuevo archivo de código para nuestro proyecto. Puede observar que el estado de la rama de desarrollo deja claro que el directorio de trabajo contiene un archivo sin seguimiento:
pi@erpi ~/test $ touch testmybranch.txt
pi@erpi ~/test $ ls
newfile.txt README.md testmybranch.txt testobject.o
pi@erpi ~/test $ git status
On branch mybranch
Untracked files:
(use "git add <file>..." to include in what will be committed)
testmybranch.txt
nothing to commit, untracked files present (use "git add" to track)
Puede añadir entonces este nuevo archivo al área de preparación de la rama usando los mismos comandos:
pi@erpi ~/test $ git add .
pi@erpi ~/test $ git status
On branch mybranch
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: testmybranch.txt
Podemos hacer commit de este cambio en la rama mybranch del repositorio local. Este cambio afectará a la rama mybranch, pero no tendrá efecto sobre la rama maestra:
pi@erpi ~/test $ git commit -m "Test commit to mybranch"
[mybranch d4cabf3] Test commit to mybranch
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 testmybranch.txt
pi@erpi ~/test $ git status
On branch mybranch
nothing to commit, working directory clean
pi@erpi ~/test $ ls
newfile.txt README.md testmybranch.txt testobject.o
De la salida anterior puede apreciar que el archivo testmybranch.txt se encuentra en el repositorio local y podemos verlo en el directorio.
Si ahora pasa de la rama mybranch a la rama maestra mediante git checkout master, observará algo interesante al solicitar un listado del directorio:
pi@erpi ~/test $ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
pi@erpi ~/test $ ls
newfile.txt README.md testobject.o
Así es, el archivo testmybranch.txt ha desaparecido del directorio. Aún existe, obviamente, pero se encuentra en forma de objeto blob (de BLOB, Binary Large OBject, objeto binario grande), dentro del directorio git/objects. Si regresa a la rama y lista el directorio, verá esto:
pi@erpi ~/test $ git checkout mybranch
Switched to branch 'mybranch'
pi@erpi ~/test $ ls
newfile.txt README.md testmybranch.txt testobject.o
Es decir, el archivo vuelve a aparecer. Por lo tanto, puede observar lo bien integrado que está el sistema de ramas de desarrollo. En este punto puede regresar a la rama maestra e introducir cambios en el código original y ver que los cambios introducidos en la rama mybranch no afectan al código maestro. Aun si cambiamos el código en el mismo archivo, carecerá de efectos sobre el código original de la rama maestra.
Cómo fusionar ramas de desarrollo (git merge)
¿Qué ocurre si deseamos aplicar los cambios introducidos en la rama de desarrollo mybranch a la versión maestra del proyecto? Es decir, ¿cómo fusionamos las dos ramas? Pues utilizaremos el comando git merge.
pi@erpi ~/test $ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
pi@erpi ~/test $ git merge mybranch
Updating 3eea9a2..d4cabf3
Fast-forward
testmybranch.txt | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 testmybranch.txt
pi@erpi ~/test $ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working directory clean
pi@erpi ~/test $ ls
newfile.txt README.md testmybranch.txt testobject.o
Ahora el archivo testmybranch.txt está en la rama maestra y también se han aplicado el resto de cambios introducidos en cualquier otro documento de la misma. El repositorio local va ahora un commit por delante del remoto, y podemos utilizar git push para actualizar este último.
Cómo borrar una rama de desarrollo (git branch -d)
Para borrar una rama de desarrollo, use el comando git branch -d mybranch:
pi@erpi ~/test $ git branch -d mybranch
Deleted branch mybranch (was d4cabf3).
pi@erpi ~/test $ ls
newfile.txt README.md testmybranch.txt testobject.o
En este caso, la rama testmybranch.txt sigue presente en el proyecto maestro, como por otro lado deber ser, puesto que se ha fusionado con él. Si la rama se hubiera borrado antes de realizar la fusión, se habría perdido el archivo.
Comandos habituales de Git
La table 3-8 ofrece un resumen de los principales comandos de Git. Llegados a este punto, ya hemos visto el uso básico de Git. Cuando se desarrolla código directamente en el RPi, Git puede resultar muy útil, puesto que nos permite subir fácilmente nuestros desarrollos a un repositorio remoto. Esta funcionalidad sirve muy bien para realizar copias de respaldo de nuestro código, así como para volver a desplegar el proyecto en múltiples RPi.
Table 3-8: Resumen de los principales comandos Git.
Operación | Descripción | Operación | Descripción |
git clone | Realiza la clonación desde el repositorio remoto. | git rm | Elimina un archivo o directorio del área de preparación. |
git init | Crea un repositorio completamente nuevo. | git mv | Mueve o renombra un archivo o directorio en el área de preparación. |
git pull | Fusiona cambios desde un repositorio maestro. | git log | Muestra un log (registro) con los commits. El histórico del proyecto. |
git fetch | Encuentra los cambios en el repositorio maestro sin realizar la fusión. | git tag | Da nombre a un commit (por ejemplo: versión 2). |
git status | Muestra el estado del proyecto. | git merge [name] | Fusiona la rama. |
git add | Añade un nuevo archivo o edita un archivo existente. | git show | Obtiene detalles sobre el commit actual o sobre cualquier otro. |
git diff | Muestra las diferencias que pasarán al repositorio cuando se haga el commit. | git branch [name] | Crea una nueva rama de desarrollo. (Use el parámetro -d para borrar (delete)). |
git commit | Realiza commit en el reposi-torio. | git checkout [name] | Cambia a otra rama de desarrollo. |
git push | Envía los cambios desde un repositorio local a uno remoto. |
Cómo utilizar escritorios virtuales
El RPi es perfectamente capaz de actuar como plataforma de computación general, pero si planeamos compilar un núcleo de Linux o realizar desarrollo multiplataforma cruzado (véase el capítulo 7) lo más recomendable es utilizar un ordenador personal con Linux. Es posible utilizar un ordenador con un gestor de arranque que nos permita un sistema dual. Sin embargo, si mayoritariamente utilizamos un PC o un Mac, podríamos decantarnos por utilizar escritorios o máquinas virtuales.
La virtualización de escritorio, o creación de máquinas virtuales, como también se conoce esta tecnología, permite que un solo ordenador ejecute varios sistemas operativos al mismo tiempo. Emplea tecnología de hipervisores (monitores de máquinas o escritorios virtuales) que incluyen elementos hardware, firmware y software para crear y ejecutar entornos de emulación software conocidos como virtual machines, máquinas virtuales o VM, por sus siglas en inglés. Si queremos ejecutar múltiples sistemas operativos en un solo PC, las máquinas virtuales ofrecen una alternativa a la configuración de un gestor de arranque con múltiples sistemas operativos.
En un entorno de virtualización suele haber dos o más SO en ejecución. El sistema operativo host es el que se ejecuta en la máquina de forma nativa y se instala el primero. El software hipervisor se utiliza después para crear una instancia de otro SO, guest o invitado, dentro de una VM. La figura 3-5 muestra una máquina Windows 8.1 (host) ejecutando una máquina virtual (guest) Linux Debian Jessie de 64 bits en una ventana. La VM Debian muestra la interfaz gráfica de usuario Cairo-Dock.
Figura 3-5: VirtualBox ejecutando Debian (Jessie) como máquina virtual en un host Windows.
Existen muchos productos de virtualización, pero la mayoría son muy costosos, tienen licencias propietarias y limitan el tipo de máquinas físicas y virtuales con las que pueden trabajar. Dos de los productos de virtualización de escritorio más populares en Linux son VMware Player y VirtualBox. VMware Player (www.vmware.com/products/player/) es gratuito para uso personal. VirtualBox (www.virtualbox.org) está disponible bajo licencia GNU GPLv2, pero algunas de sus características están disponibles gratis bajo licencia propietaria.
Ambos productos emplean hipervisores tipo 2 (hosted hypervisors) para la virtualización. Esto quiere decir que se ejecutan dentro de un SO estándar y permiten usar ambas máquinas, la física y la virtual, de forma simultánea. VirtualBox está disponible para Windows, Mac OS X y Linux, y puede virtualizar máquinas Windows, Linux y Mac OS X. En la actualidad, VMware Player no está disponible para instalaciones host en Mac OS X. En su lugar debemos adquirir un producto llamado VMware Fusion.
Ambos productos son muy potentes y sería difícil distinguir entre ellos. Sin embargo, VirtualBox funciona bajo licencia GPL e incluye una funcionalidad muy interesante llamada snapshot o instantánea (tenga en cuenta que en la documentación del programa aparece el término inglés). La interfaz de usuario hace posible capturar una instantánea de la VM y guardarla para su uso posterior. Por ejemplo, podríamos capturar una instantánea antes de introducir cambios significativos en la configuración del SO de la VM, lo que nos permitiría retornar a dicha configuración en caso de problemas. La instantánea almacena todos los ajustes de configuración de la VM, los cambios en el contenido de los discos virtuales y el estado de la memoria de la máquina en el momento de realizar la captura. Por lo tanto, cuando restauramos una instantánea, la VM continúa ejecutándose exactamente en el mismo punto en el que se capturó la instantánea.
Si instalamos el software VirtualBox Guest Additions, podremos copiar y pegar texto entre el SO de la VM y el de nuestro propio ordenador, compartir directorios e incluso redimensionar dinámicamente la ventana del hipervisor. La página web de este capítulo (www.exploringrpi.com/chapter3/) ofrece consejos sobre cómo instalar Linux en una VM de un ordenador con Windows.
NOTA Todos los paquetes y software Linux de este libro se han compilado y probado en una distribución estándar Debian de 64 bits instalada en una máquina virtual de VirtualBox.
El código fuente de este libro
Ahora que el lector tiene configurada y funcionando la instalación Linux en VirtualBox, o ha optado por una instalación completa de Linux en un ordenador, podrá descargar todo el código fuente, los scripts y la documentación empleada en el libro abriendo una ventana de terminal y escribiendo lo siguiente (en el ordenador y en el RPi):
pi@erpi ~ $ sudo apt install git
pi@erpi ~ $ git clone https://github.com/derekmolloy/exploringRPi.git
Cloning into 'exploringRPi'...
Si desea descargar el código desde Windows o Mac OS X, existe una interfaz gráfica para trabajar con repositorios GitHub, que está disponible en las direcciones windows.github.com y mac.github.com.
NOTA Si el lector tiene su propia cuenta en GitHub, puede utilizar su interfaz web para copiar (fork) este repositorio en ella. También puede consultar el repositorio de forma regular para observar si hay actualizaciones y cambios. Las cuentas GitHub sin repositorios privados son gratuitas en la actualidad. Asimismo, estudiantes y profesores pueden solicitar una cuenta Micro gratuita, que les da derecho a mantener privados hasta cinco repositorios durante dos años.
Resumen
Después de leer este capítulo debería ser capaz de hacer lo siguiente:
❏Describir los conceptos básicos de un sistema Linux empotrado.
❏Describir cómo un dispositivo Linux empotrado, por ejemplo el RPi, se inicia y arranca su SO Linux.
❏Describir conceptos Linux importantes, como espacio del núcleo (kernel space), espacio de usuario (user space) y la inicialización del sistema utilizando systemd.
❏Realizar tareas de administración de sistema de Linux en el RPi.
❏Utilizar con eficiencia el sistema de archivos del RPi.
❏Emplear comandos de Linux para gestionar archivos y procesos.
❏Gestionar sus propios proyectos de desarrollo de software mediante Git.
❏Instalar una distribución Linux en su ordenador creando una máquina virtual con una herramienta como VirtualBox.
❏Descargar el código fuente de este libro usando Git.
Otras lecturas
Los textos siguientes pueden ayudarnos a aprender más acerca de Linux empotrado, la administración de sistemas Linux, Git y las tecnologías de virtualización:
❏Embedded Linux Primer: A Practical Real-World Approach, Second Edition (Upper Saddle River, NJ: Prentice Hall, 2011), de Christopher Hallinan.
❏The Debian Policy Manual ("Manual de normas de Debian"): tiny.cc/erpi303.
❏Para aprender más acerca de Git, comience consultando el manual del propio sistema; para ello, escriba man gittutorial en la línea de comandos. Si después necesita más información, puede leer la magnífica guía de referencia Pro Git, de Scott Chacon, en el sitio tiny.cc/erpi304 (también está disponible en tapa blanda en Nueva York: Apress Media, 2009).
Bibliografía
❏ARM Holdings (11 de febrero de 2015): "ARM Holdings PLC Reports Results for the Fourth Quarter and Full Year 2014". Descargado el 14 de junio de 2015 desde www.arm.com/about/newsroom/arm-holdings-plc-reports-results-for-the-fourth-quarter-and-full-year-2014.php
❏McCracken, J.; Sherman, A., y King, I. (27 de mayo de 2015): "Avago to Buy Broadcom for $37 Billion in Biggest Tech Deal Ever". Bloomberg Business. Descargado el 14 de junio de 2015 desde www.bloomberg.com/news/articles/2015-05-27/avago-said-near-deal-to-buy-wireless-chipmaker-broadcom
❏Git FAQ (9 de marzo de 2013). Descargado el 22 de febrero de 2014 desde Git Wiki: git.wiki.kernel.org/index.php/GitFaq#Why_the_.27git.27_name.3F
❏Smith, B. (29 de julio de 2013). A Quick Guide to GPLv3. Descargado el 14 de junio de 2015 desde www.gnu.org/licenses/quick-guide-gplv3.html
Continúa