
Hojeando en un software de código abierto diverso, periódicamente encuentro todo tipo de cosas interesantes: a veces es solo un comentario divertido, a veces es algo ingenioso en un sentido más amplio. Colecciones similares aparecen periódicamente tanto en el "Internet global" como en Habré; por ejemplo, hay una pregunta bien conocida en StackOverflow sobre los comentarios en el código, y recientemente se publicó aquí una selección de nombres divertidos de entidades legales y topónimos. Intentaré estructurar y diseñar lo que gradualmente acumulé. Debajo del corte, te esperan citas de QEMU, el kernel de Linux y más.
Kernel de Linux
Creo que para muchos no es ningún secreto que las cartas de la lista de correo del kernel de Linux divergen periódicamente entre comillas. Así que echemos un vistazo mejor al código. E inmediatamente, el sistema de ensamblaje del núcleo nos recibe con una sorpresa: como saben, los proyectos creados por Autoconf tienen un Makefile con dos objetivos estándar para la limpieza: clean
y distclean
. Naturalmente, el kernel no se construye con Autoconf, y lo que solo vale menuconfig
, por lo que hay más objetivos aquí: clean
, distclean
y mrproper
: sí, sí, Mr.Proper, un limpiador central dos veces más rápido .
Hablando del sistema de configuración: una vez me sorprendí cuando lo encontré además de comandos claros como allnoconfig
, allyesconfig
(sospecho que se puede compilar algo muy depurador , por lo que ahora no me arriesgaría a descargarlo en hardware real ... .) y allmodconfig
al misterioso objetivo allrandconfig
. "¿Se están burlando?", Pensé, luego le conté a mi amigo acerca de esta observación, a lo que él respondió que probablemente era un comando completamente significativo, pero no para un montaje real, sino para probar la corrección de la disposición de las dependencias entre las opciones, como dije. Ahora, una especie de parámetros de configuración difusos.
Sin embargo, hay vida en el núcleo más allá del sistema de ensamblaje: la documentación a veces no solo es técnica, sino también de un tipo de valor artístico. Suponga que desea alertar a los usuarios del modo de suspensión de su fragilidad y el riesgo de pérdida de datos si no se siguen ciertas reglas. Escribiría tristemente, diciendo ATENCIÓN: <sustituye un par de las líneas más aburridas> . Pero el desarrollador que escribió esto hizo algo diferente:
Some warnings, first. * BIG FAT WARNING ********************************************************* * * If you touch anything on disk between suspend and resume... * ...kiss your data goodbye. * * If you do resume from initrd after your filesystems are mounted... * ...bye bye root partition. * [this is actually same case as above] * * ...
Pequeños trucos
No es sorprendente que no todos los códigos se puedan compilar con optimizaciones: cuando intenté forzarlos a que se activaran para todos los archivos de objetos, naturalmente encontré alguna fuente de entropía o algo similar que #error
si la optimización estaba activada. Bueno, la criptografía es así. Pero, ¿desea un código que no se ensamblará si desactiva todas las optimizaciones, la alineación, etc.? ¿Cómo es esto posible? Y esta es una afirmación tan estática:
Se supone, aparentemente, que para cualquier uso con un argumento constante, esta función se expandirá en una sola rama de switch
, y cuando se usa con un argumento válido , esta rama no será default:
En una forma no optimizada, esta función causará un error de enlace casi por diseño ...
Sabes
- ... que el kernel tiene un compilador JIT bytecode del modo de usuario? Esta tecnología se llama eBPF y se utiliza para enrutamiento, rastreo y mucho más. Por cierto, si no tienes miedo de las herramientas "nucleares" experimentales, mira el paquete bpftools.
- ... que el núcleo puede durar unos cinco minutos de tiempo de procesador? Existe una llamada al sistema
sendfile
que copia bytes de un descriptor de archivo a otro. Si le dice el mismo descriptor y establece el desplazamiento correcto en el archivo, rebobinará los mismos datos hasta que copie 2 GB. - ... que existe una variante del trabajo de hibernación realizado por el proceso del usuario . No me sorprenderá si también puede guardarlo en el almacenamiento de red.
QEMU
En general, cuando leí a Robert Love sobre el dispositivo kernel de Linux, y luego subí a las fuentes QEMU, tuve una cierta sensación de deja vu. Había listas incrustadas en estructuras por valor (y no como en el curso de programación inicial que aprenden, a través de punteros), y un cierto subsistema de RCU (lo que es, todavía no lo entendí completamente, pero también existe en el núcleo) y, Probablemente mucho más similar.
¿Qué es lo primero que una persona ordenada quiere trabajar en un proyecto para conocer? Probablemente con estilo de codificación. Y ya en esto, uno podría decir, ceremonial, documento, vemos:
1. Whitespace Of course, the most important aspect in any coding style is whitespace. Crusty old coders who have trouble spotting the glasses on their noses can tell the difference between a tab and eight spaces from a distance of approximately fifteen parsecs. Many a flamewar has been fought and lost on this issue.
Aquí está la eterna pregunta sobre la longitud máxima de la línea:
Lines should be 80 characters; try not to make them longer. ... Rationale: - Some people like to tile their 24" screens with a 6x4 matrix of 80x24 xterms and use vi in all of them. The best way to punish them is to let them keep doing it. ...
(Hmm ... Es dos veces más grande en cada eje de lo que a veces uso. ¿Es tal Linux HD?)
Todavía hay mucho interesante - leer .
Y de nuevo trucos
Dicen que C es un lenguaje de bajo nivel. Pero si es bueno pervertirse, puede mostrar las maravillas de la generación de código en tiempo de compilación sin Scala o incluso C ++.
Por ejemplo, el archivo softmmu_template.h está softmmu_template.h
en la base del código QEMU. Cuando vi este nombre, pensé que se suponía que debía ser copiado en mi implementación de back-end TCG y ajusté hasta que salió la implementación TLB correcta. ¡No importa cómo! Aquí se explica cómo usarlo correctamente :
accel / tcg / cputlb.h:
define DATA_SIZE 1 #include "softmmu_template.h" #define DATA_SIZE 2 #include "softmmu_template.h" #define DATA_SIZE 4 #include "softmmu_template.h" #define DATA_SIZE 8 #include "softmmu_template.h"
Como puede ver, juego de manos y sin C ++. Pero este es un ejemplo bastante simple. ¿Qué tal algo más complicado?
Existe dicho archivo: tcg / tcg-opc.h . Su contenido es bastante misterioso y se ve más o menos así:
... DEF(mov_i32, 1, 1, 0, TCG_OPF_NOT_PRESENT) DEF(movi_i32, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(setcond_i32, 1, 2, 1, 0) DEF(movcond_i32, 1, 4, 1, IMPL(TCG_TARGET_HAS_movcond_i32)) DEF(ld8u_i32, 1, 1, 1, 0) DEF(ld8s_i32, 1, 1, 1, 0) DEF(ld16u_i32, 1, 1, 1, 0) DEF(ld16s_i32, 1, 1, 1, 0) ...
De hecho, todo es muy simple: se usa así:
tcg / tcg.h:
typedef enum TCGOpcode { #define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, #include "tcg-opc.h" #undef DEF NB_OPS, } TCGOpcode;
Más o menos:
tcg / tcg-common.c:
TCGOpDef tcg_op_defs[] = { #define DEF(s, oargs, iargs, cargs, flags) \ { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, #include "tcg-opc.h" #undef DEF };
Incluso es extraño que en el curso de otros casos de uso no se haya encontrado. Y tenga en cuenta que, en este caso, no hay scripts complicados para la generación de código, solo C, solo hardcore.
Sabes
- ... que QEMU puede funcionar no solo en el modo de emulación de un sistema completo, sino también ejecutar un proceso separado para otra arquitectura que se comunica con el núcleo del host?
Java, JVM y todo-todo-todo
¿Qué soy todo acerca de Linux? Hablemos de algo multiplataforma. Sobre la JVM, por ejemplo. Bueno, sobre GraalVM, probablemente, muchos desarrolladores en este ecosistema ya lo han escuchado. Si no lo has escuchado, en pocas palabras: es épico. Entonces, después de hablar sobre Graal, pasemos a la buena JVM.
A veces, la JVM necesita detener todos los subprocesos administrados (la etapa de recolección de basura es muy pegadiza u otra cosa), pero el problema es que puede detener los subprocesos solo en los llamados puntos seguros. Como se describe aquí , una verificación normal de una variable global lleva mucho tiempo, incluido algún tipo de chamanismo con barreras de memoria. ¿Qué hicieron los desarrolladores? Se limitaron a una lectura variable.
Casi como en HQ9 +Existe un lenguaje cómico: HQ9 + . Fue creado como un "lenguaje de programación educativa muy conveniente", es decir, es muy simple realizar las tareas típicas que los estudiantes solicitan:
- el intérprete de comandos 'H' imprime Hello, World!
- en el comando 'Q' imprime el texto del programa en sí (quine)
- en '9' imprime la letra de 99 botellas de cerveza
- por 'i' incrementa la variable i en uno
- no puede hacer nada más, pero ¿por qué?
¿Cómo logra la JVM el objetivo con una sola instrucción? Pero es muy simple: si es necesario detenerse, elimina la pantalla de la página de memoria con esta variable: los hilos caen en SIGSEGV, y la JVM los estaciona y los detiene cuando finaliza el "mantenimiento". Recuerdo en StackOverflow cuando se le preguntó en una entrevista ¿Cómo se bloquea una JVM? respondió:
JNI De hecho, con JNI, el bloqueo es el modo de operación predeterminado. Tienes que trabajar muy duro para que no se bloquee.
Bromeando como una broma, y a veces en la JVM realmente lo es.
Bueno, como mencioné la generación de código en Scala, y ahora estamos hablando de este ecosistema, aquí hay un hecho interesante para usted: la generación de código en Scala (la que tiene macros) está estructurada de esta manera: usted escribe código en Scala usando la API compilador y compilarlo. Luego, en el siguiente inicio del compilador, simplemente pasa el generador de código resultante al classpath del compilador mismo, y ese, al ver una directiva especial, lo llama, pasando los árboles de sintaxis recibidos durante la llamada. En respuesta, recibe un AST, que debe ser sustituido en el lugar de la llamada.
Características de las ideologías de licencias.
Me gusta la ideología del software libre, pero también tiene algunas características divertidas.
Una vez, hace unos diez años, actualicé mi estable de Debian y, pensando en la sintaxis de algún comando, escribí habitualmente man <>
, que recibió una descripción exhaustiva como "[nombre del programa] es un programa con documentación distribuida bajo licencia GNU GFDL con secciones inmutables, que no está libre de DFSG ". Dicen que este programa fue escrito por algunos propietarios malvados de algunos FSF ... (Ahora la discusión es google).
Algunas distribuciones consideran que una biblioteca pequeña pero importante es un software no libre, porque el autor escribió a la licencia permisiva estándar que este programa debería usarse para el bien y no para el mal . La risa, la risa, y yo también, probablemente tendríamos miedo de tomar tal cosa en producción: nunca se sabe lo que el autor piensa sobre el bien y el mal.
Cualquier misceláneo
Características del compilador internacional durante la Ley Moore
Los duros desarrolladores de LLVM han limitado la alineación admitida:
La alineación máxima es 1 << 29.
Como dicen, te hace reír primero y luego pensar : el primer pensamiento, pero quién necesita alineación en 512 MiB. Luego leí sobre el desarrollo del kernel en Rust , y allí proponen hacer una estructura de "tabla de páginas" alineada a 4096 bytes. ¿Y cómo se lee Wikipedia, así que generalmente:
Una jerarquía de mapeo completa de páginas de 4 KB para todo el espacio de 48 bits requeriría un poco más de 512 GB de memoria (aproximadamente 0.195% del espacio virtual de 256 TB).
Una vez que decidí averiguar por qué la exportación no funciona en un programa, pero resulta que funciona ... ¿O no?
Después de haber iniciado los comandos de back-end manualmente, me di cuenta de que, en principio, todo está en orden, solo la versión debe transmitirse como "2.0", pero solo "2" se va. Anticipando una corrección trivial editando una constante de cadena, encuentro la función double getVersion()
, pero lo que es mayor, menor es, ¡incluso hay un punto! Sin embargo, al final, todo se decidió no mucho más complicado de lo esperado, yo solo mejor precisión de salida Reenvió el tipo de datos y reenvió las líneas.
Sobre la diferencia entre teóricos y practicantes
En mi opinión, en algún lugar de Habré ya vi una traducción de un artículo sobre cuáles son los bloqueos mínimos en el inicio, pero ¿aún es un programa compilado en C? int main;
- hay un símbolo main
y, técnicamente , puede transferirle el control. sirikid notó correctamente que incluso los bytes int
son superfluos aquí. En general, incluso hablando de un programa de 9 bytes de tamaño, es mejor no dispersar las afirmaciones de que es el más pequeño ... Es cierto, el programa se caerá, pero esto es completamente coherente con las reglas.
Entonces, sabemos cómo descartar lo que debería funcionar, pero ¿qué pasa con el lanzamiento de uno que no sea de lanzamiento?
$ ldd /bin/ls linux-vdso.so.1 (0x00007fff93ffa000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f0b27664000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0b2747a000) libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f0b27406000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f0b27400000) /lib64/ld-linux-x86-64.so.2 (0x00007f0b278e9000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f0b273df000) $ /lib/x86_64-linux-gnu/libc.so.6
... y libc él voz humana :
GNU C Library (Ubuntu GLIBC 2.28-0ubuntu1) stable release version 2.28. Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 8.2.0. libc ABIs: UNIQUE IFUNC ABSOLUTE For bug reporting instructions, please see: <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
Los programadores juegan golf
Hay un sitio completo en StackExchange dedicado a Code Golf: competiciones con el estilo de "Resolver este problema con una penalización mínima, dependiendo del tamaño del código fuente". El formato en sí implica soluciones muy sofisticadas, pero a veces se vuelven muy sofisticadas. Por lo tanto, en una de las preguntas , se recopiló una colección de lagunas prohibidas estándar. Me gusta especialmente este:
Usando MetaGolfScript
MetaGolfScript es una familia de lenguajes de programación. Por ejemplo, el programa vacío en MetaGolfScript-209180605381204854470575573749277224 imprime "Hello, World!".
En una linea
Finalmente, ¿de dónde viene el título del artículo? Este es un truco parafraseado de la salida del compilador emcc
de Emscripten :
$ emcc --help ... emcc: supported targets: llvm bitcode, javascript, NOT elf (autoconf likes to see elf above to enable shared object support)