Capítulo 14. Sólo para hackers serios de FreeBSD

14.1. ?Qué son SNAPs y RELEASEs?
14.2. ?Cómo puedo hacerme mi propia release personalizada?
14.3. ?Cómo creo discos de instalación personalizados?
14.4. ``make world'' destruye mis binarios instalados.
14.5. Cuando mi sistema arranca, dice (bus speed defaulted).
14.6. ?Puedo seguir la rama current con acceso limitado a Internet?
14.7. ?Cómo partir la distribución en archivos de 240k?
14.8. ?He escrito una extensión del kernel, a quien la envío?
14.9. ?Cómo se detectan e inicializan las tarjetas ISA y PnP?
14.10. ?Soporta FreeBSD arquitecturas diferentes a x86?
14.11. Necesito un numero de dispositivo para un driver propio
14.12. Alternativas a la política de directorios
14.13. Obtener todo lo posible de un "kernel panic"
14.14. dlsym() no funciona con ejecutables ELF!
14.15. Incrementando o reduciendo el espacio de direcciones del kernel

14.1.

?Qué son SNAPs y RELEASEs?

Hay actualmente tres ramas activas/semi-activas en el desarrollo de FreeBSD y en su CVS Repository:

  • RELENG_2_2 AKA 2.2-stable AKA "2.2 branch"

  • RELENG_3 AKA 3.x-stable AKA "3.0 branch"

  • HEAD AKA -current AKA 4.0-current

HEAD no es una rama actual, como las otras dos, es simplemente una constante simbólica para la versión de desarrollo actual a la cual nos referimos simplemente como -current.

Actualmente, -current es el desarrollo de la versión 4.0 y la rama 3.0-stable es RELENG_3, separada de -current en Enero de 1999.

14.2.

?Cómo puedo hacerme mi propia release personalizada?

Para hacer una release necesitas hacer tres cosas: primero, necesitas usar un kernel con el driver vn configurado. Añade esto a tu archivo de configuración del kernel y crea un nuevo kernel:


          pseudo-device vn         #Vnode driver (turns a file into a device)
        

Segundo, debes tener las herramientas del CVS a mano. Para hacer esto, puedes usar CVSUP pero en tu supfile pon el nombre de la release a cvs y borra cualquier tag campo de fecha:

        *default prefix=/home/ncvs
        *default base=/a
        *default host=cvsup.FreeBSD.org
        *default release=cvs
        *default delete compress use-rel-suffix

        ## Main Source Tree
        src-all
        src-eBones
        src-secure

        # Other stuff
        ports-all
        www
        doc-all
        

A continuación ejecuta cvsup -g supfile para tener todos los bits correctos en tu computador.

Finalmente, necesitas una buena cantidad de espacio vacío para crear en el la release. Digamos que está en /algun/disco/grande y en el ejemplo anterior has dejado los archivos del CVS en /home/ncvs:


        setenv CVSROOT /home/ncvs        # or export CVSROOT=/home/ncvs
        cd /usr/src/release
        make release BUILDNAME=3.0-MY-SNAP CHROOTDIR=/algun/disco/grande/release
        

Una release completa será creada en /algun/disco/grande/ y tendrás una instalación completa de tipo FTP en /algun/disco/grande/R/ftp cuando acabes. Si quieres crear tu SNAP usando otra rama de desarrollo diferente de -current, puedes añadir RELEASETAG=SOMETAG a la línea de comando anterior de creación de la release. Por ejemplo, RELEASETAG=RELENG_2_2 crearía un snapshot 2.2 GAMMA.

14.3.

?Cómo creo discos de instalación personalizados?

El proceso completo de creacación de discos de instalación y archivos fuentes y binarios esta automatizado por varios targets en /usr/src/release/Makefile. La información alli contenida debería ser suficiente para que puedas empezar. Falta decir que este proceso necesita la ejecución del comando "make world" y quizás te use mucho tiempo y espacio en disco.

14.4.

``make world'' destruye mis binarios instalados.

Sí, esta es la idea general; como su nombre sugiere, "make world" rehace todos los binarios del sistema, de manera que puedas estar seguro de tener un entorno limpio y consistente al final (que es por lo que tarda tanto).

Si la variable de entorno DESTDIR está definida mientras se ejecuta make world o make install, los binarios creados nuevamente seran depositados en un árbol de directorios idéntico al instalado, y a partir de ${DESTDIR}. Algunas combinaciones aleatorias de modificaciones de librerías compartidas y programas pueden causar que falle el make world.

14.5.

Cuando mi sistema arranca, dice (bus speed defaulted).

Las controladoras SCSI Adaptec 1542 permiten al usuario configurar su velocidad de acceso al bus en software. Versiones anteriores del driver de la 1542 intentaban determinar la velocidad más alta factible y configurar la Adaptec a esta. Nos hemos encontrado con que esto hace fallar el sistema de algunos usuarios, por lo que tienes que definir la opción de configuración del kernel TUNE_1542 para que esto ocurra. En algunos sistemas puede que puede hacer que los discos vayan más rápidos, pero en otros puede que los datos queden corrompidos.

14.6.

?Puedo seguir la rama current con acceso limitado a Internet?

Sí, puedes hacerlo sin bajarte todo el código fuente usando la utilidad CTM.

14.7.

?Cómo partir la distribución en archivos de 240k?

Los sistemas BSD más modernos tienen una opción -b para partir que les permite partir los archivos en tamaños arbitrarios.

Aqui hay un ejemplo de /usr/src/Makefile.

        bin-tarball:
        (cd ${DISTDIR}; \
        tar cf - . \
        gzip --no-name -9 -c | \
        split -b 240640 - \
        ${RELEASEDIR}/tarballs/bindist/bin_tgz.)
        

14.8.

?He escrito una extensión del kernel, a quien la envío?

Por favor, mira en como enviar código.

Y gracias por pensar en nosotros!

14.9.

?Cómo se detectan e inicializan las tarjetas ISA y PnP?

Brevemente, hay unos cuantos puertos de entrada/salida a los que todas las tarjetas PnP responden cuando el computador pregunta si hay alguien ahí. Así, cuando comienza la rutina de prueba de PnP, pregunta si hay alguna tarjeta PnP presente y todas las tarjetas responden con su número de modelo a una lectura I/O del mismo puerto. Así el código de prueba puede conocer el ID de cada tarjeta (asignado por Microsoft/Intel).

Los ID's son dos campos de 32 bits (2^64) + 8 bits de checksum. Los primeros 32 bits son el identificador del fabricante. No se ha dicho publicamente, pero parece estar asumido que diferentes tipos de tarjeta del mismo fabricante pueden tener diferentes id's de fabricante. La idea de necesitar 32 bits sólo para los fabricantes parece un poco excesiva.

La parte baja de 32 bits son un número de serie, dirección ethernet, algo que haga a la tarjeta única. El fabricante no debe producir nunca una segunda tarjeta que tenga los mismos 32 bits de la parte baja, aunque los 32 bits de la parte alta sean diferentes. Así puedes tener múltiples tarjetas del mismo tipo en la misma máquina y los 64 bits serán únicos para cada tarjeta.

Los grupos de 32 bits nunca pueden ser todos cero. Esto permite mostrar todos los bits no-cero durante la búsqueda binaria inicial.

Una vez el sistema ha identificado todos los ID's de las tarjetas presentes, reactivaráa cada tarjeta, una tras otra (a través de los mismos puertos I/O), y encontrará los recursos que cada tarjeta necesita, que opciones de interrupción están disponibles, etc. Se realiza un escaneo sobre todas y cada una de las tarjetas presentes para conocer esta información.

Esta información se combina con la información de los archivos ECU del disco y con las BIOS MLB. El soporte PnP de ECU y las BIOS para hardware en el MLB usualmente es sintético, y los periféricos no hacen PnP genuino. De todas maneras, examinando la información del BIOS más la información ECU, la rutina de prueba puede causar que los dispositivos que no son PnP puedan evitar a esos dispositivos que el código de prueba no puede volver a posicionar.

Así, los dispositivos PnP son visitados una vez más y se les asigna su I/O, DMA, IRQ, direcciones del mapa de memoria. Los dispositivos aparecerán en esas direcciones y permanecerán en ellas hasta que se vuelva a reinicializar la máquina.

Todo el proceso se ha simplificado mucho, pero espero que hayas podido hacerte una idea del proceso.

14.10.

?Soporta FreeBSD arquitecturas diferentes a x86?

Diferentes grupos de personas han expresado su interés en trabajar en un port multi-arquitectura de FreeBSD y FreeBSD/AXP (ALPHA) es un ejemplo de ese esfuerzo realizado, ahora disponible en forma de 3.0 SNAPshot release en ftp://ftp.FreeBSD.org/pub/FreeBSD/alpha. El port de ALPHA funciona actualmente en diferentes tipos de máquinas ALPHA, entre ellas, AlphaStation, AXPpci, PC164, Miata y Multia. Este port todavía no se considera una release completa y no lo será hasta que exista una colección completa de herramientas de instalación y una distribución completa en cdrom para instalació, incluyendo un número razonable de ports y packages funcionales. FreeBSD/AXP debe considerarse software de calidad BETA en estos momentos. Para más información del proyecto, subscríbete a la lista de correo.

También se ha expresado interés en un port de FreeBSD para arquitectura SPARC. Subscríbete a la lista si estás interesado en participar en el proyecto. Para discusiones generales en nuevas arquitecturas, participa en la lista .

14.11.

Necesito un numero de dispositivo para un driver propio

Esto depende de si quieres hacer que el driver esté públicamente disponible. Si la respuesta es afirmativa, por favor, envianos una copia del código fuente del driver y las modificaciones apropiadas del archivo files.i386, un ejemplo de configuración y el código apropiado de MAKEDEV para crear cualquier archivo especial que use tu dispositivo. Puedes enviar todo lo necesario a .

14.12.

Alternativas a la política de directorios

En respuesta a esta pregunta de políticas alternativas para los directorios, el esquema que está actualmente en uso no ha cambiado desde que lo escribí en 1983. Escribí esa política para el sistema de archivos rápido original, y nunca se ha revisado. Trabaja bién manteniendo los grupos de cilindros. Como muchos de vosotros habreis notado, el rendimiento es muy pobre con "find". Muchos sistemas de archivos son creados desde archivos que fueron creados por una primera búsqueda en profundidad (también conocido como ftw). Estos directorios terminan esparcidos a través de los grupos de cilindros. Si conociesemos el número total de directorios a crear, la solución sería crear (total / fs_ncg) por grupo de cilindros antes de moverlos. Obviamente, tendriamos que crear algún tipo de heurística para adivinar este número. Usando un número pequeño fijo (como puede ser 10) haría de orden de magnitud. Para diferencial restores de operaciones normales (cuando el algoritmo actual es probablemente más sensible), podrís usar el clustering hasta 10 si fueran todos hechos dentro de una ventana de diez segundos. De cualquier manera, mi conclusión es que este es un área para la experimentación.

Kirk McKusick, Septiembre 1998

14.13.

Obtener todo lo posible de un "kernel panic"

[Esta sección fue extraida de un mensaje escrito por Bill Paul en la lista FreeBSD-current por Dag-Erling Coïdan Smørgrav, quién a fijado algunos errores y añadido algunos comentarios entre corchetes]

From: Bill Paul <wpaul@skynet.ctr.columbia.edu>
        Subject: Re: the fs fun never stops
        To: ben@rosengart.com
        Date: Sun, 20 Sep 1998 15:22:50 -0400 (EDT)
        Cc: current@FreeBSD.org
        

[<ben@rosengart.com> envió el siguiente panic]

> Fatal trap 12: page fault while in kernel mode
        > fault virtual address   = 0x40
        > fault code              = supervisor read, page not present
        > instruction pointer     = 0x8:0xf014a7e5
                                        ^^^^^^^^^^
        > stack pointer           = 0x10:0xf4ed6f24
        > frame pointer           = 0x10:0xf4ed6f28
        > code segment            = base 0x0, limit 0xfffff, type 0x1b
        >                         = DPL 0, pres 1, def32 1, gran 1
        > processor eflags        = interrupt enabled, resume, IOPL = 0
        > current process         = 80 (mount)
        > interrupt mask          =
        > trap number             = 12
        > panic: page fault
        

[Cuando] ves un mensaje como este, no es suficiente con solo reproducirlo y enviarlo. El valor del puntero de instrucciones que he marcado arriba es importante; desafortunadamente, depende de la configuración. En otras palabras, el valor varía dependiendo de la imáden de kernel exacta que se use. Si estás usando el kernel GENERIC de uno de los snapshots, entonces es posible que alguien pueda seguir la función problemática, pero si estás usando un kernel personalizado, entonces solo puedes decirnos donde ha ocurrido el fallo.

Tendrías que hacer lo siguiente:

  • Anotar el valor del puntero de la instrucción. Ten en cuenta la parte 0x8: al inicio no es significante en este caso: es la parte 0xf0xxxxxx la que queremos.

  • Cuando el sistema rearranca, haz lo siguiente:


                  % nm /kernel.that.caused.the.panic | grep f0xxxxxx
                

    donde f0xxxxxx es el valor del puntero de la instrucción. El problema es que no obtendrás una búsqueda exacta ya que los símbolos en la tabla de símbolos del kernel son para los puntos de entrada de las funciones y la dirección del puntero de la instrucción estará en algún lugar dentro de una función, no al principio. Si no obtienes un resultado exacto, omite el último dígito del valor del puntero de la instrucción e intentalo otra vez, por ejemplo:


                  % nm /kernel.that.caused.the.panic | grep f0xxxxx
                

    Si esto no da ningún resultado, elimina otro dígito. Repite la operación hasta que obtengas algún tipo de salida. El resultado será una lista de posibles funciones que causan el panic. Este no es un sistema muy exacto de búsqueda de errores, pero es mejor que nada.

Veo gente que constantemente envía mensajes de panics como este, pero raramente veo que alguien se tome el tiempo de buscar la coincidencia entre el puntero de instrucción y una función en la tabla de símbolos del kernel.

La mejor manera de hacer el seguimiento de la causa de un panic es capturar un "crash dump", usando gdb(1) para hacer una traza del "crash dump". Por supuesto, esto depende de que gdb(1) funcione correctamente en -current, lo que no puedo garantizar (recuerdo que alguien ha comentado que el nuevo gdb(1) en formato ELF no manejaba bién los "dumps" de un crash del kernel; alguién debería mirar esto antes de que la 3.0 salga del estado beta).

En cualquier caso, el método que normalmente uso es este:

  • Crear un archivo de configuración de kernel, opcionalmente añadiendo 'options DDB' si piensas que necesitas el debugger del kernel por algún motivo. (Uso esto principalmente para configurar puntos de salida si sospecho que existe alguna condición que crea un loop infinito).

  • Usar config -g KERNELCONFIG para crear el directorio de configuración del kernel.

  • cd /sys/compile/KERNELCONFIG; make

  • Esperar a que el kernel termine de compilar.

  • cp kernel kernel.debug

  • strip -d kernel

  • mv kernel /kernel.orig/

  • cp kernel /

  • reboot

[Nota: ahora que los kernels de FreeBSD 3.x son ELF por defecto debes usar strip -g en lugar de strip -d. Si por algún motivo tu kernel es aún a.out, usa strip -aout -d.]

Ten en cuenta que TU NO QUIERES ARRANCAR CON UN KERNEL QUE TIENE TODOS LOS SIMBOLOS DE DEBUG EN EL. Un kernel compilado con -g puede llegar facilmente a los 10MB de tamaño. No tienes que arrancar esta imán masiva, solo lo necesitas para poder usar después gdb(1) (gdb(1) quiere la tabla de símbolos). Al contrario, quieres mantener una copia de la imágen completa y crear una segunda imágen con los símbolos de debug desactivados usando strip -d. Es esta segunda imágen la que quieres arrancar.

Para asegurarte de capturar un "crash dump", necesitas editar el archivo /etc/rc.conf y apuntar dumpdev a tu partición de swap. Esto hará que el script rc(8) use el comando dumpon(8) para activar los "crash dumps". También puedes ejecutar manualmente dumpon(8). Después de un panic, el "crash dump" puede ser recuperado usando savecore(8); si dumpdev está en /etc/rc.conf, el script rc(8) ejecutará savecore(8) automaticamente y pondrá el "crash dump" en /var/crash.

NOTA: los "crash dumps" de FreeBSD suelen tener el mismo tamaño que la cantidad total de memoria física del sistema. Esto significa que si tienes 64MB de RAM, obtendrás un "crash dump" de 64MB. Debido a esto, tienes que asegurarte de tener suficiente espacio libre en /var/crash. Alternativamente puedes ejecutar savecore(8) manualmente y hacer la recuparación en otro directorio donde tengas más espacio libre. Es posible limitar el tamaño del "crash dump" usando options MAXMEM=(foo) para indicar la cantidad de memoria que el kernel puede ocupar. Por ejemplo, si tienes 128MB de RAM, puedes limitar el uso de memoria del kernel a 16MB para que el "crash dump" sea de 16MB y no de 128MB.

Una vez hayas recuperado el "crash dump", puedes obtener una traza del stack con gdb(1) de la manera siguiente:


          % gdb -k /sys/compile/KERNELCONFIG/kernel.debug /var/crash/vmcore.0
          (gdb) where
        

Es posible que aparezcan muchas líneas de información: es una buena idea usar el comando script(1) para capturarlas todas. Usando la imágen del kernel con todos los símbolos de debug deberí mostrar la línea exacta de código fuente del kernel donde ha ocurrido el panic. Normalmente, tienes que leer la traza del stack de abajo hacia arriba para poder conocer la secuencia exacta de eventos que han provocado el crash. También puedes usar gdb(1) para mostrar los contenidos de las diferentes variables o estructuras para examinar el estado del sistema en el momento del crash.

Ahora, si eres realmente curioso y tienes un segundo computador, puedes configurar gdb(1) para hacer un debug remoto de manera que puedes usar gdb(1) en un sistema para revisar el kernel de otro sistema, de la misma manera que lo harías en la máquina local.

[Bill añade: "Olvidé mencionar una cosa: si tienes DDB activado, puedes forzar un panic (y un crash dump) tecleando "panic" en el prompt del ddb. Es posible que el debugger se pare durante la fase del panic. Si esto ocurre, teclea "continue" y el crash dump finalizará"]

14.14.

dlsym() no funciona con ejecutables ELF!

Las herramientas ELF no hacen por defecto que los símbolos definidos en un ejecutable sean visibles por el linker dinámico. Consecuentemente, dlsym() buscará en datos obtenidos desde llamadas a dlopen(NULL, flags), lo que provoca que no se encuentren esos símbolos.

Si quieres buscar, usando dlsym() símbolos presentes en el ejecutable principal de un proceso, necesitas linkar el ejecutable usando la opción -export-dynamic en el linkador ELF.

14.15.

Incrementando o reduciendo el espacio de direcciones del kernel

Por defecto, el espacio de direcciones del kernel es de 256MB en FreeBSD 3.x y 1GB en FreeBSD 4.x. Si gestionas un servidor de red muy cargado (por ejemplo, servidores FTP o HTTP con mucho tráfico), es posible que notes que 256MB no es suficiente.

Así que... como incremento el espacio de direcciones?. Hay dos aspectos a tener en cuenta. Primero, necesitas indicarle al kernel que reserve una mayor parte del espacio de direcciones para él mismo. Segundo, ya que el kernel se carga al inicio del espacio de direcciones, necesitas disminuir la dirección de carga.

El primer aspecto lo solucionamos incrementando el valor de NKPDE en src/sys/i386/include/pmap.h. Esta es una entrada de ejemplo para 1GB de espacio de direcciones:


          #ifndef NKPDE
          #ifdef SMP
          #define NKPDE                   254     /* addressable number of page tables/pde's */
          #else
          #define NKPDE                   255     /* addressable number of page tables/pde's */
          #endif  /* SMP */
          #endif
        

Para encontrar el valor correcto de NKPDE, divide el espacio de direcciones deseado (en megabytes) por cuatro, después resta uno por UP y dos por SMP.

Para solucionar el segundo aspecto, necesitas calcular la dirección correcta de carga: simplemente resta el tamaño del espacio de direcciones (en bytes) de 0x100100000; el resultado es 0xc0100000 para 1GB de espacio de direcciones. Ajusta LOAD_ADDRESS en src/sys/i386/conf/Makefile.i386 a ese valor; a continuación pon el contador al inicio de la sección listado en src/sys/i386/conf/kernel.script al mismo valor, como sigue:


          OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
          OUTPUT_ARCH(i386)
          ENTRY(btext)
          SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/obj/elf/home/src/tmp/usr/i386-unknown-FreeBSDelf/lib);
          SECTIONS
          {
            /* Read-only sections, merged into text segment: */
            . = 0xc0100000 + SIZEOF_HEADERS;
            .interp     : { *(.interp)    }
        

Reconfigura y compila el kernel. Probablemente tengas problemas con top(1), ps(1) y programas así haciendo un make world deberín solucionarse esos problemas (o una recompilación manual de libkvm, ps y top después de copiar el pmap.h parcheado a /usr/include/vm/.

NOTA: el tamaño del espacio de direcciones debe ser un múltiplo de cuatro megabytes.

[David Greenman añade: Pienso que el espacio de direcciones del kernel necesita ser una potencia de 2, pero no estoy totalmente seguro.]

Puede descargar éste y muchos otros documentos desde ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/

Si tiene dudas sobre FreeBSD consulte la documentación antes de escribir a la lista <questions@FreeBSD.org>.

Envíe sus preguntas sobre la documentación a <doc@FreeBSD.org>.