28 de mayo de 2012

El sentido de Unix y de ls

En los últimos días han aparecido dos artículos afirmando que Unix no sigue la filosofía Unix, recurriendo como ejemplo al montón de opciones de ls.

La repercusión de esos artículos me ha sorprendido y confirmado. Que Unix no es perfecto ni sigue sus propios principios no debería ser a estas alturas sorpresa para nadie, Plan 9 es la confirmación práctica de ello. El mundo Unix padece del mito de la perfección y de la Pureza De Los Principios, y pocos están dispuestos a vivir con el hecho de que usamos derivados de Unix sólo por costumbre y compatibilidad, que instalamos en nuestras máquinas de última generación sistemas operativos con defectos bastante sonrojantes y que podríamos tener cosas mejores si pudiéramos partir de cero.

Pero quería referirme a la extraña tendencia a poner como ejemplo de usurpación de principios a ls y sus numerosas opciones. En realidad, se suele acusar de "bloat" no sólo a ls, sino de todas las utilidades Unix básicas de GNU. Se acusa a estas utilidades de faltar al principio de un programa que hace una sola función y la hace bien, y su resultado es una salida de texto que puede ser procesada con otros comandos mediante tuberías. ls sería en cambio un programa que hace muchas cosas y no siempre bien, y por tanto un comando "antiunix". A mi, esta acusación me parece errónea por varios motivos.

En primer lugar hay que dejar bien claro -aunque no tenga mucho que ver con ls- que si Unix tiene comandos simples para combinarlos mediante tuberías, es precisamente para eso: para combinar, para construir cosas. El propósito de un sistema Unix no es evitar la creación de programas grandes, complejos y con muchas opciones, sino precisamente facilitar su creación. Esos programas no son contrarios al espíritu Unix, son bienvenidos y no desentonan.

Pero sobre todo, el entorno de programación de Unix no es la solución a todo, de hecho a menudo es un entorno pobre (programar scripts complejos en bash es una proeza que debería tenerse en cuenta en el Purgatorio). En el Unix original, construir lineas de comando simples comunicadas con tuberías no sólo eran la manera recomendada de construir programas complejos, sino que era la única manera: Unix en sus inicios no soportaba aspectos tan elementales hoy en día como librerías compartidas. Parte de la funcionalidad de las Xlib de X11 se programó en el servidor porque las librerías compartidas no se había popularizado y los "demonios" eran la manera habitual de compartir código entre clientes.

En el caso de ls y sus numerosísimas opciones, está claro que hay algunas que podrían ser eliminadas y sustituidas por una combinación de shell que procese la salida de un ls más simple. Por ejemplo, la opción -i, que muestra el número de inodo, podría ser eliminada y sustituida por una combinación de comandos que extraigan cada nombre de archivo de la salida de ls, se lo pasen al comando stat, extraigan de stat el número de inodo, e impriman en la pantalla la salida equivalente de ls -i. Sin embargo, lo cierto es que semejante combinación es más compleja, y probablemente también menos eficiente (por los stat()s extra), que su equivalente en ls.c.

Por último, si a pesar de esto a la gente le parece que el ls actual es excesivamente complejo, la propia extensibilidad de Unix permite a uno programarse su ls ideal y utilizarlo en sus scripts (aunque nadie lo hará, porque no merece la pena). En realidad, parte de todo este dilema de ls es que el comando ls es un bloque constructor básico de comandos Unix y simultaneamente es utilizado como programa orientado al "usuario" (del shell): lo "ideal" para algunos sería, supongo, que estas dos funcionalidades estuvieran en dos comandos diferentes y que el último fuese implementado procesando la salida del primero. Lo cierto es que sería una pérdida de tiempo. Aunque Unix tenga sus más y sus menos, que ls tenga muchas opciones es, en mi opinión, el menor de sus problemas.

21 de mayo de 2012

Las novedades de Linux 3.4

Ya se ha anunciado la versión 3.4 del kernel Linux. Esta versión incluye varias novedades en Btrfs: bloques de metadatos más grandes, mejor rendimiento de los metadatos, mejor gestión de errores y herramientas de recuperación; también hay una nueva ABI X32 que permite ejecutar programas en modo de 64 bits pero con punteros de 32 bits; hay actualizaciones de drivers gráficos como soporte inicial de modesetting de Nvidia Geforce 600 'Kepler', soporte de AMD RadeonHD 7xxx y APUs Trinity , soporte de gráficos de Intel Medfield; también hay soporte para la carga automática de drivers de cpu x86, dos nuevos targets para el device-mapper, mejoras de perf tal y como una GUI en GTK2 para perf report, y un nuevo módulo de seguridad 'Yama'. También se han incluido drivers nuevos y muchas otras mejoras y pequeños cambios. La lista completa de cambios, en inglés, puede encontrarse aquí, como siempre.


· Novedades en Btrfs (Chris Mason, el principal desarrollador, ha hecho un vídeo (archivo en formato webm y h.264) sobre el estado y futuras novedades de Btrfs que merece la pena ver)

· Btrfs: Herramientas de recuperación de datos y de reparación: Hay una nueva herramienta de recuperación de datos (btrfs-restore). Este programa no intenta reparar el sistema de archivos, tan sólo intenta extraer los archivos de un sistema de archivos dañado. La herramienta de comprobación del sistema de archivos (fsck) también ha mejorado, y puede reparar el árbol de asignación de extents (otros modos de reparación se están implementando)

· Btrfs: Bloques de metadatos mayores que 4KB: Btrfs fue diseñado desde el principio para soportar diferentes tamaños de bloques, pero el código no era estable así que se desactivó, con lo cual se usó el tamaño de bloque usado por defecto en Linux (4KB en x86). En esta versión se ha activado el soporte de bloques de metadatos mayores que una página de memoria (4 KB), con un máximo de 64 KB (los tamaños 16KB/32KB parecen funcionar mejor y están recomendados). El soporte se activa al crear el sistema de archivos (por ejemplo, mkfs.btrfs -l 32K). Estos tamaños dismuyen dramáticamente el tamaño del "allocation tree" y se fragmenta mucho menos.

· Btrfs: Mejoras de rendimiento: Btrfs ha mejorado el rendimiento en varias áreas. El aumento del tamaño máximo de los bloques de metadatos ya de por si mejora el rendimiento en cargas que incidan en el uso de metadatos, ya que la sobrecarga del allocation tree se reduce considerablemente. Pero hay otras mejoras: La manera en que Btrfs se acopla con el caché de páginas se ha mejorado y es mucho más rápido. El uso de CPU ha sido reducido. Además, el mecanismo de copy-on-write no se acoplaba muy bien con la VM de Linux, forzando a hacer más lectoras de las necesarias. Se ha tuneado el código para evitar esos casos
    Como resultado de todas estas mejoras, el rendimiento de cargas centradas en metadatos es mucho mejor. En un benchmark consistente en crear 32 millones de archivos vacíos, Btrfs creó 170.000 archivos por segundo, frente a los 110.000 de Ext4 y 115.000 de XFS. Gráficos comparando el rendimiento en 3.3 con el rendimiento en 3.4.

· Btrfs: Mejor gestión de errores: Muchas partes del código de Btrfs no eran fiables. No porque los datos pudieran ser dañados (está diseñado para que los datos siempre estén seguros), sino porque muchas funciones no gestionaban condiciones inesperadas correctamente, en su lugar se limitaban a provocar un "panic" y colgar el sistema. En esta versión, Btrfs ha sido auditado para gestionar esas situaciones correctamente: Cuando uno de esos errores ocurre, las transacciones en vuelo serán abortadas, se retornarán errores a las llamadas al sistema de los programas en espacio de usuario, y el sistema de archivos entrará en modo de sólo lectura, tal y como es la tradición en Linux.

· GPU: Soporte inicial de Nvidia GeForce 600 'Kepler': Nvidia anunció nuevas GPUs Kepler (serie GeForce 600) el día 22 de Marzo, y ese fue el día en que el equipo de Nouveau pidió la inclusión en el kernel de soporte de modesetting básico (no 3D, etc) en Nouveau para ese hardware. Se requiere firmware externo. Además, el driver Nouveau ya no está en el área "staging" y ahora es un driver normal

· GPU: Soporte de RadeonHD 7xxx y APUs Trinity: Se soportan las más nuevas GPUs y APUs de AMD

· GPU: Soporte de gráficos de Intel Medfield: Se añade soporte para el chip gráfico de Medfield derivado del GMA500. Medfield es una arquitectura embebida orientada a smartphones.

· Nueva ABI X32: El modo de 64 bits de los x86 ensancha los registros de la CPU a 64 bits, lo cual permite direccionar más de 4GB de memoria. Este ensanchamiento, sin embargo, tiene una desventaja. Dado que las direcciones de memoria son de 64 bits, los punteros también ocupan 64 bits, el doble de espacio utilizado en el modo de 32 bits. De modo que los programas compilados para 64 bits son más grandes, y cuando se ejecutan ocupan más espacio en memoria, lo cual puede tener impacto en el rendimiento dado que a mayor tamaño del puntero, menos instrucciones caben en los caches de la CPU.

Algunos programas tienen cargas lo suficientemente intensivas en CPU y punteros como para preocuparse por este rendimiento, pero no necesitan el direccionamiento de memoria de 64 bits. Pueden intentar evitar el mayor tamaño de punteros simplemente usando el modo de 32 bits, dado que las CPUs lo permiten. Pero esta opción también tiene desventajas, dado que cuando un programa se ejecuta en el modo de 32 bits, pierde todas las demás ventajas del modo de 64 bits: mayor número de registros, mejor rendimiento de cálculo de punto flotante, librerías compartidas PIC (position-independent code) más rápidas, parámetros de funciones pasados por registros, llamadas al sistema más veloces..

De modo que se ha creado una nueva ABI, X32. Un programa compilado para esta ABI se ejecuta en el modo de 64 bits, con todas sus ventajas, pero usando punteros de 32 bits. De modo que las aplicaciones que lo necesiten pueden disfrutar de las ventajas del modo de 64 bits, pero con los requisitos de memoria del de 32 bits.

· Autosondeo de drivers x86: Hay un número creciente de drivers que soportan características específicas de CPUs x86. A día de hoy estos driver necesitan varios hacks específicos que a menudo no funcionan. Por ejemplo, es un problema muy común que no se cargue el módulo de soporte de aceleración de CRC por hardware en las CPUs que soportan SSE 4.2: esto puede disminuir significativamente el rendimiento de Btrfs, que requiere una implementación de cálculo de CRC veloz. Otro problema es la carga del driver CPUFREQ correcto. Las distribuciones actuales prueban todos los drivers disponibles hasta que uno funciona, lo cual no es una buena manera de hacer las cosas.

Linux ya tiene mecanismos de autosondeo de drivers, basados en notificaciones del kernel y udev. En esta versión, Linux añade soporte de autosondeo para drivers de CPU, basado en la información de modelos y cpuid.

· Arranque verificable con el target "verity" del Device Mapper: El target "verity" del device mapper ("mapeador de dispositivos") permite utilizar un dispositivo para almacenar hashes criptográficos de los bloques de un sistema de archivos. Este dispositivo puede utilizarse para comprobar cada intento de lectura del sistema de archivos, y si el hash del bloque es erróneo, se aborta la lectura. Este target es utilizado por productos como Chrome OS y Netflix para segurar que no hay modificaciones del sistema de archivos. También puede utilizarse para asegurarse de que sólo se pueda arrancar un equipo a partir de un dispositvo-llave, tal y como un lápiz USB o un DVD.

· Capacidad de usar un disposito externo en sólo-lectura como origen de un target del device mapper de provisionamiento de espacio de disco: El device mapper soporta el uso de técnicas de aprovisionamiento de disco (crear pools de almacenamiento mayores que el tamaño máximo de los discos). En esta versión permite usar un dispositivo externo como fuente del volumen aprovisionado. Cualquier lectura a un área no provisionada se hará al dispositivo externos. Las escrituras causarán asignaciones de nuevos bloques, como siempre.

Un caso de uso para esta característica son los hosts de imágenes virtualizadas que quieren ejecutar huéspedes en volúmenes provisionados, pero tienen la imagen base en otro dispositivo compartido por muchas VMs.

· perf: GUI de report basada en GTK2, mejor visualización de ensamblador, profiling de saltos, filtrado de usuarios y threads
   · Se ha añadido un navegador basado en GTK2 para perf report. Para usarlo, ejecutar "perf report --gtk". La interfaz es algo limitada por el momento
   · Mejor visualización de ensamblador: 'perf annotate' tiene mejoras visuales para frikis del ensamblador. Se reconocen las llamadas de funciones en la interfaz TUI, y pulsando Intro se pueden seguir las llamadas (recursivas) y volver, entre otras mejoras.
   · Profiling de saltos en el código mediante hardware: perf soporta una nueva característica de CPUs Intel modernas que permite analizar con mejor detalle los saltos del código. El modo más simple se activa con 'perf record -b', for example "perf record -b any_call,u -e cycles:u branchy-command; perf report -b --sort=symbol"
   · Filtrado de usuarios y threads: perf soporta una opción de comando --uid que puede usarse para filtrar a usuarios determinados, por ejemplo perf top --uid 1000. También pueden recolectar eventos de múltiples threads especificados a mano, por ejemplo, perf top -p 21483,21485

· Módulo de seguridad 'Yama': Linux tiene varios módulos de seguridad SELinux, Apparmor, etc. Yama es uno nuevo que colecciona un número de protecciones DAC que no son gestionadas por el propio kernel. Por ahora, el Yama restringe el uso de ptrace, que permite examinar la memoria de otros procesos del mismo usuario.

· Sistema de archivos QNX6: el sistema de archivos qnx6fs es utilizado por versiones modernas de QNX (ejemplo, Neutrino). Fue introducido en QNX 6.4.0 y se usa por defecto desde 6.4.1. Esta versión añade soporte de sólo lectura

Y eso es todo. La lista completa de cambios en inglés, aquí.