El Maravilloso Mundo de Linux 2.6
Joseph Pranevich - jpranevich <en> kniggit.net
Aunque parece que fue ayer cuando instalábamos nuestros primeros
sistemas con Linux 2.4, el tiempo pasa, y el equipo del kernel
se acerca al final del desarrollo de la versión 2.6.
Este documento intenta ser una visión general de las
nuevas funcionalidades en esta versión, centrado sobre todo
en Linux para i386. Al contrario que muchos anuncios
de software cerrado, todas las novedades descritas en este documento
están ya disponibles en la serie de kernels de prueba 2.6.
(Sin embargo, existe la posibilidad de que algunas no vean la luz,
o incluso aparezcan nuevas funcionalidades.) El desarrollo del kernel
ha alcanzado la fase de estabilización, y es probable que
la versión final no se desvíe mucho de lo que hay ahora
disponible. Hay que tener también en cuenta que algunas de las
nuevas capacidades pueden ser portadas a Linux 2.4 tras
aparecer primero en Linux 2.6, bien oficialmente o como parte
de una distribución. También he incluido información sobre ciertas
funcionalidades generadas durante el ciclo de mantenimiento
de Linux 2.4, cosa que está indicada claramente donde corresponde.
Estás leyendo la segunda revisión de este documento, terminada el
26/10/2003 y basada en el kernel de desarrollo 2.6.0-test9.
Iré sacando nuevas revisiones a medida que el kernel se vaya completando,
pero este documento no estará cerrado "oficialmente"
hasta que salga la versión final. En esta segunda revisión
pueden quedar todavía errores u omisiones; si te encuentras
alguno, házmelo saber y lo corregiré gustoso.
De momento, este documento ha sido traducido a siete idiomas.
La información pertinente está en la sección "Traducciones",
justo al final.
La Historia Hasta Ahora...
Linus Torvalds inició el proyecto de kernel Linux en 1991,
un Sistema Operativo tipo Minix para su 386. (Linus originariamente
quiso ponerle "Freax" a su proyecto, pero el nombre que prosperó
fue el que hoy conocemos.) La primera versión oficial, la 1.0, apareció en
marzo de 1994; soportaba sólo máquinas i386 con
un único procesador. Exactamente un año después, apareció Linux 1.2 (marzo
de 1995) y fue la primera versión con soporte para distintas
plataformas (Alpha, Sparc y Mips), pero todavía modelos
de un solo procesador. Linux 2.0, aparecido en junio de 1996,
no sólo soportaba nuevas arquitecturas; también introdujo a Linux
en el mundo de máquinas multiprocesador (SMP: Symmetrical
Multi-Processing, o "Multi-Proceso Simétrico").
Tras 2.0, las revisiones importantes
han ido tardando más en aparecer (Linux 2.2 en enero de 1999 y 2.4
en enero de 2001), y con cada nueva entrega se ha ampliado
el rango de hardware al igual que la escalabilidad. (Linux 2.4
también fue notable al acercarse al escritorio, con soporte
en el kernel para ISA Plug-and-Play, USB, PC Card
y otras novedades.) Linux 2.6 no sólo trae ampliaciones
por ese lado: también será un salto importante al mejorar
el soporte tanto para sistemas mucho más grandes como
para dispositivos más pequeños (PDAs y similares).
Soporte de Hardware
Uno de los puntos fuertes de los sistemas operativos
basados en Linux es su flexibilidad y disponibilidad
para un rango amplio de plataformas. Aunque este documento
se centra específicamente en los usos de Linux sobre
hardware basado en la arquitectura PC, el kernel 2.6 ha
mejorado tanto en este área que merece la pena señalar
los cambios.
Bajando de Escala -- Linux para Sistemas Embebidos
Uno de los cambios fundamentales en Linux para la versión 2.6
viene de la aceptación e inclusión de gran parte del proyecto
uClinux en el kernel principal. El proyecto uClinux (que puede
pronunciarse como "u-cé-linux" [en inglés "you-see-Linux"],
aunque en rigor debe escribirse con la letra griega "mu") significa
Linux para Microcontroladores. Esta variante de Linux
ha sido un pilar fundamental para su aceptación
en el mercado embebido, y su inclusión en la versión oficial
debería aumentar aún más el desarrollo en este campo.
Al contrario que las variantes de Linux a las que estamos
acostumbrados, en los sistemas embebidos no tenemos
todas las capacidades del kernel, debido
a limitaciones de hardware. La principal diferencia en
estas variantes es la ausencia de MMU
(memory management unit o "unidad de gestión de memoria"
- lo que hace que un sistema operativo pueda trabajar
en modo protegido) integrada en el procesador.
Aunque suelen ser sistemas Linux
multitarea, no tienen protección de memoria ni otras
características asociadas.
(Sin protección de memoria, es posible que un proceso
aventurero lea los datos de otros procesos, o incluso
que los haga colgarse.) Esto reduce su utilidad en
un sistema multi-usuario, pero los hace ideales para una agenda electrónica
(PDA: Personal Digital Assistant o "Asistente
Digital Personal") de bajo coste
o un dispositivo dedicado. Es difícil exagerar la importancia
de este cambio de arquitectura en Linux 2.6: hasta el momento,
todas las versiones seguían afectadas (por más remotamente que fuera)
por las limitaciones inherentes al trabajo inicial de Linus
en su Intel 80386.
Hay varias líneas nuevas de procesadores embebidos
con soporte en Linux 2.6, incluyendo la serie Hitachi H8/300,
el procesador NEC v850, y la línea de procesadores embebidos m68k
diseñada por Motorola. Éstos últimos
son los más familiares para el usuario corriente de Linux,
ya que están en el corazón de las agendas Palm Pilot desde
el principio (la Palm 1000), hasta la Palm III. Otros modelos,
con nombres tan sugerentes como DragonBall o ColdFire,
son utilizados en sistemas y placas de evaluación
fabricadas por Motorola, Lineo, Arcturus, y otras empresas.
Por desgracia, la v2.6 todavía no permite usar
otros procesadores m68k más antiguos sin MMU (como los
procesadores 68000 utilizados en los primeros Macintosh),
pero es bastante probable que surjan proyectos amateur
para incluir éstos sistemas y otros parecidos.
Aunque no sea parte de la inclusión de uClinux
(al disponer de MMU), esta nueva revisión puede usarse
también en los procesadores de Axis
Communications, la serie ETRAX CRIS
(Code Reduced Instruction Set, o "Conjunto Reducido
de Instrucciones de Código").
(Hay que señalar que la inclusión de este procesador llegó durante
el ciclo de mantenimiento del kernel 2.4 -- bastante después
de que saliera la versión 2.4.0.)
Es un procesador embebido usado sobre todo en equipamiento
de redes. El kernel no incluye todavía soporte para variantes sin MMU,
pero varios proyectos externos están ya trabajando en ello.
Además de soporte para hardware, hay también mejoras importantes
resultantes de la integración de sistemas embebidos en el kernel
principal. Mientras que la mayoría de ellas no son visibles,
la robustez general del sistema operativo se ve mejorada por cambios
como la capacidad de construir un sistema completo
sin soporte de swap.
Aumentando la Escala -- NUMA y Máquinas Grandes
El segundo de los cambios fundamentales en Linus 2.6
ha sido en la dirección exactamente opuesta: hacer Linux un
kernel aceptable en servidores tan grandes como sea posible.
(Algunos de estos servidores están basados en procesadores
i386, otros no.) La gran diferencia en este sentido es
el nuevo soporte en Linux para servidores NUMA. Tras la arquitectura
SMP, NUMA (Non-Uniform Memory Access, o "Acceso
No Uniforme a Memoria") es un paso más dentro
del multiproceso: trae consigo una mayor eficiencia al trabajar
con muchos procesadores.
Los sistemas multiprocesador actuales fueron diseñados con
las mismas limitaciones que los mono-procesador, ya que disponen
de un único espacio de memoria para todos los procesadores.
Al aumentar su número, la altísima tasa de contención entre
las CPUs y el bus compartido de memoria crea un cuello de botella
que obstaculiza el rendimiento. Los servidores NUMA están basados
en la idea de que, para cada procesador, parte de la memoria
está más cerca que el resto; así se evita la congestión
de accesos. Una forma sencilla de entender su funcionamiento,
y no del todo incorrecta, es imaginarse un sistema con tarjetas
separadas, cada una con CPUs, memoria, y posiblemente
otros componentes (como podría ser entrada/salida). Hay muchas
tarjetas similares, que por supuesto pueden hablar entre sí;
pero está claro que a cada CPU le será más fácil comunicarse con su
memoria local, la de la misma tarjeta. Puede imaginarse la
arquitectura NUMA como un cluster integrado en los niveles
más bajos del hardware.
Para poder trabajar con estas nuevas máquinas NUMA, ha habido
que adaptar Linux en varios sentidos para crear un modelo
eficiente. Para empezar, ha sido necesario crear una API
(Application Programmer Interface,
o "Interfaz de Programación de Aplicaciones")
de topología interna, que permita a los entresijos del kernel
entender las relaciones que cada procesador y cada memoria tienen
entre sí y con los dispositivos de entrada salida.
Relacionado con lo anterior, el sincronizador de procesos
[process scheduler] de Linux es ahora capaz de entender
estas relaciones, e intentar optimizar las tareas haciendo
el mejor uso posible de los recursos locales. Por otro lado,
muchas máquinas NUMA se fabrican con "agujeros" en el espacio
de memoria "entre" nodos. El nuevo kernel puede tener en cuenta
estas discontinuidades de una forma razonable. Hay múltiples
cambios internos que han ayudado al buen uso de los recursos
en estos super-servidores,
y que han permitido refinar el funcionamiento del kernel.
Sin embargo, quedan bastantes posibilidades de mejora
en este área, y mucho trabajo por hacer para conseguir
un rendimiento óptimo. En el próximo año, veremos seguramente
aún más mejoras en el soporte de máquinas de gama altísima.
Soporte de Sub-arquitecturas
Aunque no tan nucleares como los dos cambios previos,
la nueva revisión del kernel también incluye el concepto
de "sub-arquitectura", que aumenta el alcance de Linux
en nuevos categorías de hardware. Hasta ahora,
Linux tendia a asumir que los tipos de procesador y
de sistemas estaban ligados. Por ejemplo, al ver un procesador
de tipo i386, se daba por hecho que se estaba ante un servidor
basado en PC/AT. En Linux 2.4, se deshizo la ligazón para
i386 con la adición de soporte para la Visual Workstation
de SGI, una plataforma totalmente nueva pero basada
en un chip de Intel. (De hecho, ya antes se había reconocido
esta diversidad en muchas otras arquitecturas: los procesadores
m68k se utilizan desde hace tiempo en Amigas, Macintosh
y otras plataformas.) El gran cambio en Linux 2.6 ha sido
reconocer esta diferencia de concepto y hacerla estándar,
de forma que todas las arquitecturas tengan un tratamiento
más coherente: agrupando sólo los componentes que
deben ser agrupados.
Del brazo de esta estandarización vienen dos nuevas plataformas
soportadas con procesadores i386. La primera es la arquitectura
Voyager de NCR. Es un sistema SMP
(desarrollado antes de la especificación MP de Intel,
la habitual hoy día)
basado en procesadores 486-686 en configuraciones de hasta 32x.
El número real de configuraciones vendidas con esta arquitectura
es bastate pequeño, y el soporte no ha llegado a las más antiguas.
La segunda arquitectura incluida es más corriente: la plataforma
PC-9800 desarrollada por NEC, que llegó a ser
casi predominante en Japón hasta hace relativamente poco.
Las máquinas PC-9800 originales tenían un procesador 8086;
la línea fue evolucionando (en paralelo con los derivados
del PC/AT) hasta llegar a tener procesadores tipo Pentium
y soporte para multiproceso. (Como es lógico, Linux
requiere de 386 en adelante.) Aunque completamente desconocida
en los Estados Unidos [y en Europa], Microsoft
ha sacado versiones de sus productos, hasta Windows 95,
para estas máquinas. El fabricante ha abandonado oficialmente
esta línea a favor de PCs más estándar.
Al formalizar el soporte en Linux para estos tipos de hardware
"ligeramente diferentes", será más fácil crear variantes
del kernel para otros sistemas, como almacenamiento dedicado
y otros componentes basados en procesadores ya extendidos
en la industria. Hay que tener muy claro que no debe llevarse
esta subdivisión demasiado lejos. Se ha hecho la distinción entre
varias sub-arquitecturas cuando hay componentes de bajo nivel
en el sistema, como el enrutado de IRQ, que son un poco (incluso bastante)
diferentes. Pero correr Linux en una consola X-Box es otra historia;
aquí sólo los drivers y cuatro cosas más la separan
de un sistema i386 "genérico": no debe por tanto
considerarse como una sub-arquitectura.
Hyperthreading
Otra mejora importante en el hardware soportado por Linux 2.6 está
en el soporte de hyperthreading [hiper-hilos]:
consiste en la capacidad de considerar, a nivel
de hardware, un único procesador como dos o más. Actualmente sólo
está disponible en algunos procesadores Pentium 4 modernos.
En ciertas situaciones, el rendimiento mejora notablemente,
aunque a costa de aumentar la complejidad en la sincronización
de procesos, entre otras cosas. La mejora de su
soporte en el kernel requiere que el sincronizador sea capaz
de reconocer y optimizar la carga entre los procesadores reales
y virtuales de una misma máquina.
En anteriores versiones de Linux, era posible sobrecargar
uno de ellos, ya que era complicado evaluar la carga total.
En este sentido, conviene destacar es que Linux se ha
adelantado al resto del mercado, al soportar hyperthreading
de forma inteligente y transparente. (Los servidores Win2K
pueden ver procesadores "falsos" adicionales, pero no los
reconocen como virtuales: así, se requieren licencias adicionales
para aprovechar estas CPUs. Hasta la aparición
de WinXP, el soporte de Microsoft no estuvo completo.)
El Interior de Linux
Mejoras de Escalabilidad
Además de las capacidades ya descritas, NUMA y hyperthreading,
Linux 2.6 también tiene mejoras específicas para servidores
Intel de gama alta. En lugar destacado está la inclusión
de otras novedades de este fabricante: el soporte PAE
(Physical Address Extension,
o "Extensión de Direccionamiento Físico") permite acceder a
64 GB de memoria en casi todos
los sistemas recientes x86 de 32 bits, usando una modalidad de paginado.
Además, el balanceo de IRQ ha mejorado significativamente
en sistemas multiprocesador, gracias a mejoras en el soporte
de APIC.
Aparte de las mejoras de hardware, se han aumentado
los límites internos siempre que ha sido posible. Por ejemplo,
el número máximo de usuarios y grupos en un sistema Linux ha pasado
de 65.000 a más de cuatro mil millones (de 16 bits a 32 bits),
haciendo posible su uso en grandes servidores de almacenamiento o de
autentificación. El número de PIDs (identificadores de proceso)
también ha sido aumentado de 32.000 a mil millones, lo que
disminuye el tiempo de arranque de las aplicaciones
en sistemas que sufren grandes cargas a lo largo de mucho tiempo.
Aunque el número máximo de ficheros abiertos no ha aumentado,
el kernel 2.6 no requiere fijarlo a priori;
el límite va creciendo según hace falta. Por último,
Linux 2.6 incluirá soporte mejorado de 64 bits en aquellos
dispositivos de bloque que lo soporten, incluso
en plataformas de 32 bits como la i386. Así se puede llegar
a sistemas de archivo de hasta 16 TB en hardware corriente.
Una mejora más de escalabilidad: el kernel no sólo soporta
más tipos de dispositivos, sino que además permite disponer
de más dispositivos de cada tipo. Bajo todas las iteraciones
de Linux (de hecho, en casi todos los sistemas derivados de UNIX),
los usuarios y aplicaciones de un sistema se comunican con
los periféricos a través de nodos numerados de dispositivo:
se corresponden con las entradas en el directorio "/dev".
Los nodos estaban antes limitados a 255 dispositivos "mayores"
(en general, a cada tipo de dispositivo se le asignan uno o más
nodos) y 255 números "menores" (a su vez, dispositivos del mismo
tipo). Por ejemplo, el dispositivo "/dev/sda2"
(la segunda partición del primer disco SCSI detectado) se
identifica con un número mayor 8, común para todos los discos SCSI,
y número menor 2, que indica la segunda partición.
Distintos tipos de dispositivo tienen diferentes formas de
asignar los números mayores y menores, de forma que
es difícil estar seguro del número total de dispositivos
disponibles en un sistema Linux.
Por desgracia, este esquema deja de funcionar cuando
se requiere tener más de 255 dispositivos del mismo tipo
en la misma máquina. (Sólo hace falta pensar en almacenamiento
centralizado o centros de impresión para ver la importancia
de esta limitación.)
En Linux 2.6 se han aumentado los límites: ahora hay
4095 tipos mayores de dispositivo y más de un millón
de dispositivos disponibles para cada tipo.
Con esto tendría que ser suficiente por el momento para cubrir todas
las necesidades de hardware.
Interactividad y Velocidad de Respuesta
Además de aumentar la escala, otra prioridad de la nueva
versión ha sido hacer que el sistema tenga una respuesta más ágil:
no sólo es útil para el usuario final (a nadie le gusta
un sistema que tarda en responder), también para aplicaciones
críticas donde se requiere precisión absoluta.
A pesar de estos cambios, Linux 2.6 no puede considerarse
estrictamente como Sistema Operativo en Tiempo Real,
ya que no cumple los rígidos criterios que aseguran que todas
las acciones ocurren de forma predecible; pero los cambios
en velocidad de respuesta deberían ser atractivos para
todo tipo de usuarios de Linux. (Dicho esto,
hay proyectos externos responsables de parches extraoficiales
para operar en Tiempo Real; podrían hacerse oficiales
en la próxima gran revisión.)
Una de las mejoras definitivas en Linux 2.6 es que el kernel es
por fin interrumpible (preemptible). En todas las versiones
anteriores de Linux, el propio kernel no puede ser interrumpido
mientras está procesando. (En un sistema multiprocesador,
este comportamiento se repetía en cada procesador.)
Pero en Linux 2.6, el kernel puede ser detenido a la mitad
para que otras aplicaciones sigan corriendo, aunque se esté
en medio de un proceso
intensivo de bajo nivel. Claro que habrá situaciones en que
el kernel no pueda ser interrumpido. En condiciones
normales, la mayoría de los usuarios nunca han visto retrasos
anormales, ya que no suelen pasar de una fracción de segundo.
Aun así, muchos usuarios pueden notar que el sistema
se comporta más ágilmente en modo interactivo al activar
esta opción; la entrada de usuario parecerá más rápida,
incluso en un sistema colapsado.
Los subsistemas de entrada/salida (I/O, o Input/Output)
también han sido remodelados, con vistas a hacerlos menos
sensibles bajo grandes cargas. Los cambios incluyen
la reescritura completa del sincronizador de entrada/salida,
el código del kernel que decide qué procesos leen de qué
dispositivos y cuándo. La nueva capa da más seguridad
de que los procesos no se atasquen esperando su turno,
pero sin perder las optimizaciones anteriores que
aseguran que las lecturas ocurran de la forma más
eficiente para cada dispositivo.
En la parte de las aplicaciones de software, un cambio que permitirá
una respuesta más ágil de los programas (al menos los que
lo utilicen) es el soporte de "futexes" (o Fast User-Space
Mutexes, "Mutexes Rápidos en Espacio de Usuario").
Los futexes son una forma de serializar eventos desde
múltiples procesos o hilos sin pisarse unos a otros
(la temida "condición de carrera", o race
condition). Al contrario que las operaciones tradicionales
con mutex, incluidas en muchas librerías de hilos,
este concepto está respaldado por el kernel, aunque sólo
en el caso de contención; además permite fijar
prioridades para que las aplicaciones o hilos más críticos
accedan antes al recurso en disputa. Al permitir que un programa
priorice sus tareas, las aplicaciones pueden volverse más
ágiles en las tareas críticas.
Además de todo lo anterior, hay unos cuantos cambios menores
que mejoran la interactividad y el rendimiento en muchos casos.
Entre ellos están la eliminación de más casos de
"Gran Bloqueo del Kernel" [Big Kernel Lock]
(bloqueos poco afinados, usados por Linux en los inicios del soporte
para multiprocesador), optimizaciones en la lectura anticipada
de ficheros, escritura retrasada, en la manipulación
de ficheros pequeños, y otros similares.
Otras Mejoras
Linux, como todo el movimiento de Código Abierto en general,
siempre ha sido ejemplar en su defensa de los estándares abiertos.
Otro cambio importante en la versión 2.6 es la reescritura
de la infraestructura del kernel para manejo de hilos, de forma
que se pueda correr la Librería Nativa de Hilos POSIX, NTPL
[Native POSIX Thread Library] sobre ella.
Esto conlleva mejores rendimientos en procesadores
Pentium Pro y superiores, cuando se hace uso de muchos hilos,
y el sector empresarial lleva tiempo esperándolo.
(De hecho, RedHat ya lo ha retro-implementado en Linux 2.4,
y lo incluye desde la versión 9 de RedHat y 3.0 de
Advanced Server.)
Este cambio incluye nuevos conceptos en el espacio de hilos
de Linux: grupos de hilos, memoria local para hilos individuales,
señales tipo POSIX, y demás. Y esto puede perjudicar a las
aplicaciones que, en lugar de seguir las especificaciones,
dependen de "linuxismos" ya desfasados;
así ocurre con ciertas versiones de Java desarrolladas por Sun.
Dado que los beneficios son mayores que el perjuicio, y
vista la importancia de los promotores, está claro que
las aplicaciones importantes serán adecuadas a los nuevos
mecanismos poco después de la versión definitiva.