Las habilidades técnicas de desarrollo del núcleo Linux se dividen en 2 categorías: generales y específicas de dominio. Las habilidades generales deben ser conocidas por cada desarrollador del kernel, mientras que las específicas del dominio por un desarrollador que trabaje en esta área de dominio en particular, por ejemplo: redes, almacenamiento, virtualización, criptografía, incrustado, etc... El Kernel de Linux es enorme y es imposible conocer cada parte con el mismo nivel de detalle.
Empecemos por los conocimientos generales:
- Estilo de codificación del Kernel - El Kernel Linux tiene su propio estilo de codificación que puede variar ligeramente de un subsistema a otro. Siempre es una buena idea comprobar periódicamente su código con un script especial dentro del árbol de código fuente del kernel scripts/checkpatch.pl.
Puede leer también | Lanzamiento del kernel GNU Linux-Libre 6.5 para quienes buscan un PC 100% libre
- Los patrones de codificación del Kernel - El Kernel tiene un conjunto de patrones de codificación que se recomienda utilizar. El más conocido de ellos es la asignación/desasignación de recursos durante la inicialización de recursos en varios pasos utilizando el operador goto.
- Las estructuras de datos internas del Kernel - Hay varias estructuras de datos importantes en el Kernel que se usan globalmente y que todo desarrollador debe conocer. Estas son: Listas Simple y Doblemente Enlazadas, Colas, Hashes, Árboles Binarios, Árboles Rojo-Negro, Árboles Maple, etc.
- Primitivas de sincronización: a principios de la década de 2000, se introdujeron las primeras CPU SMP. Desde entonces, todos los desarrolladores del núcleo deben escribir su código teniendo en cuenta el multithreading. El Kernel Linux tiene muchas primitivas de sincronización, cada una con un propósito diferente: operaciones atómicas, bloqueos de giro, semáforos, mutexes, RCUs (clase de algoritmo sin bloqueo), etc.
- Manejo de interrupciones: mitades superior e inferior - El núcleo Linux tiene un esquema único de manejo de interrupciones: mitades superior e inferior. La mitad superior está destinada a manejar una interrupción tan rápido como sea posible y regresar, mientras que la mitad inferior es trabajo diferido que maneja los resultados entregados por la mitad superior. Por ejemplo: la mitad superior copia un nuevo paquete de una tarjeta de red a la memoria principal lo más rápido posible y regresa a la espera de uno nuevo. Una mitad inferior, que se ejecuta más tarde como trabajo diferido, examina el paquete recibido y lo maneja: rellena algunos campos, crea estructuras de datos apropiadas y lo pasa a la pila de red del Kernel. Cada desarrollador debe ser consciente de este esquema de manejo de interrupciones y diseñar sus manejadores de interrupciones apropiadamente.
Puede leer también | Linus Torvalds y su post para presentar el kernel de Linux 6.5
- Trabajo diferido - Una situación común en el desarrollo del Kernel de Linux es posponer una parte del trabajo para algún momento en el futuro. Las interrupciones, mencionadas en el punto anterior, son un buen ejemplo. Existen varios mecanismos de trabajo diferido en el kernel para diferentes situaciones: colas de tareas, softirqs, tasklets, workqueues, etc.
- Gestión de memoria - Los desarrolladores del kernel deben ser conscientes de la existencia de 2 capas de gestión de memoria: la capa más baja, nativa, consiste en las funciones kmalloc/kfree, y la capa slab, que se construye sobre la nativa y está pensada para almacenar estructuras de diferentes tamaños en diferentes cachés para evitar la fragmentación de la memoria.
- Sistema de archivos virtual - Independientemente del tipo de sistema de archivos de capa inferior (ext3, ext4, zfs, lustrefs, xfs, etc...), el núcleo mantiene una interfaz universal sobre él. Vale la pena obtener un conocimiento general de VFS, ya que la interacción del sistema de archivos es uno de los métodos de comunicación más populares entre el núcleo y el espacio de usuario.
Puede leer también | Cómo obtener el último kernel de Linux en su equipo Ubuntu
- Planificador - El planificador gestiona todos los procesos del sistema operativo: kernel y espacio de usuario. El desarrollador debe conocer sus fundamentos.
- Interfaz de llamada al sistema - La principal forma de comunicación entre el núcleo y el espacio de usuario es la interfaz de llamada al sistema. La biblioteca libc en el espacio de usuario la encapsula y proporciona funciones más completas y convenientes para un desarrollador, pero a veces es necesario llamar a un sistema directamente. Así que tanto los programadores del espacio de usuario como los del núcleo deben saber cómo hacerlo. En el caso muy raro, un desarrollador del kernel podría querer añadir una nueva llamada al sistema o rastrear sus argumentos usando ftrace/strace. Conocimientos útiles.
Puede leer también | El Kernel de Linux obtiene más infraestructura para Rust
- Catálogos /sys /proc - La segunda forma más popular de interacción entre el núcleo y el espacio de usuario es un sistema de ficheros. En particular, montones de información y configuraciones están contenidas en los directorios /sys y /proc. Vale la pena aprender esas estructuras.
- Módulos cargables del núcleo - Una de las principales ocupaciones de un desarrollador del núcleo Linux es desarrollar controladores para nuevos dispositivos de hardware. Los controladores Linux se hacen en forma de Módulos Kernel Cargables, binarios de formato especial creados a partir de código fuente hecho usando una estructura particular. Estos módulos pueden cargarse/descargarse sin reiniciar el sistema. El desarrollador debe conocer la estructura de los módulos del kernel en general, y las reglas adicionales para dispositivos de caracteres, bloques y red. Además, debe conocer las formas de comunicación con el espacio de usuario: atributos sysfs, MMIO, parámetros de módulos del kernel, etc.
- Udev - Los desarrolladores de controladores deben conocer el subsistema Udev que implementa la infraestructura que soporta la ejecución de scripts de usuario cuando un dispositivo está conectado en caliente.
- Marco de inyección de fallos - Permite probar rutas de código inusuales inyectando resultados de error en funciones típicamente siempre correctas como malloc de asignación de memoria.
Puede leer también | Rust : ha actualizado como versión Rust de Coreutils para el kernel de Linux
- Kernel Sanitizers - KASAN, KMSAN y otros. Las herramientas dinámicas detectan algunas malas situaciones como la corrupción de memoria. Siempre merece la pena cargar el nuevo módulo del kernel, ejecutar una carga de trabajo e intentar detectar algún error dinámico sutil.
- Validador de corrección de bloqueos - Algunas partes del código del núcleo/módulo implementan sofisticados esquemas de bloqueo. Esto a menudo conduce a bloqueos muertos y vivos. Es difícil depurar este tipo de código. El validador en tiempo de ejecución Lockdep detecta estas situaciones y ahorra horas de depuración.
- Kdump/Kexec - Hay situaciones en las que es casi imposible depurar código, especialmente si está relacionado con código de arranque temprano del sistema. Aquí es donde Kdump/Kexec viene a ayudar. Carga el segundo, crash kernel, que intercepta el crash kernel y hace su volcado para su posterior análisis.
Cuando se trata de habilidades específicas relacionadas con el dominio, la única respuesta correcta: depende. El núcleo de Linux contiene muchos marcos especializados. Sólo un ejemplo: Merece la pena aprender los marcos I2C (SMB), SPI y GPIO para el desarrollo integrado.