Introducción
Primero que nada, déjame presentarme, mi nombre es David Abraham
Noriega Zamora y mi carnet es 202113378, en este manual encontraras
todo lo que hay que saber acerca de mi implementación del kernel
USAC Linux. Si eres el auxiliar, por favor póngame 100, pero si
por alguna razón mi versión fue elegida para continuar con ella en
los semestres por venir y eres un estudiante buscando iluminación
para saber como siquiera empezar, espero haber detallado lo
suficientemente bien mi proceso de desarrollo para que el tuyo sea
lo más fluido posible, y, de esa manera, no se te dificulte sacar
el lab. En otra nota, Ánimos, ya estas cerca de cerrar el pensum
y te invito a dejar en el espacio abajo tus propias palabras para
la siguiente persona que pueda leer este manual
Entorno de trabajo
Sin mas preámbulos, empecemos, las herramientas utilizadas para
iniciar el kernel son:
• Linux mint (en mi caso estoy emulando con la ayuda de VMware
Workstation)
• Tarball de la versión longterm 6.6.44 de Linux
Una vez que tengas instalada la distribución y ya esté descargado
el kernel; Para poder modificarlo es necesario instalar ciertas
herramientas:
1. GCC (GNU Compiler Collection):
Compilador de C necesario para compilar el código fuente del
kernel.
2. Make:
Herramienta para gestionar la compilación de múltiples archivos
fuente y la generación del kernel.
3. ncurses-devel:
Biblioteca necesaria para que make menuconfig funcione, lo que te
permite configurar el kernel mediante una interfaz de texto.
4. bc:
Calculadora de precisión arbitraria utilizada en scripts de
compilación del kernel.
5. flex y bison:
Herramientas para analizar y generar analizadores léxicos y
sintácticos, requeridos en algunas partes del kernel.
6. libelf-dev:
Biblioteca necesaria para trabajar con archivos ELF, que son el
formato de binarios utilizados en Linux.
7. libssl-dev:
Biblioteca para agregar soporte SSL/TLS, necesaria si compilas
ciertas opciones de seguridad en el kernel.
Se pueden instalar ejecutando este comando en la terminal:
sudo apt update
sudo apt install build-essential libncurses-dev bison flex libssl-
dev libelf-dev bc
Con esto ya está configurado el entorno inicial de trabajo.
(van a haber más cambios adelante, dependiendo de con cuantos
problemas me encuentre, estoy escribiendo esta sección cuando
recién empiezo así que haré un update a medida que avance).
Mensaje de bienvenida
La primera modificación que debemos hacer es un mensaje de
bienvenida al iniciar el kernel, en mi caso el mensaje será
“Bienvenido a Usac Linux. -David Abraham Noriega Zamora
202113378”.
Lo primero que debemos hacer es asegurarnos de que la carpeta se
haya descomprimido correctamente
Error #1: no descomprimí la carpeta correctamente y estuve buscando el
directorio init por media hora.
Después de esto nos moveremos a la ruta (relativa) init/main.c, en
donde encontraremos todo el código relacionado al arranque del
kernel, a nosotros nos interesa el método llamado start_kernel, ya
que es el que, valga la redundancia, inicia el kernel.
En este caso, el mensaje de inicio se encuentra en la línea 878,
al comienzo del método. Se utilizó pr_info para imprimir la
información.
Nota: para evitar tener que compilar el kernel cada vez que se quiere probar
una funcionalidad, estaré creando módulos independientes, ya que estos se pueden
probar más fácil (no entrare a detalles sobre cómo hacer módulos porque eso se
enseña en sopes 1). Una vez corroborado el funcionamiento, se inserta el
fragmento de código para posteriormente compilar todo.
Error #2: por supuesto que se me olvidó el punto y coma, por lo menos me di
cuenta en la primera parte de la compilación del kernel. Usar mucho Python me
malacostumbró, pero no me arrepiento de nada.
Aquí dejo la muestra de que el codigo funciona como un modulo.
Nombre del kernel
Para que todos (por si tenían alguna duda) sepan que en efecto
este es el kernel pertenece a USAC™ la siguiente modificación será
cambiar el nombre para que al usar el comando uname el nombre
mostrado sea “USAC Linux”.
Esto se hace en el archivo include/linux/uts.h y se mira así:
Nota: sé que la explicación parece sencilla, pero solo lo es para las personas
que saben donde buscarlo, en realidad, la información acerca de estas variables
no está del todo documentada, así que para ese pequeño cambio tuve que investigar
un par de horas.
Debido a que este cambio no hay manera de probarlo si no es haciendo
uso del comando uname, es necesario compilar el kernel para probar
los resultados, lo cual nos queda como anillo al dedo para
aprovechar y probar la funcionalidad anterior.
Compilación del kernel #1
Compilar el kernel es un trabajo que pone nervioso a cualquiera,
ya que normalmente, incluso en bare metal, llegan a tardar hasta
15 minutos, es por eso que si existe un error, puede que no salga
a la luz hasta que haya pasado una cantidad considerable de tiempo.
En este caso tengo buenas noticias, pero antes veamos como se
compila el kernel.
Lo primero que debes hacer es regresar al directorio raíz del
kernel, en mi caso esa carpeta se llama Linux-6.6.44, una vez ahí,
en una terminal se ejecuta el comando:
make defconfig
Esto crea un archivo de configuraciones estándar necesario para
compilar el kernel. Después de eso, se compila el núcleo del kernel
junto con los módulos con el comando:
make -j$(nproc)
Este comando es el que tarda mas o menos unos 10 minutos en
terminar de ejecutarse, así que hay que tener un poco de
paciencia. Después de que termine la ejecución (y si no hubo
ningun error) procedemos con los siguientes dos comandos:
sudo make modules_install
sudo make install
Estos instalan el kernel en el directorio /boot y actualizan el
gestor de arranque, tras lo cual ejecutaremos:
sudo update-grub
Para que nuestro gestor de arranque incluya el nuevo kernel.
Y listo! Lo único que queda es reiniciar nuestra computadora con
sudo reboot
Cuando la computadora este arrancando, se debe pulsar la tecla
shift para acceder al menú de grub, luego ir a “Advanced options
for Linux mint” y escoger el kernel que acabamos de instalar, en
este caso el 6.6.44
Nota: cuando el kernel se inicie va a parecer que da un error, pero esto es
normal, solo debemos esperar a que termine de iniciarse y después de un par
de minutos estarás en una consola negra.
Error #3: Si algo parece que tiene un error es porque TIENE un error, en este
caso el archivo de configuración estaba en blanco, y por eso al bootear da un
error y se queda en la pantalla de initramfs.
Ahora, las buenas noticias es que, como lo mencionaba en la
sección anterior, el error del punto y coma fue el único que
hubo, y la compilación del kernel salió bien.
Compilación del kernel #2
Para empezar, debo decir que me siento totalmente avergonzado por
la manera en la que asegure que todo me estaba funcionando
correctamente la primera vez, pues la verdad era completamente
diferente a lo que pensaba.
En primer lugar reemplazamos make defconfig con:
make localmodconfig
ya que este comando crea una plantilla utilizando como referencia
la configuración que ya existe en el kernel de la distribución,
pero tenemos que hacer unas configuraciones adicionales, y debemos
correr los siguientes comandos:
scripts/config --disable SYSTEM_TRUSTED_KEYS
scripts/config --disable SYSTEM_REVOCATION_KEYS
scripts/config --set-str CONFIG_SYSTEM_TRUSTED_KEYS ""
scripts/config --set-str CONFIG_SYSTEM_REVOCATION_KEYS ""
Ahora en vez de utilizar make -j$(nproc), utilizaremos:
fakeroot make -j$(nproc)
Esto es una pequeña optimización que ayuda a reducir el tiempo de
compilación del kernel, después continuaremos desde sudo make
install_modules, al terminar, y elegir la versión nueva del kernel
Linux mint se iniciará y podremos revisar los resultados.
Llamada al sistema #1
Ahora ya que estamos familiarizados con el kernel es hora de
movernos a una tarea más compleja, hacer llamadas al sistema
personalizadas, estas llamadas son básicamente fragmentos de
código en c que nos ofrecen funcionalidades que pueden ser
accedidas con el simple hecho de estar utilizando el kernel, la
primera que vamos a hacer es una que nos permita acceder a los
últimos 5 mensajes que ha mandado el sistema, esto nos ayudara a
monitorear y depurar el comportamiento del sistema, empecemos:
Lo primero que tenemos que hacer es dirigirnos (desde la raíz del
código fuente) al archivo kernel/sys.c, en este archivo se guarda
todo el código relacionado con las llamadas al sistema, en este
caso, voy a insertar mi nueva llamada en el final del archivo, en
la línea 2916.
En este caso no voy a entrar en detalles acerca del código, pero
para resumir, utilizamos un buffer que vamos llenando línea por
línea hasta llegar a los últimos 5 mensajes, mientras descartamos
las líneas que hayan pasado, aquí está el código:
En esta función utilizamos una variable que se llama
BUFFER_SIZE, que declaramos mas arriba de la siguiente manera:
#define BUFFER_SIZE 1024
Y necesitamos hacer uso de una librería que no esta importada, y
tenemos que hacerlo con la línea:
#include <linux/slab.h>
Compilacion #3
Después de volver a compilar el kernel, haremos un programa
sencillo en c para probar el funcionamiento de la llamada al
sistema que acabamos de implementar:
Al compilar y ejecutar este programa comparando la salida con el
comando dmesg, podemos ver que conseguimos extraer exitosamente
los mensajes.
Nota: sorpendentemente no hubo ningun error en esta sección, que feliz estoy
Llamadas al sistema #2 y #3
La llamada que acabamos de hacer funcionar era la mas complicada,
y es por eso que puedo decir con certeza que no va a haber ningún
error en las demás llamadas (veamos que tan bien envejece esta
sección en un par de minutos).
Las dos llamadas son funciones prácticamente de 5 lineas cada una,
las dejo a continuación.
Y después de compilar el kernel una ultima vez y probar las
llamadas, obtenemos los resultados que buscábamos:
Nota: efectivamente, no tuvimos ningún problema en esta sección, el
comentario del inicio envejeció como el vino.
Fin de la practica #1
Con esto concluimos la practica #1 y podemos afirmar que ya estamos
más familiarizados con el kernel y como modificarlo, este es mi
mensaje final por el momento, volveré en el proyecto.