¿Qué tan pequeño puede ser un kernel de Linux?

Hace algún tiempo aprendí cómo convertir máquinas virtuales a Oracle Cloud desde ubuntu 20.04 a gentoo. Las máquinas proporcionadas dentro del nivel siempre gratuito son muy débiles. Esto, en particular, lleva al hecho de que la recompilación del kernel se convierte en un proceso bastante largo. El kernel original de ubuntu 20.04 tenía 7904 parámetros en su configuración. Después de que lo hice:



make localmodconfig && make localyesconfig
      
      





el número de parámetros se redujo a 1285. Me resultó interesante tratar de eliminar todas las cosas innecesarias del núcleo y ver qué pasaba.



Estaré compilando el kernel de vainilla 5.4.0 porque esa es la versión utilizada en mi instalación de gentoo. Para acelerar el proceso, compilo el kernel en mi máquina de trabajo (i7, 8 núcleos, 64 Gb de RAM, tmpfs). Copio el kernel terminado a la máquina en Oracle Cloud. Debe iniciar el proceso con el comando:



make tinyconfig
      
      





Esto le dará un archivo .config para el núcleo mínimo de la arquitectura actual. En mi caso, este archivo contiene 284 líneas que no están en blanco y que no son comentarios.



Compilemos y observemos el tamaño del kernel:



$ yes ""|make -j$(nproc)
$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 441872 Jan 31 18:09 arch/x86/boot/bzImage
284
$

      
      





Este núcleo es completamente inútil. No solo no puede arrancar, sino que ni siquiera tiene la capacidad de informar problemas. Arreglemos esto. Para activar los parámetros, usaré el comando:



script/config -e config_parameter_name
      
      





Entonces, nuestro kernel será de 64 bits, activamos la salida de diagnóstico del kernel, habilitamos el soporte de terminal, configuramos el puerto serie y la consola en él:



./scripts/config -e CONFIG_64BIT -e CONFIG_PRINTK -e CONFIG_TTY -e CONFIG_SERIAL_8250 -e CONFIG_SERIAL_8250_CONSOLE
      
      





Una máquina en la nube de Oracle no podrá arrancar con este kernel, no habrá salida a la consola. Al final resultó que, debe agregar soporte para EFI y ACPI, que es su dependencia. El script ./scripts/config no implementa la lógica para agregar dependencias inversas, es decir, si agrega solo CONFIG_EFI, make eliminará este parámetro de la configuración. También vale la pena señalar que las opciones de habilitación a menudo incluyen las siguientes opciones. Entonces, en el caso de habilitar CONFIG_ACPI, se habilita automáticamente, por ejemplo, soporte para el botón de encendido / apagado.



./scripts/config -e CONFIG_ACPI -e CONFIG_EFI -e CONFIG_EFI_STUB
      
      





Juntando el núcleo:



$ yes ""|make -j$(nproc)
$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 1036960 Jan 31 18:13 arch/x86/boot/bzImage
409
$
      
      





Este kernel aún no puede completar el proceso de arranque, pero al menos puede informarlo:



Kernel panic - not syncing: No working init found.  Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance.
Kernel Offset: 0x22000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
---[ end Kernel panic - not syncing: No working init found.  Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance. ]---
      
      





Agreguemos los parámetros requeridos:



# virtual guest support and pci
./scripts/config -e CONFIG_PCI -e CONFIG_VIRTIO_PCI -e CONFIG_VIRTIO -e CONFIG_VIRTIO_MENU -e CONFIG_PARAVIRT -e CONFIG_HYPERVISOR_GUEST  
# disk support
./scripts/config -e CONFIG_BLOCK -e CONFIG_SCSI -e CONFIG_BLK_DEV_SD -e CONFIG_SCSI_VIRTIO
# filesystems
./scripts/config -e CONFIG_EXT4_FS -e CONFIG_PROC_FS -e CONFIG_SYSFS -e CONFIG_DEVTMPFS -e CONFIG_DEVTMPFS_MOUNT
# executable formats
./scripts/config -e CONFIG_BINFMT_ELF -e CONFIG_BINFMT_SCRIPT
# network
./scripts/config -e CONFIG_NET -e CONFIG_VIRTIO_NET -e CONFIG_PACKET -e CONFIG_UNIX -e CONFIG_INET -e CONFIG_NET_CORE -e CONFIG_NETDEVICES -e CONFIG_VIRTIO_NET
      
      





Juntando el núcleo:

$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 1950368 Jan 31 18:18 arch/x86/boot/bzImage
616
$
      
      





Y estamos cargados. Esta vez, el kernel encuentra con éxito el disco raíz, pero vemos errores en la consola:



The futex facility returned an unexpected error code.
...
 * Call to flock failed: Function not implemented
      
      





Estamos intentando iniciar sesión:

(none) login: root
process 182 (login) attempted a POSIX timer syscall while CONFIG_POSIX_TIMERS is not set
Password:
setgid: Function not implemented
      
      





Tampoco funciona, pero se nos pide sin ambigüedades qué parámetro agregar. Agréguelo y otros parámetros necesarios:



./scripts/config -e CONFIG_POSIX_TIMERS -e CONFIG_FUTEX -e CONFIG_FILE_LOCKING -e CONFIG_MULTIUSER
      
      





Juntando el núcleo:



$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 1979040 Jan 31 18:25 arch/x86/boot/bzImage
623
$
      
      





Esta vez logramos iniciar sesión:



instance-20210124-1735 login: root
Password:
Last login: Mon Feb  1 02:25:10 UTC 2021 from 73.239.106.74 on ssh
root@instance-20210124-1735:~#
      
      





¡Hurra! Ssh también funciona. Sin embargo, nuevamente hay errores en la consola:



 * Some local filesystem failed to mount
...
hwclock: Cannot access the Hardware Clock via any known method.
hwclock: Use the --verbose option to see the details of our search for an access method.
 * Failed to set the system clock

      
      





Y en dmesg también encontramos:



[    2.910198] udevd[360]: inotify_init failed: Function not implemented
      
      





Agregamos soporte para reloj en tiempo real, sistema de archivos vfat e inotify:



./scripts/config -e CONFIG_RTC_CLASS -e CONFIG_DNOTIFY -e CONFIG_INOTIFY_USER -e CONFIG_VFAT_FS
      
      





Juntando el núcleo:



$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 2015904 Jan 31 18:36 arch/x86/boot/bzImage
643
$
      
      





Hmmm, la unidad vfat todavía no se puede montar:



 * Some local filesystem failed to mount
      
      





Y aquí está la respuesta por qué en dmesg junto con un error más:



[    3.782884] udevd[527]: error creating signalfd
[    4.107698] FAT-fs (sda15): codepage cp437 not found
      
      





Agregar parámetros:



./scripts/config -e CONFIG_SIGNALFD -e CONFIG_NLS_CODEPAGE_437 -e CONFIG_NLS_ISO8859_1
      
      





Juntando el núcleo:



$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 2015904 Jan 31 18:41 arch/x86/boot/bzImage
646
$
      
      





Cargamos, no hay errores en la consola, pero apareció un nuevo error en dmesg:



[    2.756136] udevd[360]: error creating epoll fd: Function not implemented
      
      





Arreglemos:



./scripts/config -e CONFIG_EPOLL
      
      





Juntando el núcleo:



$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 2020000 Jan 31 19:13 arch/x86/boot/bzImage
647
$
      
      





¡Reiniciamos y esta vez no vemos nuevos errores!



En mi máquina de trabajo (i7, 8 núcleos, 64 gb de RAM, tmpfs), la configuración final está construida en 1m 16s. En la nube de Oracle con dos núcleos y en un disco normal, el mismo proceso tarda 19 m 51 s.



El núcleo resultante no es absolutamente mínimo. Entonces, por ejemplo, habilitar el soporte de red agrega un montón de adaptadores de red diferentes. No me dediqué al perfeccionismo y limpié absolutamente todo lo que no es necesario. Además, quiero advertirle que aunque la carga y las pruebas rápidas no revelaron problemas con la ausencia de componentes importantes adicionales del kernel, lo más probable es que existan. Entonces, si de repente decides reutilizar mi configuración por favor pruebe el kernel a fondo para su caso particular y agregue los parámetros requeridos del kernel si es necesario.



All Articles