Logo Studenta

TFM-1056-CEA

¡Este material tiene más páginas!

Vista previa del material en texto

Proyecto Fin de Carrera
Ingeniería de Telecomunicación
Formato de Publicación de la Escuela Técnica
Superior de Ingeniería
Autor: F. Javier Payán Somet
Tutor: Juan José Murillo Fuentes
Dep. Teoría de la Señal y Comunicaciones
Escuela Técnica Superior de Ingeniería
Universidad de Sevilla
Sevilla, 2013
Trabajo Fin de Master
Master Universitario en Ingeniería Aeronáutica
Creación Sistemas Operativos embebidos
personalizados basados en Linux para SoC
con FPGA integrada
Autor: Ángel Cea Fons
Tutor: María de los Ángeles Martín Prats
Dep. Ingeniería Electrónica
Escuela Técnica Superior de Ingeniería
Universidad de Sevilla
Sevilla, 2017
Trabajo Fin de Master
Master Universitario en Ingeniería Aeronáutica
Creación Sistemas Operativos embebidos
personalizados basados en Linux para SoC con
FPGA integrada
Autor:
Ángel Cea Fons
Tutor:
María de los Ángeles Martín Prats
Profesor Titular
Dep. Ingeniería Electrónica
Escuela Técnica Superior de Ingeniería
Universidad de Sevilla
Sevilla, 2017
Trabajo Fin de Master: Creación Sistemas Operativos embebidos personalizados basados
en Linux para SoC con FPGA integrada
Autor: Ángel Cea Fons
Tutor: María de los Ángeles Martín Prats
El tribunal nombrado para juzgar el trabajo arriba indicado, compuesto por los siguientes profesores:
Presidente:
Vocal/es:
Secretario:
acuerdan otorgarle la calificación de:
El Secretario del Tribunal
Fecha:
Resumen
En el mundo de la ingeniería la electrónica está cobrando cada vez una mayor importancia debido,entre otros motivos, a la creciente automatización de procesos, el crecimiento en número y
complejidad de los sistemas de aviónica embarcados en las aeronaves y la marcada tendencia hacia
el All Electric Aircraft, que requiere la sustitución de sistemas mecánicos por sistemas electrónicos.
Aunque suele pasar desapercibido, los sistemas embebidos tienen una importancia capital en el
mundo de la aviación, controlando y supervisando procesos de una manera eficiente. El problema
de estos sistemas, por lo general, subyace en su limitada potencia de procesamiento. Aquí es
donde entra en juego lo que se va a tratar en el presente documento, el cual contendrá información
acerca del desarrollo de una distribución de un sistema operativo (Linux) personalizada con Yocto
Project, que permita satisfacer las necesidades pertinentes de forma eficiente, lo que nos permite no
desaprovechar potencia de cálculo y además personalizar las herramientas y utilidades de las que se
quiere dotar al sistema embebido.
I
Índice
Resumen I
Comandos VII
1 Introducción 1
1.1 Sistemas embebidos 1
1.1.1 ZedBoard 3
1.2 Linux embebido 4
1.2.1 Código abierto (Open source) 4
1.2.2 Licencias Open source 5
1.2.3 Los cuatro elementos de Linux embebido 5
Toolchain 5
2 Toolchain 7
2.1 Tipos de Toolchain 7
2.2 Elección de la librería de C 7
2.3 Crosstool-NG 8
2.3.1 Instalación de crosstool-NG 8
2.3.2 Anatomía de una toolchain 12
2.3.3 El directorio sysroot, la librería y los archivos de cabecera 14
2.3.4 Librerías, componentes de la librería de C 14
3 Bootloader 19
3.1 ¿Cuál es la función del bootloader? 19
3.2 La secuencia de arranque 19
3.2.1 Fase 1: código ROM 19
3.2.2 Fase 2: SPL 20
3.2.3 Fase 3: TPL 20
3.3 Del bootloader al kernel 21
3.4 Device trees 21
3.4.1 La propiedad reg 23
3.4.2 Phandles e interrupciones 24
3.4.3 Inclusión de archivos en los árboles de dispositivos 24
3.4.4 Elección de un bootloader 26
4 El kernel 29
4.1 ¿Qué es el kernel? 29
III
IV Índice
4.2 Elección del kernel 29
4.3 Configuración del kernel 32
4.4 Módulos kernel 33
4.5 Compilando 33
4.5.1 Compilando la imagen del kernel 33
4.5.2 Compilando arboles de dispositivos 36
4.5.3 Compilando módulos kernel 36
4.5.4 Limpiando fuentes del kernel 36
5 El sistema de archivos raiz 39
5.1 Contenido del sistema de archivos raiz 39
5.2 Estructura del directorio 40
5.3 Permisos de acceso POSIX a archivos 40
5.4 Programas para el sistema de archivos raíz 41
5.4.1 El programa init 41
5.4.2 Shell 42
5.4.3 Utilidades 42
5.4.4 Busybox 43
5.4.5 Construyendo Busybox 43
5.4.6 Librerías para el sistema de archivos raiz 45
5.4.7 Nodos de dispositivos 45
5.4.8 El sistema de archivos proc y sys/ 45
5.4.9 Módulos kernel 47
5.5 Transfiriendo el sistema de archivos raíz al dispositivo de destino 47
6 Yocto Project 49
6.1 ¿Qué es Yocto Project? 49
6.2 Instalación de Yocto Project 49
6.3 Configuaración de Yocto Project 51
6.4 Capas 53
6.4.1 Creación de una nueva capa 54
6.5 Modulos kernel 60
6.5.1 Compilando modulos kernel con Yocto Project 61
6.5.2 Drivers 66
6.5.3 Creando un character device driver 70
7 Construyendo con Yocto Poject 81
7.1 Construyendo para la ZedBoard 81
7.1.1 Antes de intentar realizar la construcción 81
7.1.2 Construyendo 83
7.1.3 Creando una capa para la ZedBoard 87
7.1.4 Despliegue en la zedboard 90
7.1.5 Prueba del sistema con QEMU 92
7.2 Construyendo para la enclustra 93
8 Conclusiones 97
9 Futuras líneas de investigación 99
Índice V
Índice de Figuras 101
Índice de Tablas 103
Índice de Códigos 105
Bibliografía 109
Comandos
En esta lista se incluirán comandos utilizados en el documento para facilitar la lectura a personas
que no estén familiarizadas con la terminal de Linux, pero este apartado no se dedicará a explicar
con detalle y detenimiento ni el funcionamiento ni la sintaxis de los comandos, para ello buscar
información en fuentes externas a este documento.
sudo Colocado delante de otros comandos
permite ejecutarlos como superusua-
rio
tar Comando que sirve pare realizar ta-
reas de compresión y descompresión
cd <destino> Realiza el cambio al directorio que
se le indique a continuación (change
directory)
pwd > Muestra la ubicación del directorio
actual (Print Working Directory)
make <opciones> Permite ejecutar archivomakefile en
el directorio actual
mv <opciones> <origen> <destino> Mueve y renombra ficheros o direc-
torios entre otras funciones
rm <opciones> <archivo1> <archivo2> Permite eliminar directorios y archi-
vos dependiendo de las opciones que
se le indiquen
cp <opciones> <origen> <destino> Copia directorios y archivos origen
en el destino atendiendo a las opcio-
nes que se le indiquen
cat <opciones> <archivo> Lee un archivo y muestra su conteni-
do
tail <opciones> <archivo> Muestra las últimas líneas de un ar-
chivo
ls <opciones> <destino> Muestra un listado de directorios y
archivos dentro del directorio <des-
tino>, y si no se le indica destino el
listado lo hace del directorio actual
lsmod Muestra un listado de los módulos
kernel instalados actualmente en el
sistema
insmod <módulo kernel> Instala módulo kernel en el sistema
VII
VIII Comandos
rmmod <módulo kernel> Desinstala módulo kernel del siste-
ma
df Muestra los sistemas de archivos
montados, su espacio disponible y
el espacio utilizado
dmesg Muestra mensajes del kernel
git clone <dirección> Realiza una clonación del reposito-
rio que se encuentre en la dirección
indicada
git checkout <rama> Cambia la rama a la que apunta el re-
positorio a la indicada en el comando
git branch Muestra la rama a la que apunta ac-
tualmente el repositorio
git branch -r Muestra las ramas disponibles
mknod Crea nodo de dispositivo. Es necesa-
rio tener permisos de superusuario
mount Para montar directorios y archivos.
Es necesario tener permisos de su-
perusuario
umount Para desmontar directorios y archi-
vos. Es necesario tener permisos de
superusuario
sudo apt-get install Realiza la instalación de la aplica-
ción indicada a continuación
./<aplicación> Ejecuta la aplicación que se le indi-
que
source Ejecuta la aplicación que se le in-
dique a continuación guardando los
cambios realizados en elas variables
de entorno
tree <opciones> <dirección> Muestra estructura de directorios y
ficheros en forma de árbol de la di-
rección que se le indique
<comando> | grep <caracteres> Toma como entrada la salida del co-
mando anterior y muestra los resul-
tados que contienen los caracteres
indicados
fdisk <opciones> Permite formatear particiones y obte-ner información acerca de dispositi-
vos del almacenamiento conectados
al sistema
find <opciones> Comando que, como su propio nom-
bre indica, sirve para realizar búsque-
das de distinta índole dependiendo
de las opciones que se añadan al co-
mando
1 Introducción
En este capítulo se aclarará la definición de sistema embebido y se mostrarán los componentesque habitualmente forman parte de los sistemas embebidos. Se introducirá a la ZedBoard
como el sistema embebido que se utilizará para el desarrollo del presente proyecto y se hablará
sobre lo que es Linux embebido y las partes que lo conforman.
1.1 Sistemas embebidos
Un sistema embebido o empotrado (integrado, incrustado) es un sistema de computación diseñado
para realizar una o algunas pocas funciones dedicadas, frecuentemente en un sistema de computación
en tiempo real. Al contrario de lo que ocurre con los ordenadores de propósito general (como por
ejemplo una computadora personal o PC) que están diseñados para cubrir un amplio rango de
necesidades, los sistemas embebidos se diseñan para cubrir necesidades específicas. En un sistema
embebido la mayoría de los componentes se encuentran incluidos en la placa base (tarjeta de vídeo,
audio, módem, etc.) y muchas veces los dispositivos resultantes no tienen el aspecto de lo que se
suele asociar a una computadora. Algunos ejemplos de sistemas embebidos podrían ser dispositivos
como un taxímetro, un sistema de control de acceso, la electrónica que controla una máquina
expendedora o el sistema de control de una fotocopiadora entre otras múltiples aplicaciones.
Como ya se ha nombrado anteriormente, estos sistemas suelen disponer de una potencia de
cálculo limitada y pueden llevar a cabo generalmente también una variedad de tareas limitadas
en comparación a un pc habitual. Debido a esto cobra mucho sentido desarrollar una distribución
de un sistema operativo que esté optimizado en cuanto a consumo de recursos y solo incluya las
funcionalidades que se vayan a utilizar, ahorrando así espacio de almacenamiento requerido y
complejidad del sistema operativo.
Por lo general, los Sistemas Embebidos se pueden programar directamente en el lenguaje ensam-
blador del microcontrolador o microprocesador incorporado sobre el mismo, o también, utilizando
los compiladores específicos que utilizan lenguajes como C o C++ y en algunos casos, cuando
el tiempo de respuesta de la aplicación no es un factor crítico, también pueden usarse lenguajes
interpretados como Java.
Las arquitecturas más empleadas en sistemas embebidos contienen en general los siguientes
componentes:
1. Microprocesador: Es el encargado de realizar las operaciones de cálculo principales del
sistema. Ejecuta código para realizar una determinada tarea y dirige el funcionamiento de los
demás elementos que le rodean, a modo de director de una orquesta.
2. Memoria principal: En ella se encuentra almacenado el código de los programas que el
sistema puede ejecutar así como los datos. Su característica principal es que debe tener un
1
2 Capítulo 1. Introducción
acceso de lectura y escritura lo más rápido posible para que el microprocesador no pierda
tiempo en tareas que no son meramente de cálculo. Al ser volátil el sistema requiere de un
soporte donde se almacenen los datos incluso sin disponer de alimentación o energía.
3. Caché: Memoria más rápida que la principal en la que se almacenan los datos y el código
accedido recientemente. Dado que el sistema realiza microtareas, muchas veces repetitivas, la
caché hace ahorrar tiempo, ya que no hará falta ir a memoria principal si el dato o la instrucción
ya se encuentra en la caché. Dado su alto precio tiene un tamaño muy inferior comparado
la memoria principal. En el interior del chip del microprocesador se encuentra una pequeña
caché (L1), pero normalmente se tiene una mayor en otro chip de la placa madre (L2).
4. Disco duro: En él la información no es volátil y además puede conseguir capacidades muy
elevadas. A diferencia de la memoria que es de estado sólido éste solía ser magnético, lo
que hacía inviable a veces su inclusión en sistemas embebidos, debido a su excesivo tamaño.
Otro problema que presentan los dispositivos magnéticos, a la hora de integrarlos en sistemas
embebidos, es que llevan partes mecánicas móviles, lo que los hace inviables para entornos
donde estos estarán expuestos a ciertas condiciones de vibración. Estos problemas han sido
subsanados en los últimos tiempos con la creación de tarjetas SD y micro SD (las cuales
tienen un tamaño muy reducido) con capacidades cada vez mayores, disponiendo en muchos
casos de más capacidad que discos duros convencionales de hace una década.
5. Disco flexible: Su función era la de almacenamiento, pero con discos con capacidades mucho
más pequeñas y la ventaja de su portabilidad. Normalmente se encontraban en computadores
personales estándar pero no así en una PC embebida. En nuestros días llevan varios años en
total desuso en PC comunes.
6. BIOS-ROM: BIOS(Basic Input Output System, sistema básico de entrada y salida) es código
que es necesario para inicializar la computadora y para poner en comunicación los distintos
elementos de la placa madre. La ROM (Read Only Memory, memoria de sólo lectura no
volátil) es un chip donde se encuentra el código BIOS.
7. CMOS-RAM: Es un chip de memoria de lectura y escritura alimentado con una pila donde
se almacena el tipo y ubicación de los dispositivos conectados a la placa madre (disco duro,
puertos de entrada y salida, etc.). Además contiene un reloj en permanente funcionamiento
que ofrece al sistema la fecha y la hora.
8. Chipset: Chip que se encarga de controlar las interrupciones dirigidas al microprocesador, el
acceso directo a memoria (DMA) y al bus ISA, además de ofrecer temporizadores, etc. Es
frecuente encontrar la CMOS-RAM y el reloj de tiempo real en el interior del Chip Set.
9. Entradas al sistema: Pueden existir puertos para ratón, teclado, vídeo en formato digital,
comunicaciones serie o paralelo, etc.
10. Salidas del sistema: Puertos de vídeo para monitor o televisión, pantallas de cristal líquido,
altavoces, comunicaciones serie o paralelo, etc.
11. Ranuras de expansión para tarjetas de tareas específicas: Pueden no venir incorporadas en
la placamadre, como pueden ser más puertos de comunicaciones, acceso a red de computadoras
vía LAN (Local Area Network, red de área local) o vía red telefónica: básica, RDSI (Red
Digital de Servicios Integrados), ADSL (Asynchronous Digital Subscriber Loop, Lazo Digital
Asíncrono del Abonado), Cablemódem, etc. Un PC estándar suele tener muchas más ranuras
de expansión que un sistema embebido. Las ranuras de expansión están asociadas a distintos
tipos de bus: VESA, ISA, PCI, NLX (ISA + PCI), etc.
Finalmente se le va a dedicar un espacio en esta sección a nombrar las ventajas que tienen estos
sistemas embebidos:
1.1 Sistemas embebidos 3
• Posibilidad de utilización de sistemas operativos potentes que ya realizan numerosas tareas:
comunicaciones por redes de datos, soporte gráfico, concurrencia con lanzamiento de threads,
etc. Estos sistemas operativos pueden ser los mismos que para PC compatibles (Linux,
Windows, MS-DOS) con fuertes exigencias en hardware o bien ser una versión reducida de
los mismos con características orientadas a los PC embebidos.
• Al utilizar los Sistemas Embebidos, se pueden encontrar fácilmente herramientas de desarrollo
de software potentes, así como numerosos programadores que las dominan, dada la extensión
mundial de las aplicaciones para computadores compatibles.
• Reducción en el precio de los componentes hardware y software debido a la gran cantidad de
computadores en todo el mundo.
1.1.1 ZedBoard
La Zedboard (Zynq Evaluation Development Board) va a ser la placa que va a ser utilizada en este
proyecto y para la cual se va a desarrollar el sistema operativo.
La ZedBoard es una placa de desarrollo para el Xilinx Zynq-7000 Extensible Processing Plat-
form (EPP). El Xilinx Zynq-7000 es un SoC que combina microprocesadoresde doble núcleo
ARM Cortex-A9, estructura de FPGA y periféricos claves en un solo dispositivo. El procesador
y la estructura del FPGA se comunican con más de 10,000 interconexiones internas, ofreciendo
un rendimiento entre el microprocesador y FPGA que es físicamente imposible de lograr con un
procesador discreto y un FPGA implementado en una tarjeta de circuito impreso.
Figura 1.1 Diagrama del SoC Zynq-7000.
Una FPGA (field-programmable gate array) es un circuito integrado diseñado para ser configu-
rado por un cliente, diseñador o desarrollador. Es decir, es una matriz donde se distribuyen bloques
lógicos que pueden ser programados para que se conecten de distinta manera y se puedan realizar
muy diversas tareas (se puede literalmente modificar las interconexiones entre puertas y bloques
lógicos).
Es decir, con esta placa se tiene la posibilidad de programar las tareas que realizará el procesador
mediante software y la posibilidad de programar hardware para realizar tareas de una forma mucho
más rápida y eficiente de lo que lo haría un procesador. Por ejemplo se podría dejar a la FPGA la
4 Capítulo 1. Introducción
tarea de filtrado de señales de entrada, ya que en lugar de que el filtrado lo realice el procesador
mediante la potencia de cálculo, lo haría la FPGA de forma "física" tras haberse programado las
conexiones físicas que esta utiliza.
El SoC Zynq no es llamado un “FPGA” porque es el procesador el que está a cargo del sistema,
en lugar de la estructura del FPGA. Es decir, el sistema de procesamiento arranca primero y controla
la funcionalidad de la estructura del FPGA. Esto significa que los usuarios no tienen que estar
profundamente familiarizados con técnicas de diseño de FPGA para ejecutar una aplicación en el
subsistema del procesador del SoC Zynq. El SoC Zynq ofrece a los clientes la habilidad para crear
sus diseños en C, C++ o SystemC usando el software de desarrollo de su elección y programar su
diseño en el sistema de procesamiento del SoC Zynq. Si una parte de su diseño no se está ejecutando
lo suficientemente rápido, los diseñadores pueden usar la herramienta Vivado High-Level Synthesis
(HLS) de Xilinx o HANDEL-C de Mentor Graphics para traducir un algoritmo o parte de un
algoritmo que desarrollaron a un nivel de C a VHDL y probar ese código ejecutándose en la sección
de FPGA del SoC Zynq. Al descargar las funciones adecuadas del procesador a la estructura del
FPGA y liberar el procesador para realizar las funciones que hace mejor, los clientes pueden alcanzar
un incremento de 700 veces más rendimiento del sistema en comparación con los diseños basados
en procesadores.
1.2 Linux embebido
Para realizar este proyecto se utilizará Linux, que será el sistema operativo que se instalará en la
ZedBoard. ¿Por qué utilizar este SO en nuestro dispositivo target y no otro?. A continuación se
mostrarán algunos argumentos en favor de Linux:
• Linux es capaz de lidiar con muchas funcionalidades, tanto necesarias en este proyecto como
otras que no lo son (por lo menos en principio). Soporte para distintos tipos de conectividad
(USB, Wi-Fi, Bluetooth etc), para gran variedad de dispositivos de almacenamiento, soporte
para dispositivos multimedia y mucho más.
• Linux es de código abierto, lo que implica que se dispone de la libertad de adquirir código
fuente y modificarlo a nuestro antojo. Es decir, se pueden agregar nuevas funcionalidades
que no estaban en el código fuente y/o eliminar otras que no sean necesarias en el proyecto
para reducir los requisitos de procesamiento y memoria.
• Linux tiene el soporte de una gran comunidad muy activa, lo que garantiza que va a estar
actualizado, soportará las últimas tecnologías y se tendrá acceso a multitud de información
que, entre otras cosas, ahorrará tiempo en la resolución de los problemas que vayan surgiendo
en el desarrollo de este proyecto.
• Las licencias de código abierto de Linux garantizan que se tiene acceso al código fuente.
1.2.1 Código abierto (Open source)
Se va a hablar de manera escueta sobre qué significa que la mayoría de los componentes de Linux
embebido sean Open source y qué tipos de licencia se pueden encontrar en este tipo de código.
Open source tiene varios significados, o más bien, el significado de open source tiene varias
implicaciones. La primera implicación es el hecho de que el código Open source se puede adquirir
gratuitamente y desplegarlo en el sistema que se requiera sin ningún coste, y esto es justo lo
que garantizan las licencias de software de código abierto, pero la más importante es la segunda
implicación. La segunda implicación del significado de Open source es que se tiene total libertad
de adquirir el código fuente y modificarlo sin ningún tipo de restricción.
No confundir código Open source con licencias de shareware que permiten copiar y ejecutar
archivos binarios pero no permiten el acceso al código fuente o de otras licencias que permiten el
1.2 Linux embebido 5
uso del software en ciertas condiciones como por ejemplo que su uso sea personal y no comercial.
Estos dos últimos ejemplos no son Open source.
1.2.2 Licencias Open source
Las licencias Open source se dividen básicamente en dos categorías:
• GPL (General Public License)
• BSD (Berkeley Software Distribution)
La licencia BSD especifica que se puede modificar el código fuente y utilizarlo en cualquier
sistema siempre que no se modifiquen los términos de la licencia. Es decir, cualquier usuario podrá
tomar software bajo esta licencia libremente , sólo respetando la autoría del software, pero pudiendo
decidir si liberar o no los cambios que se hayan realizado. Lo que el desarrollador logra es que su
código sirva para cualquier propósito y, con código abierto o sin él, el siguiente desarrollador pueda
elegir con libertad qué hacer con su propio trabajo.
En cambio la licencia GPL también obliga a respetar la autoría, pero en este caso se debe liberar
el código con la misma licencia. Esto quiere decir que el usuario que utilice software con esta
licencia deberá publicar el resultado de su trabajo y además bajo la misma licencia.
1.2.3 Los cuatro elementos de Linux embebido
Cualquier proyecto de creación y desarrollo de un sistema operativo para ser instalado en un sistema
embebido comienza con la obtención, personalización y el despliegue de estos 4 elementos:
1. Toolchain: Está compuesto del compilador y otras herramientas que se necesitan para crear
el código que se instalará en el dispositivo final.
2. Bootloader: Este elemento es necesario para inicializar la placa y para cargar y "arrancar" el
kernel de Linux.
3. Kernel: Es el elemento central del sistema, gestiona los recursos del sistema y hace de interfaz
con el hardware.
4. Sistema de archivos raíz (root filesystem): Estos son los archivos que contienen información
crítica sobre el sistema como pueden ser librerías y programas que arrancan una vez que el
kernel ha completado su inicialización
Los capítulos dedicados a cada elemento de Linux embebido por separado estarán basados en el
libro Simmonds, C. (2015).Mastering Embedded Linux Programming. Packt Publishing Ltd, por lo
que en él se puede encontrar información similar y ampliada. Es un buen libro para consulta de
muchos de los temas tratados en el presente documento.
A continuación se hará una ampliación breve sobre la toolchain, aunque se ahondará más en
capítulos posteriores.
Toolchain Una toolchain es el conjunto de herramientas que compila código fuente en ejecutables
que se pueden ejecutar en el dispositivo de destino e incluye un compilador, un enlazador (linker) y
bibliotecas de tiempo de ejecución. Inicialmente, se necesita para construir los otros tres elementos
de un sistema Linux incorporado: el bootloader, el kernel y el sistema de archivos raíz. Tiene que
ser capaz de compilar código escrito en ensamblador, C y C ++ ya que estos son los lenguajes
utilizados en los paquetes de código abierto base.
Por lo general, las toolchain para Linux se basan en componentes del proyectoGNU (http://www.gnu.org).El proyecto GNU es un proyecto colaborativo de software libre con el objetivo de crear un sistema
operativo completamente libre: el sistema GNU.
Una toolchain estandar de GNU está compuesto de tres componentes principales:
6 Capítulo 1. Introducción
• Binutils: Un conjunto de utilidades binarias incluyendo el ensamblador, y el linker. Está
disponible en http://www.gnu.org/software/binutils/.
• GNUCompiler Collection (GCC): Estos son los compiladores para C y otros lenguajes que,
dependiendo de la versión de GCC, incluyen C ++, Objective-C, Objective-C ++, Java, Fortran,
Ada y Go. Todos ellos utilizan un back-end común que produce el código ensamblador del
que se alimenta el ensamblador de GNU. Está disponible en http://gcc.gnu.org/.
• Librerías de C: Una API estandarizada basada en la especificación POSIX, que es la interfaz
principal de las aplicaciones con el kernel del sistema operativo.
2 Toolchain
En este capítulo se profundizará sobre la toolchain y su utilización. Se nombrarán los tipos detoolchain, cuál será la mejor elección en este proyecto y se introducirá la utilización de una
toolchain específica para mostrar la manera habitual de trabajar con estas herramientas y poder
tener una idea un poco más profunda de esta parte de Linux embebido.
2.1 Tipos de Toolchain
Las toolchain se pueden dividir en dos tipos:
• Nativa: Este tipo de toolchain se ejecuta en el mismo tipo de sistema que el sistema para el
que se generan los programas. Por poner un ejemplo sencillo, si vamos a utilizar la toolchain
para crear programas que serán ejecutados en el mismo ordenador o un ordenador similar
(misma arquitectura de procesador y similar arquitectura general).
• Cruzada: Este tipo de toolchain se ejecuta en un sistema de distinto tipo del sistema donde
se ejecutarán los programas que se creen con la toolchain.
En el caso de este proyecto tendremos que elegir una toolchain de tipo cruzada, ya que el sistema
donde desarrollaremos el sistema operativo (nuestro sistema host) será un sistema con arquitectura
x86 de 64 bits (un procesador intel más específicamente), mientras que el sistema donde se ejecutarán
los programas que se creen con la toolchain (el target) será la ZedBoard que cuenta con arquitectura
ARM.
Se podría directamente hacer el desarrollo de los programas directamente sobre la Zedboard, con
una toochain de tipo nativa, pero parece más adecuado, no solo por potencia y recursos sino por
comodidad realizar el desarrollo en un ordenador convencional utilizando una toolchain de tipo
cruzada.
2.2 Elección de la librería de C
Las librerías son archivos con rutinas que realizan tareas comunes (y a veces muy complejas),
que ahorran infinidad de trabajo al poder ser llamadas desde otros programas y no teniendo así
que ser programadas cada vez que se necesita de una funcionalidad en concreto. Las bibliotecas
estándares, como puede ser la biblioteca estandar de C (también conocida como libc) es exacta-
mente lo anteriormente comentado, solo que esta estandarizado por laOrganización Internacional
para la Estandarización (ISO) para que de esta manera pueda ser utilizado por gran cantidad de
aplicaciones.
7
8 Capítulo 2. Toolchain
El lenguaje C es el que hace de interfaz entre la aplicación (que está dentro del espacio de usuario)
y el kernel, por tanto será muy importante tener unas librerías de C que permitan la comunicación
entre los programas generados y el kernel. Hay muchas opciones en cuanto a librerías de C se
refiere, a continuación se muestran las principales opciones:
• glibc: Es la librería estándar de C de GNU.
• eglibc: Las siglas significan embedded glibc. Agrega opciones de configuración y soporte
para arquitecturas que no están cubiertas por gilbc.
• ulibc: La "u" de las siglas en realidad es una "µ" haciendo referencia a que es una librería
de C para microcontroladores.
• musl libc: Es una nueva librería de C para sistemas embebidos.
Para este proyecto se ha elegido libc, ya que es la librería estándar y es quizás la opción más
completa de todas las nombradas.
2.3 Crosstool-NG
Hay gran cantidad y variedad de toolchain en el mercado. Por ejemplo se pueden encontrar toolchain
del proveedor del SoC que se haya adquirido, toolchain para Linux creados por terceros como
Mentor Graphics, o un SDK generado por una herramienta integrada para desarrollo embebido
como Yocto Project. Se tiene que evaluar si la una toolchain preconstruida satisface las necesidadesn
del proyecto, y si nó se tendrá que construir una toolchain.
Para mostrar la creación de la toolchain se trabajará con crosstool-NG ya que es una alternativa
sencilla que encapsula el proceso en scripts. Es importante que se mantenga la toolchain una vez
elegida o creada ya que el cambio inconsistente de compiladores y de librerías es una fuente de
errores importante.
2.3.1 Instalación de crosstool-NG
Para instalar los paquetes necesarios para el correcto funcionamiento de crosstool-NG se tendrá que
ejecutar el siguiente comando en la consola:
Código 2.1 Instalación de paquetes para crosstool-NG.
$ sudo apt-get install automake bison chrpath flex g++ git gperf gawk
libexpat1-dev libncurses5-dev libsdl1.2-dev libtool python2.7-dev
texinfo
Tras esto habrá que descargar la última versión de crosstool-NG visitando el enlacehttp://crosstool-
ng.org/download/crosstool-ng. En el momento de escritura del presente documento la última ver-
sión disponible es crosstool-ng-1.23.0. La instalación se realiza con los siguientes comandos, los
cuales se pueden encontrar en la dirección http://crosstool-ng.github.io/docs/install/ .
Código 2.2 Instalación de crosstool-NG.
$ tar xf crosstool-ng-1.23.0.tar.bz2
$ cd crosstool-ng-1.23.0
$ ./configure --enable-local
2.3 Crosstool-NG 9
$ make
$ make install
Como se puede observar en el código 2.2 la primera línea descomprime el archivo descargado, la
segunda línea cambia de directorio al directorio que ha quedado después de descomprimir el archivo.
Al hacer make puede dar un error, en este caso dio el error configure: error: missing required tool:
help2man que se soluciona simplemente insertando el comando $ sudo apt-get install help2man.
Luego el comando $ ./configure –enable-local permite instalar crosstool-NG en en directorio en el
que se encuentra situado el sistema (crosstool-ng-1.23.0 ) y por lo tanto ejecutarlo también en este
mismo directorio. Si ejecutamos el comando $ ./ct-ng se especificarán los distintos comandos que
se pueden utilizar. Se puede ver lo anteriormente comentado en el código 2.3.
Código 2.3 Lista de comandos disponibles para crosstool-NG.
angel@ubuntu:~/Desktop/croostool-ng/crosstool-ng-1.23.0$ ./ct-ng
This is crosstool-NG version crosstool-ng-1.23.0
Copyright (C) 2008 Yann E. MORIN <yann.morin.1998@free.fr>
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
See below for a list of available actions, listed by category:
Configuration actions:
menuconfig - Update current config using a menu based program
nconfig - Update current config using a menu based program
oldconfig - Update current config using a provided .config as
base
extractconfig - Extract to stdout the configuration items from a
build.log file piped to stdin
savedefconfig - Save current config as a mini-defconfig to ${
DEFCONFIG}
defconfig - Update config from a mini-defconfig ${DEFCONFIG}
(default: ${DEFCONFIG}=./defconfig)
saveconfig - Save current config as a preconfigured target
show-tuple - Print the tuple of the currently configured toolchain
Preconfigured toolchains (#: force number of // jobs):
list-samples - prints the list of all samples (for scripting)
show-<sample> - show a brief overview of <sample> (list with list-
samples)
<sample> - preconfigure crosstool-NG with <sample> (list with
list-samples)
build-all[.#] - Build *all* samples (list with list-samples) and
install in
${CT_PREFIX} (set to ~/x-tools by default)
Build actions (#: force number of // jobs):10 Capítulo 2. Toolchain
source - Download sources for currently configured toolchain
build[.#] - Build the currently configured toolchain
list-steps - List all build steps
Clean actions:
clean - Remove generated files
distclean - Remove generated files, configuration and build
directories
Distribution actions:
check-samples - Verify if samples need updates due to Kconfig changes
update-samples - Regenerate sample configurations using the current
Kconfig
wiki-samples - Print a DokuWiki table of samples
updatetools - Update the config tools
Environment variables (see /home/angel/Desktop/croostool-ng/crosstool-ng
-1.23.0/docs/0 - Table of content.txt):
STOP=step - Stop the build just after this step (list with list-
steps)
RESTART=step - Restart the build just before this step (list with
list-steps)
CT_PREFIX=dir - install samples in dir (see action "build-all", above
).
V=0|1|2|<unset> - <unset> show only human-readable messages (default)
0 => do not show commands or human-readable message
1 => show only the commands being executed
2 => show both
Use action "menuconfig" to configure your toolchain
Use action "build" to build your toolchain
Use action "version" to see the version
See "man 1 ct-ng" for some help as well
Si se quiere conocer una lista de ejemplos de toolchain preconfigurada se puede hacer con
el comando $ ./ct-ng list-samples. Ya que la zedboard cuenta con un procesador de 2 nucleos
ARM Cortex A9, del listado de ejemplos de toolchain preconfiguradas la más similar es la opción
arm-cortexa9_neon-linux-gnueabihf. Se puede consultar la configuración de esta opción como se
realiza en el código 2.4.
Código 2.4 Configuración de arm-cortexa9_neon-linux-gnueabihf.
angel@ubuntu:~/Desktop/croostool-ng/crosstool-ng-1.23.0$ ./ct-ng show-
arm-cortexa9_neon-linux-gnueabihf
IN config.gen/arch.in
IN config.gen/kernel.in
IN config.gen/cc.in
IN config.gen/binutils.in
IN config.gen/libc.in
2.3 Crosstool-NG 11
IN config.gen/debug.in
[L.X] arm-cortexa9_neon-linux-gnueabihf
OS : linux-4.10.8
Companion libs : gmp-6.1.2 mpfr-3.1.5 isl-0.18 mpc-1.0.3 expat-2.2.0
ncurses-6.0
binutils : binutils-2.28
C compilers : gcc | linaro-6.3-2017.02
Languages : C,C++
C library : glibc-2.25 (threads: nptl)
Tools : gdb-7.12.1
Y para seleccionar esta configuración como la que se utilizará se ejecutará la siguiente linea de
comando:
Código 2.5 Elección de configuración para el target.
angel@ubuntu:~/Desktop/croostool-ng/crosstool-ng-1.23.0$ ./ct-ng arm-
cortexa9_neon-linux-gnueabihf
Se pueden realizar cambios de configuración con el comando del código configuration-target.
Código 2.6 Elección de configuración para el target.
angel@ubuntu:~/Desktop/croostool-ng/crosstool-ng-1.23.0$ ./ct-ng
menuconfig
No se entrará en más detalles de mucho de los procesos ya que el objetivo de este documento
se enfoca más en la creación de un sistema con Yocto Project y no con los elementos de Linux
embebido por separado (Toolchain, bootloader, kernel y el sistema de archivos raíz). Aun así se
trata de forma somera para que se tenga cierto conocimiento del funcionamiento del desarrollo de
Linux embebido y para estar más familiarizado con las distintas herramientas de las que se disponen
para desarrollar Linux embebido.
Notar que la ejecución de $ ./ct-ng siempre se realiza desde el directorio angel@ubuntu: /Desktop/croostool-
ng/crosstool-ng-1.23.0, es decir, la ejecución del comando siempre se realiza en el directorio donde
se ha realizado la instalación de crosstool-NG. Se recomienda ejecutar $ ./ct-ng menuconfig y
realizar los siguientes cambios de configuración.
• En Paths and misc options, desabilitar Render the toolchain read-only.
• En Target options seleccionar en Target Architecture la arquitectura arm, y luego en Floating
point seleccionar hardware (FPU).
• Dentro de C-library escribir en extra config for newlib –enable-obsolete-rpc.
La primera opción es necesaria si se quieren añadir librerías a la toolchain tras la instalación. La
segunda selecciona la opción óptima de la implementación de punto flotante para un procesador
con FPU (floating point unit).
Tras esto se puede usar crosstool-NG para obtener, configurar y contruir componentes para nuestro
sistema de destino haciendo uso del comando $ ./ct-ng build. Tras unas decenas de minutos se
encontrará la toolchain en el directorio /home/angel/x-tools/arm-cortexa9_neon-linux-gnueabihf/
12 Capítulo 2. Toolchain
2.3.2 Anatomía de una toolchain
La toolchain se encuentra en el directorio /home/angel/x-tools/arm-cortexa9_neon-linux-gnueabihf/bin
y dentro de ella se encuentra el croscompilador arm-cortexa9_neon-linux-gnueabihf.gcc. Para
hacer uso de él se tendrá que añadir al path como se muestra en el código 2.7.
Código 2.7 Adición del croscompilador al path.
PATH=~/x-tools/arm-cortexa9_neon-linux-gnueabihf/bin:$PATH
Ahora, por ejemplo, se puede tomar un programa simple llamado holamundo como el que sigue.
Código 2.8 Programa holamundo.
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]) {
printf ("Hola, mundo!\n");
return 0;
}
Se puede croscompilar el programa de la siguiente manera.
Código 2.9 Croscompilación del programa holamundo.
arm-cortexa9_neon-linux-gnueabihf-gcc holamundo.c -o holamundo
Y se puede comprobar que el archivo se ha croscompilado con éxito con el comando del código
2.10.
Código 2.10 Información sobre el ejecutable holamundo.
angel@ubuntu:~/Desktop$ file holamundo
holamundo: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/
Linux 3.2.0, not stripped
Si se precisa más información sobre el croscompilador, como puede ser tener conocimiento sobre
la versión o sobre como se ha configurado, se puede adquirir información como se muestra en el
código 2.11, donde también se muestra la respuesta a los comandos:
Código 2.11 Información sobre el croscompilador arm-cortexa9_neon-linux-gnueabihf-gcc.
angel@ubuntu:~/Desktop$ arm-cortexa9_neon-linux-gnueabihf-gcc --version
arm-cortexa9_neon-linux-gnueabihf-gcc (crosstool-NG crosstool-ng-1.23.0)
6.3.1 20170109
Copyright (C) 2016 Free Software Foundation, Inc.
2.3 Crosstool-NG 13
This is free software; see the source for copying conditions. There is
NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
angel@ubuntu:~/Desktop$ arm-cortexa9_neon-linux-gnueabihf-gcc -v
Using built-in specs.
COLLECT_GCC=arm-cortexa9_neon-linux-gnueabihf-gcc
COLLECT_LTO_WRAPPER=/home/angel/x-tools/arm-cortexa9_neon-linux-
gnueabihf/libexec/gcc/arm-cortexa9_neon-linux-gnueabihf/6.3.1/lto-
wrapper
Target: arm-cortexa9_neon-linux-gnueabihf
Configured with: /home/angel/Desktop/crosstool-ng/crosstool-ng-1.23.0/.
build/src/gcc-linaro-6.3-2017.02/configure --build=x86_64-build_pc-
linux-gnu --host=x86_64-build_pc-linux-gnu --target=arm-
cortexa9_neon-linux-gnueabihf --prefix=/home/angel/x-tools/arm-
cortexa9_neon-linux-gnueabihf --with-sysroot=/home/angel/x-tools/arm-
cortexa9_neon-linux-gnueabihf/arm-cortexa9_neon-linux-gnueabihf/
sysroot --enable-languages=c,c++ --with-cpu=cortex-a9 --with-fpu=
neon --with-float=hard --with-pkgversion='crosstool-NG crosstool-ng
-1.23.0' --enable-__cxa_atexit --disable-libmudflap --disable-
libgomp --disable-libssp --disable-libquadmath --disable-libquadmath-
support --disable-libsanitizer --disable-libmpx --with-gmp=/home/
angel/Desktop/crosstool-ng/crosstool-ng-1.23.0/.build/arm-
cortexa9_neon-linux-gnueabihf/buildtools --with-mpfr=/home/angel/
Desktop/crosstool-ng/crosstool-ng-1.23.0/.build/arm-cortexa9_neon-
linux-gnueabihf/buildtools --with-mpc=/home/angel/Desktop/crosstool-
ng/crosstool-ng-1.23.0/.build/arm-cortexa9_neon-linux-gnueabihf/
buildtools --with-isl=/home/angel/Desktop/crosstool-ng/crosstool-ng
-1.23.0/.build/arm-cortexa9_neon-linux-gnueabihf/buildtools --enable-
lto --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-
Bdynamic -lm' --enable-threads=posix --enable-plugin --enable-gold --
with-libintl-prefix=/home/angel/Desktop/crosstool-ng/crosstool-ng-1.23.0/.build/arm-cortexa9_neon-linux-gnueabihf/buildtools --
disable-multilib --with-local-prefix=/home/angel/x-tools/arm-
cortexa9_neon-linux-gnueabihf/arm-cortexa9_neon-linux-gnueabihf/
sysroot --enable-long-long
Thread model: posix
gcc version 6.3.1 20170109 (crosstool-NG crosstool-ng-1.23.0)
Del código 2.11 la información más destacable es la siguiente:
• –with-sysroot=/home/angel/x-tools/arm-cortexa9_neon-linux-gnueabihf/arm-cortexa9_neon-
linux-gnueabihf/sysroot : Esta línea informa de cual es el directorio sysroot por defecto.
• –enable-languages=c,c++: Esta línea indica que tenemos disponible los lenguajes C y C++.
• –with-cpu=cortex-a9 : Indica que el código se ha croscompilado para cpu cortex-a9.
• –with-float=hard: Genera codigos de operación para la unidad de punto flotante y usa los
registros de VFP para los parámetros.
• –enable-threads=posix: habilita los hilos de ejecución POSIX.
14 Capítulo 2. Toolchain
Esta es la configuración por defecto del compilador, la cual se puede sobrescribir directamente en
la línea de comandos, si se ve necesario. Si por ejemplo se quiere compilar para una CPU distinta,
como por ejemplo la cortex A5, se haría con la línea de comando del código 2.12.
Código 2.12 Sobreescribir configuración para una ejecución a través de linea de comando.
angel@ubuntu:~/Desktop$ arm-cortexa9_neon-linux-gnueabihf-gcc -mcpu=
cortex-a5 holamundo.c -o holamundo
2.3.3 El directorio sysroot, la librería y los archivos de cabecera
El directorio sysroot es un directorio que contiene subdirectorios para las librerías, los archivos de
cabecera y otros archivos de configuración. Como se ha visto, aparece cuando se llama a las opciones
con las que se ha configurado el croscompilador mostrándose la línea –with-sysroot=/home/angel/x-
tools/arm-cortexa9_neon-linux-gnueabihf/arm-cortexa9_neon-linux-gnueabihf/sysroot , que indica
donde se encuentra este directorio. En el directorio sysroot se encontrará lo siguiente:
• lib: Contiene los objetos compartidos para las librerias de C y el linker/loader dinámico
llamado ld-linux.
• usr/lib: los archivos de la librería estática para la librería estándar de C y otras librerías que
pueden ser instaladas posteriormente.
• usr/include: Este directorio contiene archivos de cabecera para las librerías.
• usr/bin: Contiene programas de utilidad que se ejecutan en el destino como por ejemplo el
comando ldd.
• usr/share: Utilizada para la localización, contiene archivos de lectura que no dependen de la
arquitectura, por lo tanto podría ser compartido por varias máquinas distintas, no siendo así
con distintos S.O o distintas versiones de S.O.
• sbin: proporciona la utilidad ldconfig que se encarga de controlar dónde Linux busca archivos
en las librerías mientras esta ejecutando programas y por tanto optimizando la carga de los
paths.
2.3.4 Librerías, componentes de la librería de C
En informática, una biblioteca (del inglés library) es un conjunto de implementaciones funcio-
nales (como por ejemplo leer del teclado o mostrar en pantalla), codificadas en un lenguaje de
programación, que ofrece una interfaz bien definida para la funcionalidad que se invoca.
A diferencia de un programa ejecutable, las funcionalidades que implementan las librerías no
esperan ser utilizadas de forma autónoma, es decir, no ejecutaremos estas funcionalidades de
forma autónoma, sino que estas funcionalidades serán utilizadas por otros programas, de forma
independiente y pudiendo ser simultánea. Es una forma de ahorrar tiempo y código al no tener que
volver a programar de nuevo ciertas funcionalidades dentro de cada nuevo programa, sino que para
utilizar esas funcionalidades se pueden realizar llamadas a esas funciones. Se utiliza en informática
indistintamente el termino librería y biblioteca para referirse al término original en inglés library.
Para que gran cantidad de programas puedan hacer uso de librerías existen librerías estándar, que
simplemente son, como su propio nombre indica, librerías que se toman de forma estándar para
utilizar en los programas. Esto hace que muchos programas puedan implementar las funcionalidades
de la misma manera.
2.3 Crosstool-NG 15
La librería de C no solo se compone de un archivo, sino que se compone de cuatro partes
principales que juntas implementan la API de funciones POSIX.
Una API (Application Programming Interfaces) es una especificación formal sobre como un
módulo software se comunica con otro. Es decir, es un conjunto de comandos, rutinas, protocolos,
funciones y procedimientos que permiten que un programa utilice ciertas funcionalidades. Es un
concepto parecido a la librería solo que es más focalizado si hablamos en general. Sería como la
parte de la librería a la que accede un programa mientras usa la biblioteca. Una API puede ser
implementada por distintas librerías y muchas veces la implementación interna de las funciones
queda oculta al público.
POSIX es el acrónimo de Portable Operating System Interface, y la X viene de UNIX como seña
de identidad de la API. POSIX es una norma escrita por la IEEE. Dicha norma define una interfaz
estándar del sistema operativo y el entorno, incluyendo un intérprete de comandos (o "shell"), y
programas de utilidades comunes para apoyar la portabilidad de las aplicaciones a nivel de código
fuente.
Una vez aclarados estos conceptos se indica a continuación los cuatro elementos de la librería de
C:
• libc: La parte principal de la librería de C que contiene funciones conocidas como open,
close, read, write, printf, etc.
• libm: Contiene funciones matemáticas como cos, exp y log.
• libpthread: Lo forman todas las funciones d hilos de ejecución (thread) de POSIX que
comienzan con pthread_
• librt: Las extensiones para tiempo real de POSIX,incluyendo memoria compartida y I/O
asíncrono.
Atendiendo a la forma en la que se enlazan las librerías para ser utilizadas por un programa se
pueden diferenciar dos tipos de librería, las librerías estáticas y las dinámicas.
• Librerías estáticas: Se enlazan al compilar y quedan "dentro" del ejecutable final, ya que al
compilarlo queda dentro del lenguaje máquina del ejecutable las funcionalidades a las que se
han llamado a través de la librería. Es decir, para que sea de fácil entendimiento se puede
decir que en lenguaje máquina del ejecutable se encuentra, tanto el código programado por el
usuario, como el código de las librerías a las que se llama. Lo anteriormente descrito hace
que el ejecutable ocupe más espacio aunque lo hace también muy robusto al no depender de
archivos externos a él. En Windows tienen extensión .lib y en Linux tienen extensión .a.
• Librerías dinámicas: Se enlazan al ejecutar, y por tanto el sistema operativo se debe hacer
cargo de encontrarlas al ejecutar el programa, es decir, se enlazan dinámicamente en tiempo
de ejecución. Esto hace que el ejecutable ocupe menos espacio en memoria porque en el
ejecutable se encuentra en lenguaje máquina el código escrito por el usuario y las llamadas a
las librerías, que ocupa menos código que las librerías en sí, pero tiene el problema de que, si
no se han instalado bien las librerías, o en el lugar correcto pueden dar problemas. EnWindows
tienen la extensión .dll y se encuentran generalmente en la dirección c;\windows\system32 y
en Linux tiene la extensión .so y se encuentra habitualmente en la dirección usr/local/lib o
usr/lib.
Tras dar una breve explicación de los conceptos más relevantes sobre las librerías se mostrará lo
que se debe tener en cuenta a la hora de utilizar el croscompilador. libc, al ser el principal de los
nombrados cuando se nombró los cuatro componentes de la librería de C, está enlazado (linked) por
defecto, sin tener el usuario que especificarlo a la hora de croscompilar, pero si queremos cualquier
16 Capítulo 2. Toolchain
otro de los componentes de la librería de C tendremos que indicarlo explícitamente. Para indicárserlo
al croscompilador solo tendremos que introducir el parámetro -l seguido del nombre de la librería,
omitiendo eltérmino "lib". Es decir, para enlazar el programa que se quiera con libm, libthread o
librt se hará respectivamente como se muestra a continuación en el código 2.13 .
Código 2.13 Croscompilando y enlazando el ejecutable holamundo.
arm-cortexa9_neon-linux-gnueabihf-gcc holamundo.c -o holamundoa -lm
arm-cortexa9_neon-linux-gnueabihf-gcc holamundo.c -o holamundoa -
lpthread
arm-cortexa9_neon-linux-gnueabihf-gcc holamundo.c -o holamundoa -lrt
Si se quiere enlazar con varias de las anteriores a la vez lo único que habrá que hacer es poner
seguidos libm, libthread o librt, seguidos cada uno por un espacio siempre. Si por ejemplo se tiene
un ejecutable que se ha enlazado con libm, libpthread y libc (este último porque esta enlazado
siempre por defecto) y se quiere saber con qué librerías ha sido enlazado, se puede averiguar con el
comando del código 2.14.
Código 2.14 Obteniendo información sobre librerías compartidas enlazadas con el ejecutable
holamundo.
angel@ubuntu:~/Desktop$ arm-cortexa9_neon-linux-gnueabihf-readelf -a
holamundo | grep "Shared library"
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libc.so.6]
Como se puede observar en el código 2.14, el programa holamundo ha sido enlazado con las tres
librerías que se deseaban. Además, las librerías compartidas necesitan un enlazador en tiempo de
ejecución (runtime linker), el cual se puede conocer con el comando de código 2.15.
Código 2.15 Obteniendo información sobre el runtime linker para el ejecutable holamundo.
angel@ubuntu:~/Desktop$ arm-cortexa9_neon-linux-gnueabihf-readelf -a
prueba2 | grep "program interpreter"
[Requesting program interpreter: /lib/ld-linux-armhf.so.3]
Se tendrá que tener cuidado de que en el target se encuentren tanto las librerías como el linker
una vez desplegado el sistema en el target objetivo.
Para realizar el enlace con las librerías de manera estática se realiza como se muestra a continua-
ción en el código 2.16 :
Código 2.16 Enlazando librerías de forma estática.
2.3 Crosstool-NG 17
arm-cortexa9_neon-linux-gnueabihf-gcc -static holamundo.c -o holamundo-
estatico
Si se recoge información acerca del ejecutable holamundo-estatico se podrá comprobar que lo
que ocupa el ejecutable ha incrementado dramáticamente con respecto al ejecutable holamundo,
el cual se enlazó de forma dinámica. Esto ocurre debido a lo anteriormente comentado cuando se
explicó la diferencia entre enlazar las librerías de forma estática y dinámica.
Se dejará a elección del lector investigar más acerca de esta herramienta ya que el objetivo del
presente documento es realizar una introducción de las toolchain, poner un ejemplo de ellas y
mostrar funcionalidades de ejemplo para poder tener una idea global de lo que es Linux embebido,
de qué partes está formado y qué papel juega cada elemento.
3 Bootloader
En este capítulo se hablará sobre los bootloaders y el proceso seguido en el arranque del sistema,así como la introducción a la utilización de U-Boot, uno de los bootloaders más versátiles
que se pueden encontrar. También se introducirá el concepto de árbol de dispositivos (device tree).
3.1 ¿Cuál es la función del bootloader?
En en Linux embebido el bootloader se encarga de dos tareas: la inicialización básica del sistema
(también llamado con frecuencia el arranque) y la carga del kernel.
Cuando el bootloader comienza a trabajar lo único que se encuentra operativo en el sistema
es, típicamente, un núcleo de la cpu y algún dispositivo de memoria. Es decir, no se encuentran
disponible la mayoría del hardware, ni siquiera está configurado el controlador de la RAM, por lo
que el proceso de arranque será el encargado de conseguir, en varias fases, poner en funcionamiento
cada vez más partes del sistema.
La fase inicial de inicio o arranque finaliza cuando están funcionando las interfaces requeridas
para cargar un kernel, es decir, está operativa la memoria principal (RAM) y además están operativos
los dispositivos utilizados para acceder al kernel. La última fase del arranque del sistema es cargar
el kernel en la RAM y crear un entorno de ejecución para él.
3.2 La secuencia de arranque
Con los años el proceso de arranque se ha ido haciendo cada vez más complejo, y actualmente
se suele tratar de un proceso multietapa, que tiene diferencias dependiendo del SoC, pero que en
general cuenta de las siguientes fases.
3.2.1 Fase 1: código ROM
En ausencia de memoria externa "confiable", el código que se ejecuta nada más que se intenta
iniciar el sistema es el código ROM. Este código es programado directamente en el chip una vez
que el chip se construye, y por tanto es software propietario y no se puede intercambiar por un
equivalente de código abierto. En este caso la única memoria RAM a la que tiene acceso el código
ROM es a la SRAM (Static Random Access Memory), ya que no tiene disponible los controladores
de la DRAM (Dynamic Random Access Memory).
El código ROM es capaz de cargar pequeñas porciones de códigos en una de varias ubicaciones
preprogramadas en la SRAM. Cuando la SRAM de los SoCs no tiene la capacidad suficiente como
para cargar un bootloader completo como U-boot se carga el SPL (Secondary Program Loader)
que es un programa que se encarga de hacer el arranque intermedio. Haciendo una analogía con el
19
20 Capítulo 3. Bootloader
mundo de la aeronáutica, es como el sistema de arranque intermedio que se necesita para que el
turbofan (el compresor más específicamente) gire a las revoluciones necesarias para poder terminar
por el mismo el proceso de arranque.
Al final de esta fase se encuentra cargado el SPL en la SRAM.
3.2.2 Fase 2: SPL
El SPL debe configurar y poner a punto para el funcionamiento a la memoria principal (DRAM) y
otras partes esenciales del sistema, para cargar el TPL (Third Stage Program Loader) en la memoria
principal. La funcionalidad del SPL se parece a la del código ROM , ya que lo que es capaz de hacer
es de leer código desde distintas ubicaciones preprogramadas. Esas ubicaciones preprogramadas se
basan en offsets preprogramados en ciertos dispositivos de almacenamiento o de nombres conocidos
(preprogramados) como u-boot.bin. Esta fase del arranque todavía no permite ninguna interacción
del usuario más allá de que probablemente se impriman por pantalla ciertos mensajes.
A diferencia del código ROM el SPL puede ser de código abierto aunque no es extraño que
contenga código de propiedad del fabricante.
Al final de esta fase se encontrará cargado el TPL en la memoria principal del sistema.
3.2.3 Fase 3: TPL
Finalmente el sistema se encuentra por fin ejecutando un bootloader completo como U-boot. En
esta ocasión ya si se permite interacción entre el usuario y el sistema en tareas como elegir el kernel
que se quiere cargar y otras tareas.
Al final de esta fase estará el kernel presente en la memoria principal esperando a ser iniciado.
Habitualmente los bootloader de sistemas embebidos desaparecen de la memoria una vez iniciado
el sistema.
En la siguiente figura 3.1 se muestra de forma esquemática y de seguido, las distintas fases del
arranque del sistema que se acaban de explicar.
Figura 3.1 Proceso de arranque del sistema.
3.3 Del bootloader al kernel 21
3.3 Del bootloader al kernel
Cuando el bootloader le pasa el control del sistema al kernel tiene que aportarle cierta información
básica al kernel, como la que se presentará a continuación:
• En arquitecturas PowerPC y ARM se le muestra al kernel un número que identifica inequívo-
camente el tipo de Soc.
• Información básica acerca del hardware detectado hasta el momento, incluyendo al menos la
capacidad y localización de la RAM física y la frecuencia de reloj de la CPU.
• La línea de comando del kernel. La línea de comandos del kernel es una cadena ASCII simple
que controla el comportamiento de Linux, configurando, por ejemplo, el dispositivo que
contiene el sistema de archivos raíz.
• Opcionalmente la localización y tamaño del binariode un device tree.
• Opcionalmente también la localización y el tamaño del initial RAM disk, que es un sistema
de archivos temporal usado por el núcleo Linux durante el inicio del sistema. Es usado
típicamente para hacer los arreglos necesarios antes de que el sistema de archivos raíz pueda
ser montado.
La forma en la que se le proporciona esta información al kernel depende de la arquitectura. Si se
quiere obtener más información acerca de esto siempre es una buena idea consultar la documentación
correspondiente. La manera en la que se le proporciona esta información al kernel actualmente es
principalmente mediante lo llamado árbol de dispositivos (device tree).
3.4 Device trees
Un device tree es una manera flexible de definir los componentes hardware de un sistema. Normal-
mente el device tree es cargado por el bootloader y pasado al kernel, pero es posible agrupar el
device tree con la imagen del kernel para bootloader que no sean capaz de manejar estos de forma
separada.
El kernel de Linux contiene un gran número de archivos fuente de device tree en arch/$ARCH/boot/dts.
Si se ha adquirido hardware de un tercero muy probablemente se disponga del archivo .dts por el
paquete de soporte.
El árbol de dispositivos representa al sistema como una colección de componentes unidos en una
jerarquía en forma de árbol, con nodos raíz y nodos hijo. Este árbol comienza con un nodo raíz,
representado por "/", el cual contiene nodos y nodos hijos que representan el hardware del sistema.
Se va a representar un fragmento del device tree generado por Yocto Project para la ZedBoard en el
código 3.1 y posteriormente se va a comentar.
Código 3.1 Fragmento del device tree generado por Yocto Project para la ZedBoard.
/dts-v1/;
/ {
#address-cells = <0x1>;
#size-cells = <0x1>;
compatible = "xlnx,zynq-zed", "xlnx,zynq-7000";
model = "Zynq Zed Development Board";
22 Capítulo 3. Bootloader
cpus {
#address-cells = <0x1>;
#size-cells = <0x0>;
cpu@0 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <0x0>;
clocks = <0x1 0x3>;
clock-latency = <0x3e8>;
cpu0-supply = <0x2>;
operating-points = <0xa2c2b 0xf4240 0x51616 0xf4240>;
};
cpu@1 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <0x1>;
clocks = <0x1 0x3>;
};
};
fpga-full {
compatible = "fpga-region";
fpga-mgr = <0x3>;
#address-cells = <0x1>;
#size-cells = <0x1>;
ranges;
};
memory@0 {
device_type = "memory";
reg = <0x0 0x20000000>;
};
};
Se puede observar en el código 3.1 como se tiene un nodo raíz compuesto por un nodo llamado
CPUs con dos nodos hijos (que son los dos núcleos que tiene la CPU de la ZedBoard), un nodo
que contiene la descripción de la FPGA y otro nodo que describe la memoria RAM. En el árbol
de dispositivos mostrado se puede observar nombres con una arroba seguido de una numeración,
corchetes dentro de los cuales hay asignaciones de propiedades, etc. En resumen lo que se puede
encontrar en un árbol de dispositivos es lo representado en la figura 3.2.
Cuando se tiene un nombre_de_nodo@dirección, el cometido de la dirección es la de distinguir
ese nodo de otros. Se puede observar en el código 3.1 que en los nodos de la CPU y de la FPGA
se encuentra una propiedad llamada compatible que es la que utiliza el kernel de Linux para
emparejar el hardware que tiene esta propiedad con sus correspondientes drivers. Se ha dicho con
sus correspondientes drivers porque es común encontrarse con que la propiedad compatible tiene
varios valores, esto significa que más de un driver pueden manejar esta pieza de hardware.
3.4 Device trees 23
Figura 3.2 Sintaxis básica de los arboles de dispositivos.
3.4.1 La propiedad reg
Los nodos de memoria y CPU tienen la propiedad reg. Esta propiedad nos indica posición inicial en
registro de memoria y la longitud que ocupa en el registro. Ambos números se anotan como enteros
de 32 bits llamado celdas (cells). Por tanto en el caso del código 3.1 el nodo de memoria señala que
solo hay un banco de memoria la cual empieza en 0x0 y tiene 0x20000000 bytes de longitud, que
si lo pasamos a sistema decimal son 536870912 bytes, es decir, 512 MiB (mebibyte). Esto que se
acaba de comentar es en el caso de sistemas de 32-bits. En sistemas con direccionamiento de 64-bits
se vuelve un poco más complejo, ya que se necesitan dos celdas. un ejemplo sería el del código 3.2.
Código 3.2 Ejemplo de fragmento de device tree de sistema de 64 bits.
/ {
#address-cells = <2>;
#size-cells = <2>;
memory@80000000 {
device_type = "memory";
reg = <0x00000000 0x80000000 0 0x80000000>;
};
}
La información que contiene las celdas requeridas se encuentra en #address-cells y #size-cells,
es decir, que para entender la propiedad reg se tendrá que buscar #address-cells y #size-cells. Si no
24 Capítulo 3. Bootloader
están estas dos propiedades definidas tendrán valor 1 por defecto.
Los nodos del tipo CPU también tienen direcciones que indican el núcleo de la CPU del que se
trata, es decir, el árbol de dispositivos de un procesador de 4 núcleos tendrá direcciones 0, 1, 2 y 3
respectivamente. En este caso como no se tiene profundidad, es decir solo se tienen direcciones
de inicio y no se tiene longitud o rango de memoria se tiene en este caso #address-cells = <0x1>
y #size-cells = <0x0> en lugar de #address-cells = <0x1> y #size-cells = <0x1> (como se puede
observar en el código 3.1), lo que indica que se tiene celda de dirección pero no celda de tamaño.
3.4.2 Phandles e interrupciones
Con lo que se ha explicado hasta el momento se tiene una idea del árbol de dispositivos como una
jerarquía de dispositivos hardware que se enlazan con drivers, pero hasta ahora no se ha dicho nada
acerca de conexiones de unos dispositivos hardware con otros. Para expresar estas conexiones entre
distintos componentes se tienen los phandles, que se pueden entender como punteros a otros nodos.
Código 3.3 Fragmento del device tree para mostrar phandles e interrupciones.
/dts-v1/;
{
intc: interrupt-controller@48200000 {
compatible = "ti,am33xx-intc";
interrupt-controller;
#interrupt-cells = <1>;
reg = <0x48200000 0x1000>;
};
serial@44e09000 {
compatible = "ti,omap3-uart";
ti,hwmods = "uart1";
clock-frequency = <48000000>;
reg = <0x44e09000 0x2000>;
interrupt-parent = <&intc>;
interrupts = <72>;
};
};
Tomando como ejemplo el código 3.3 podemos observar la propiedad #interrupt-cells = <1> que
lo que indica es el número de valores de 4 bytes que son necesarios para representar una línea de inte-
rrupción, que en este caso es uno. El phandle se puede encontrar en el nodo serial@44e09000 como
interrupt-parent = <&intc>, que indica un puntero al nodo con etiqueta intc. Por esto cuando en el
nodo serial@44e09000 se define la propiedad interrupts = <72> solo aparece un número, porque
en el nodo al que apunta serial@44e09000, que es el nodo intc: interrupt-controller@48200000
define que solo tendrá un valor de 32 bits.
3.4.3 Inclusión de archivos en los árboles de dispositivos
Los archivos de árboles de dispositivos pueden no presentarse de forma unitaria, sino que pueden
estar divididos en varios archivos. Para esto se utilizan los archivos con extensión .dtsi, los cuales son
los archivos de inclusión por llamarlos de alguna manera, mientras que los archivos con extensión
.dts son los árboles de dispositivos finales.
Normalmente los archivos .dtsi contienen definiciones a nivel de SoC (y a veces definiciones
comunes a placas casi idénticas), mientras que los archivos .dts aportan la información a nivel de
3.4 Device trees 25
placa. La inclusión del archivo .dtsi se realiza simplemente superponiendo el archivo .dtsi al archivo
.dts. En el caso de la ZedBoard, la información sobre el SoC zynq-7000 lo contendría el archivo
.dtsi, y la información referente a la ZedBoard lo contendría el archivo .dts.
La inclusión del archivo .dtsi dentro del archivo .dts se puede realizar de cualquiera de las 2
formas mostradas en el código 3.4, de la cual se recomienda la segunda de las opciones.
Código 3.4 Inclusión de archivo .dtsi en el archivo .dts./include/ "archivo.dtsi"
ó
#include "archivo.dtsi"
Como ejemplo se mostrará un fragmento de archivo .dtsi y la inclusión de este en un archivo .dts
y como quedaría el código del .dtb final (si se descompilara para pasarlo a un archivo .dts).
Código 3.5 Fragmento de ejemplo de un .dtsi.
/{
compatible = "ti,am33xx";
[...]
ocp {
uart0: serial@44e09000 {
compatible = "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
status = "disabled";
};
};
};
Código 3.6 Fragmento de ejemplo de un .dts que incluye el .dtsi.
#include "am33xx.dtsi"
/{
compatible = "ti,am335x-bone", "ti,am33xx";
[...]
ocp {
uart0: serial@44e09000 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins>;
status = "okay";
};
};
26 Capítulo 3. Bootloader
};
Compilando el archivo .dts del código 3.6 se obtiene el código 3.7 en el archivo .dtb. Notar que
el archivo .dtb es un archivo en formato binario, por lo que el código que se ha representado es el
texto equivalente al contenido de ese archivo .dtb
Código 3.7 Fragmento de ejemplo de un .dts que incluye el .dtsi.
/{
compatible = "ti,am335x-bone", "ti,am33xx";
[...]
ocp {
uart0: serial@44e09000 {
compatible = "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins>;
status = "okay";
};
};
};
El bootloader y el kernel necesitan de una representación binaria del árbol de dispositivos, por
lo tanto el archivo .dts debe compilarse utilizando el compilador de árbol de dispositivos (DTC).
El archivo resultante es el llamado binario del árbol de dispositivos como marca su extensión .dtb
(device tree binary).
3.4.4 Elección de un bootloader
En la próxima tabla se presentan algunos de los bootloaders más utilizados:
Tabla 3.1 Principales bootloaders.
Nombre Arquitecturas soportadas
Das U-Boot ARM, Blackfin, MIPS, PowerPC, SH
Barebox ARM, Blackfin, MIPS, PowerPC
GRUB 2 X86, X86_64
RedBoot ARM, MIPS, PowerPC, SH
CFE Broadcom MIPS
YAMON MIPS
Para el caso que ocupa se recomienda usar U-Boot ya que como se puede comprobar es la más
versátil.
Simplemente para introducir U-Boot se mostrará como se descarga el código, como se compila y
el resultado de la compilación. Para su instalación copiamos el repositorio y realizamos un checkout
a la versión más reciente como se muestra en el código 3.8.
Código 3.8 Obtención del código de U-Boot.
3.4 Device trees 27
angel@ubuntu:~/Desktop$ git clone git://git.denx.de/u-boot.git
angel@ubuntu:~/Desktop$ cd u-boot
angel@ubuntu:~/Desktop/u-boot$ git checkout v2016.09
Hay muchísimos archivos de configuración para placas típicas dentro del directorio configs/.
Normalmente se sabe cuál usar nada más revisando el nombre del fichero de configuración, aunque
se puede obtener más información en los archivos README de las placas situadas en el directorio
board/. Si la información no queda clara, o se requiere más información, nunca viene mal una
búsqueda por Internet o una consulta en algún foro.
En el caso de querer compilar el U-Boot para la zedboard, ya que en el directorio configs/ se
tiene un archivo de zynq_zed_defconfig el proceso es muy sencillo. Lo único que se requiere es
informar a U-Boot del archivo de configuración que se va a utilizar y el croscompilador que se va a
utilizar asignando este a la variable CROSS_COMPILE del make.
Código 3.9 Compilación de U-Boot para la zedboard.
angel@ubuntu:~/Desktop/u-boot$ PATH=~/x-tools/arm-cortexa9_neon-linux-
gnueabihf/bin:$PATH
angel@ubuntu:~/Desktop/u-boot$ make zynq_zed_config
angel@ubuntu:~/Desktop/u-boot$ make CROSS_COMPILE=arm-cortexa9_neon-
linux-gnueabihf-
Notar que se ha añadido al path el croscopilador generado por la toolchainmostrada en el capítulo
2 para que pueda ser utilizado en la compilación de U.-Boot. Tras el último comando del código 3.9
se ejecutará la compilación de U-Boot para nuestro target.
Algunos de los archivos importantes generados por la compilación de U-Boot son los siguientes:
• u-boot: U-Boot en formato objeto ELF, adecuado para usar con un programa de depuración.
• u-boot.map: La tabla de símbolos
• u-boot.bin: U-Boot en formato binario sin procesar, adecuado para ejecutarse en el dispositivo.
• u-boot.img: u-boot.bin con un encabezado para ser usado por la memoria de sólo lectura de
arranque (boot ROM) para determinar cómo y donde cargar y ejecutar U-Boot.
• u-boot.dtb: el device tree.
4 El kernel
En este capítulo se explicará qué es el kernel, que contiene este y como se puede conseguiry compilar uno para introducirlo en el dispositivo de destino. También se introduciran el
concepto de módulo kernel.
4.1 ¿Qué es el kernel?
El kernel es la parte del sistema operativo que se encarga de gestionar los recursos del sistema y
de hacer de interfaz con el hardware. Es el que permite que el sistema funcione correctamente y
de forma segura, ya que se encarga de proteger fragmentos de memoria para que no puedan ser
corrompidos por acceder de forma errónea o intencionada, de manera que se pueda llegar a un
estado degradado del funcionamiento de alguna funcionalidad o algún componente hardware (ya
que también protege y gestiona el acceso al hardware).
En resumen, el kernel tiene 3 tareas:
• Gestionar recursos
• Hacer de interfaz con el hardware.
• Proveer de una API que permita un nivel de abstracción útil para los programas del espacio
de usuario.
En la figura 4.1 se representa de forma gráfica los distintos espacios en los que se pueden dividir
un sistema.
Las aplicaciones ejecutadas en el espacio de usuario tienen un bajo nivel de privilegio (en
ejecución en CPU). Lo que realizan en mayor medida son llamadas a las bibliotecas. En concreto la
biblioteca de C se puede decir que es la principal interfaz entre el espacio de usuario y el espacio
kernel, ya que traduce las funciones de nivel de usuario (como las definidas por POSIX) a las
llamadas al sistema kernel (kernel system calls).
4.2 Elección del kernel
En el caso de que se este creando un sistema operativo para un sistema embebido de manera
"artesanal", es decir, construyendo todos los componentes del sistema operativo embebido por
separado, se querrá tener un kernel que sea estable y tenga soporte a largo plazo.
Se puede obtener el árbol de git estable de Linux con cualquiera de los comandos que se muestran
en el código 4.1.
29
30 Capítulo 4. El kernel
Figura 4.1 Representación de espacio de usuario y espacio kernel.
Código 4.1 Descargando el repositorio con versiones estables del kernel de Linux.
$ git clone \
https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-
stable.git
$ git clone \
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
$ git clone \
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
Tras esto se puede hacer un git checkout a una versión particular de Linux.
Por lo general, el kernel estable se mantiene solo hasta la próxima versión principal, es decir,
de 8 a 12 semanas más tarde. Para atender a aquellos usuarios que desean actualizaciones durante
un período de tiempo más largo y tener la seguridad de que se detectarán y corregirán los errores,
algunos núcleos se etiquetan a largo plazo y se mantienen por dos o más años. Hay al menos un
kernel a largo plazo cada año. Si está construyendo para un producto que tendrá que mantenerse
durante este período de tiempo, el último kernel a largo plazo disponible podría ser una buena
4.2 Elección del kernel 31
opción. La versiones del kernel que se mantienen a largo plazo se pueden revisar en la dirección
https://www.kernel.org/.
Llegados a este punto se puede pensar que lo que queda es fácil, descargar el kernel que se quiera,
configurarlo e introducirlo en el dispositivo objetivo, pero la realidad es que eso no siempre es
posible. De hecho, Linux solo tiene soporte sólido para un pequeño subconjunto de la infinidad de
dispositivos que pueden funcionar con Linux. También se podrá encontrar soporte para la placa
objetivo o SoC de proyectos independientes de código abierto, como YoctoProject, por ejemplo.
Pero muchas veces es casi obligatorio tener que consultar con el proveedor del SoC o placa para un
kernel funcional.
En el momento de escritura del documento una de las versiones de kernel mantenidas a largo
plazo es la versión 4.9.61. Por tanto se apuntará a esta versión como se indica en el código 4.2.
Código 4.2 Cambiando a la versión del kernel que apunta ala rama 4.9.61.
angel@ubuntu:~/Desktop/linux-stable$ git checkout v4.9.61
Checking out files: 100% (32386/32386), done.
Note: checking out 'v4.9.61'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in
this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again.
Example:
git checkout -b <new-branch-name>
HEAD is now at 5caae9d... Linux 4.9.61
angel@ubuntu:~/Desktop/linux-stable$ git branch
* (HEAD detached at v4.9.61)
master
Una vez actualizado a la rama deseada se puede echar un vistazo a lo que incluye este repositorio.
Los principales directorios de interés son los siguientes:
• arch: Contiene archivos específicos de cada arquitectura contando con un directorio por cada
arquitectura.
• Documentation:Contiene ,como su propio nombre indica, documentación acerca del kernel.
Siempre que se quiera tener cierta información acerca de algún aspecto de Linux se debe
buscar en esta carpeta.
• drivers: Contiene device drivers. Contiene un directorio por cada tipo de driver.
• fs: Contiene código del filesystem.
• include: Contiene archivos de cabecera del kernel, incluyendo también los que se necesitan
para la construcción de la toolchain.
• init: Contiene código de inicio del kernel.
32 Capítulo 4. El kernel
• kernel: Contiene funciones principales, como bloqueo, los temporizadores (timers), adminis-
tración de energía, etc.
• mm: Contiene la gestión de memoria.
• net: Contiene protocolos de red.
• scripts: Contiene muchos scripts utililes como el compilador de arboles de dispositivos (dtc).
• tools: Contiene muchas herramientas utiles como por ejemplo la herramienta de contadores
de rendimiento de Linux llamada perf.
4.3 Configuración del kernel
Uno de los puntos fuertes de Linux es el grado de personalización y configuración que tiene, ya que
se puede configurar un kernel de manera que se oriente para el dispositivo más sencillo que uno
pueda imaginar hasta el más complejo.
El mecanismo de configuración se llama Kconfig, y el sistema de construcción con el que esta
integrado se llama Kbuild. La documentación de ambós se puede consultar en Documentation/k-
build. Estos dos mecanismos son utilizados también en otros proyectos, por ejemplo U-Boot y
crosstool-NG, como ya se ha visto en capítulos anteriores.
Las opciones de configuración se declaran en una jerarquía de archivos denominada Kconfig
utilizando una sintaxis descrita enDocumentation/kbuild/kconfig-language.txt. Es decir, se pueden
definir las configuraciones mediante la modificación de archivos y después la lectura de estos y la
producción de un archivo .config, que como indica el punto antes del nombre es un archivo oculto.
Hay muchas maneras de leer los archivos Kconfig y generar el archivo .config, algunas de ellas
muestran menús en pantalla que permiten hacer elecciones de una manera interactiva. Este es el
caso de menuconfig, que quizas sea el más utilizado, pero también se podrían utilizar xconfig o
gconfig.
Para lanzar el menú de configuración se realiza como se indica en el código 4.3. Hay que tener
en cuenta que se le debe especificar la arquitectura para la que se va a configurar, llamando a unos
de los nombres de los directorios que contiene el directorio arch.
Código 4.3 Comando para ejecutar la interfaz de configuración del kernel.
angel@ubuntu:~/Desktop/linux-stable$ make ARCH=arm menuconfig
Configurar el kernel de cero puede ser un trabajo que no es razonable. Sería mas razonable
empezar con alguna configuración parecida a la que se quiere, que ya esté construida buscando en
arch/$ARCH/configs. Cada archivo contiene una configuración apropiada para un SoC o grupo
de SoCs. En este caso, si se quiere construir un kernel para la ZedBoard el directorio en el que
buscar será arch/arm/configs, ya que la arquitectura de la CPU de la ZedBoad es arm. La ZedBoard
cuenta con un procesador dual core Cortex A9 que cuenta con arquitectura Armv7-A. Por tanto,
se puede seleccionar el archivo multi_v7_defconfig que es una configuración realizada para una
amplia variedad de SoCs que utilizan la arquitectura Armv7-A. Por lo tanto solo habrá que ejecutar
el comando que se muestra en el código 4.4.
Código 4.4 Comando para ejecutar la creación del .config.
angel@ubuntu:~/Desktop/linux-stable$ make ARCH=arm multi_v7_defconfig
4.4 Módulos kernel 33
HOSTCC scripts/kconfig/conf.o
HOSTLD scripts/kconfig/conf
#
# configuration written to .config
#
Se puede obtener la versión del kernel que se ha creado con el comando que se muestra en el
código 4.5.
Código 4.5 Comando para revisar que version del kernel se ha construido.
angel@ubuntu:~/Desktop/linux-stable$ make kernelversion
4.9.61
#
4.4 Módulos kernel
Se va a realizar una breve introducción acerca de los módulos kernel, los cuales se volverán a
nombrar repetidas veces a lo largo del documento.
Los módulos kernel (kernel modules en inglés) son piezas de código que se vinculan dinámi-
camente con el kernel en tiempo de ejecución, extendiendo así la funcionalidad del kernel. Estas
funcionalidades se cargan en tiempo de ejecución según las características requeridas y el hardware
detectado (ya que se pueden conectar nuevos dispositivos una vez encendido el sistema). Si no
fuera así, todos los controladores y funciones deberían estar vinculados estáticamente al kernel
y cargados en el kernel de manera permanente, lo que podría hacer al kernel alcanzar un tamaño
mucho mayor. Además los módulos kernel eliminan la necesidad de recompilar todo el kernel si se
le quiere añadir a este una nueva funcionalidad.
También se pueden añadir funcionalidades como módulos kernel para aliviar la carga de arranque
y por tanto que el arranque se produzca más velozmente, dejando los drivers y funcionalidades que
no sean esenciales para ser cargados más tarde.
4.5 Compilando
El sistema de construcción (en este caso también se le puede llamar compilación) del kernel Kbuild
lo forman un conjunto deMakefiles que reunen la información que se presenta en el archivo .config,
resuelven las dependencias y compilan todo lo que sea necesario para crear una imagen del kernel,
que contiene todos los componentes enlazados de forma estática y posiblemente uno o más modulos
kernel.
4.5.1 Compilando la imagen del kernel
Lo primero que hay que tener en cuenta es lo que cada bootloader requiere. En el caso de U-Boot,
que es el bootloader que se ha utilizado en el capítulo anterior, requiere una imagen con nombre
uImage, aunque las últimas versiones de U-Boot permiten cargar un archivo zImage usando el
comando bootz. La mayoría de los demás bootloaders requieren imagen tipo zImage.
Crear estos dos tipos de imagen es muy sencillo. Se comenzará con la imagen zImage, que se
puede compilar como se muestra en el código 4.6.
34 Capítulo 4. El kernel
Código 4.6 Creación de zImage.
angel@ubuntu:~/Desktop/linux-stable$ PATH=~/x-tools/arm-cortexa9_neon-
linux-gnueabihf/bin:$PATH
angel@ubuntu:~/Desktop/linux-stable$ make -j 4 ARCH=arm CROSS_COMPILE=
arm-cortexa9_neon-linux-gnueabihf- zImage
Notar que se tiene que tener agregado a la variable de entorno PATH el binario que permite
utilizar el croscompilador creado con la toolchain. Con la asignación en el comando del código
4.6 "-j 4" se le indica al sistema cuantos hilos de procesamiento se quieren dedicar a la tarea de
compilación de la imagen, en este caso 4 hilos.
Se puede comprobar la ubicación de zImage en la última línea que aparece

Continuar navegando