Open source: humour de code, astuces de code, PAS de code

Old GLib vs New Clang


En fouillant dans un logiciel open source diversifié, je trouve périodiquement toutes sortes de choses intéressantes: parfois c'est juste un commentaire drôle, parfois c'est quelque chose d'esprit dans un sens plus large. Des collections similaires apparaissent périodiquement à la fois sur "l'Internet mondial" et sur Habré - par exemple, il y a une question bien connue sur StackOverflow concernant les commentaires dans le code, et une sélection de noms amusants d'entités juridiques et de toponymes a récemment été publiée ici. Je vais essayer de structurer et d'exposer ce que j'ai progressivement accumulé. Sous la coupe, des citations de QEMU, du noyau Linux et bien plus vous attendent.


Noyau Linux


Je pense que pour beaucoup, ce n'est un secret pour personne que les lettres de la liste de diffusion du noyau Linux divergent périodiquement entre guillemets. Examinons donc mieux le code. Et immédiatement, le système d'assemblage du noyau nous surprend: comme vous le savez, les projets construits par Autoconf ont un Makefile avec deux objectifs standard de nettoyage: clean et distclean . Naturellement, le noyau n'est pas construit en utilisant Autoconf, et ce que seul menuconfig vaut, donc il y a plus d'objectifs ici: clean , distclean et mrproper - oui, oui, Mr.Proper, un nettoyeur de noyau deux fois plus rapide .


En parlant du système de configuration: il était une fois j'ai été surpris quand je l'ai rencontré en plus de commandes claires comme allnoconfig , allyesconfig (je soupçonne que quelque chose de très débogage peut être compilé, donc maintenant je ne risquerais pas de le télécharger sur du vrai matériel .. .) et allmodconfig à la mystérieuse cible allrandconfig . «Sont-ils moqueurs», ai-je pensé, puis j'ai parlé à mon ami de cette observation, à laquelle il a répondu que c'était probablement une commande tout à fait significative, mais pas pour un assemblage réel, mais pour tester l'exactitude de l'arrangement des dépendances entre les options - comme je l'ai dit serait maintenant, une sorte de paramètres de configuration fuzzing.


Cependant, il y a de la vie au-delà du système d'assemblage: la documentation est parfois non seulement technique, mais aussi, d'une sorte, de valeur artistique. Supposons que vous souhaitiez alerter les utilisateurs du mode veille de sa fragilité et du risque de perte de données si certaines règles ne sont pas suivies. J'écrirais tristement, en disant ATTENTION: <remplacez quelques-unes des lignes les plus ennuyeuses> . Mais le développeur qui a écrit cela a fait quelque chose de différent:


 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] * * ... 

Petits trucs


Il n'est pas surprenant que tous les codes ne puissent pas être compilés avec des optimisations: lorsque j'ai essayé de les forcer à être activés pour tous les fichiers objet, j'ai naturellement rencontré une source d'entropie ou quelque chose de similaire que #error si l'optimisation était activée. Eh bien, la cryptographie est comme ça. Mais voulez-vous un code qui ne sera pas assemblé si vous désactivez toutes les optimisations, les alignements, etc.? Comment est-ce possible? Et ceci est une telle affirmation statique:


 /* SPDX-License-Identifier: GPL-2.0 */ // ... /* * This function doesn't exist, so you'll get a linker error if * something tries to do an invalidly-sized xchg(). */ extern void __xchg_called_with_bad_pointer(void); static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size) { unsigned long ret, flags; switch (size) { case 1: #ifdef __xchg_u8 return __xchg_u8(x, ptr); #else local_irq_save(flags); ret = *(volatile u8 *)ptr; *(volatile u8 *)ptr = x; local_irq_restore(flags); return ret; #endif /* __xchg_u8 */ // ... default: __xchg_called_with_bad_pointer(); return x; } } 

Il est supposé, apparemment, que pour toute utilisation avec un argument constant, cette fonction se développera en une seule branche de switch , et lorsqu'elle est utilisée avec un argument valide , cette branche ne sera pas default:
Sous une forme non optimisée, cette fonction entraînera une erreur de lien presque par conception ...


Tu sais


  • ... que le noyau possède un compilateur JIT de bytecode en mode utilisateur? Cette technologie est appelée eBPF et est utilisée pour le routage, le traçage et bien plus encore. Soit dit en passant, si vous n'avez pas peur des outils expérimentaux "nucléaires", regardez le paquet bpftools.
  • ... que le noyau peut durer environ cinq minutes de temps processeur? Il existe un tel appel système sendfile qui copie des octets d'un descripteur de fichier à un autre. Si vous lui dites le même descripteur et définissez le décalage correct dans le fichier, il rembobinera les mêmes données jusqu'à ce qu'il copie 2 Go.
  • ... qu'il existe une variante du travail d'hibernation effectué par le processus utilisateur - je ne serais pas surpris si vous pouvez également l'enregistrer sur le stockage réseau.

QEMU


En général, lorsque j'ai lu Robert Love sur le périphérique du noyau Linux, puis que j'ai grimpé dans les sources QEMU, j'ai eu un certain sentiment de déjà-vu. Il y avait des listes intégrées dans les structures par valeur (et non pas comme dans le cours de programmation initial qu'ils apprennent - à travers des pointeurs), et un certain sous-système RCU (ce que c'est, je ne comprenais toujours pas complètement, mais il existe également dans le noyau) et, probablement beaucoup plus similaire.


Quelle est la première chose qu'une personne soignée veut travailler sur un projet pour apprendre à connaître? Probablement avec le style de codage. Et déjà dans cela, on pourrait dire, cérémonial, document, nous voyons:


 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. 

Voici l'éternelle question sur la longueur de ligne maximale:


 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 ... Il est deux fois plus grand sur chaque axe que j'utilise parfois. Est-ce un tel Linux HD?)


Il y a encore beaucoup d'intéressant - lire .


Et encore des astuces


Ils disent que C est un langage de bas niveau. Mais s'il est bon d'être perverti, vous pouvez montrer les merveilles de la génération de code à la compilation sans Scala ni même C ++.


Par exemple, le fichier softmmu_template.h est softmmu_template.h dans la base de code QEMU. Quand j'ai vu ce nom, j'ai pensé qu'il était censé être copié dans mon implémentation de backend TCG et modifié jusqu'à ce que l'implémentation TLB correcte en sorte. Peu importe comment! Voici comment l'utiliser correctement :


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" 

Comme vous pouvez le voir, tour de passe-passe et pas de C ++. Mais ceci est un exemple assez simple. Et quelque chose de plus compliqué?


Il existe un tel fichier: tcg / tcg-opc.h . Son contenu est plutôt mystérieux et ressemble à ceci:


 ... 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)) /* load/store */ 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) ... 

En fait, tout est très simple - il est utilisé comme ceci:


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; 

Ou alors:


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 }; 

Il est même étrange qu'au cours d'autres cas d'utilisation il n'ait pas été trouvé. Et notez, dans ce cas, il n'y a pas de scripts délicats pour la génération de code - seulement C, seulement hardcore.


Tu sais


  • ... que QEMU peut fonctionner non seulement dans le mode d'émulation d'un système complet, mais également exécuter un processus distinct pour une autre architecture qui communique avec le noyau hôte?

Java, JVM et tout-tout


Que suis-je tout au sujet de Linux? Parlons de quelque chose de multiplateforme. À propos de la JVM, par exemple. Eh bien, à propos de GraalVM, probablement, de nombreux développeurs de cet écosystème ont déjà entendu parler. Si vous ne l'avez pas entendu, alors en bref: c'est épique. Donc, après avoir parlé de Graal, passons à la bonne vieille JVM.


Parfois, la machine virtuelle Java doit arrêter tous les threads gérés - l'étape de collecte des ordures est tellement accrocheuse ou autre - mais le problème est que vous ne pouvez arrêter les threads que sur les soi-disant points de sécurité. Comme décrit ici , une vérification normale d'une variable globale prend beaucoup de temps, y compris une sorte de chamanisme avec des barrières de mémoire. Qu'ont fait les développeurs? Ils se sont limités à une lecture variable.


Presque comme dans HQ9 +

Il y a un tel langage comique - HQ9 + . Il a été créé comme un "langage de programmation pédagogique très pratique", à savoir, il est très simple d'effectuer les tâches typiques que les étudiants demandent:


  • l'interpréteur de commandes «H» imprime Bonjour tout le monde!
  • à la commande 'Q' imprime le texte du programme lui-même (quine)
  • sur '9', il imprime les paroles de 99 bouteilles de bière
  • par «i», il incrémente la variable i d'une unité
  • il ne peut rien faire d'autre, mais pourquoi? ..

Comment la JVM atteint-elle cet objectif avec une seule instruction? Mais c'est très simple - s'il est nécessaire d'arrêter, il supprime l'affichage de la page mémoire avec cette variable - les threads tombent sur SIGSEGV, et la JVM les parcourt et les met en pause à la fin de la "maintenance". Je me souviens sur StackOverflow lorsqu'on lui a demandé lors d'une interview Comment planter une JVM? répondu:


JNI. En fait, avec JNI, le plantage est le mode de fonctionnement par défaut. Vous devez travailler très dur pour qu'il ne plante pas.

Blague comme une blague, et parfois dans la JVM, c'est vraiment le cas.


Eh bien, puisque j'ai mentionné la génération de code dans Scala, et nous ne parlons que de cet écosystème maintenant, voici un fait intéressant pour vous: la génération de code dans Scala (celle qui a des macros) est structurée comme ceci: vous écrivez du code dans Scala en utilisant l'API compilateur et compilez-le. Ensuite, au prochain démarrage du compilateur, vous passez simplement le générateur de code résultant au chemin de classe du compilateur lui-même, et celui-ci, en voyant une directive spéciale, l'appelle, en passant les arbres de syntaxe reçus pendant l'appel. En réponse, il reçoit un AST, qui doit être remplacé au lieu de l'appel.


Caractéristiques des idéologies de licence


J'aime l'idéologie du logiciel libre, mais il a aussi des fonctionnalités amusantes.


Il y a une dizaine d'années, j'ai mis à jour mon écurie Debian et, en pensant à la syntaxe d'une commande, j'ai typé man <> , qui a reçu une description exhaustive comme «[nom du programme] est un programme avec de la documentation distribuée sous licence GNU GFDL avec des sections immuables, qui n'est pas exempt de DFSG. " Ils disent que ce programme a été écrit par des propriétaires maléfiques de certains FSF ... (Maintenant, la discussion est google.)


Et certaines bibliothèques, petites mais importantes, sont considérées par certaines distributions comme des logiciels non libres, car l'auteur a écrit à la licence permissive standard que ce programme devrait être utilisé pour le bien et non pour le mal . Rires, rires, et moi aussi, j'aurais probablement peur de prendre une telle chose en production - on ne sait jamais ce que l'auteur pense du bien et du mal.


Divers


Caractéristiques de la construction de compilateurs internationaux sous la loi Moore


Les développeurs LLVM sévères ont limité l'alignement pris en charge:


L'alignement maximum est de 1 << 29.

Comme on dit, cela vous fait d'abord rire, puis réfléchir : la première pensée - mais qui a besoin d'un alignement à 512 Mio. Ensuite, j'ai lu sur le développement du noyau dans Rust , et là ils proposent de faire une structure de "page table" alignée sur 4096 octets. Et comment lisez-vous Wikipedia, donc généralement:


Une hiérarchie de mappage complète de pages de 4 Ko pour l'ensemble de l'espace 48 bits prendrait un peu plus de 512 Go de mémoire (environ 0,195% de l'espace virtuel de 256 To).

Format de la version - comment stocker?


Une fois, j'ai décidé de comprendre pourquoi l'exportation ne fonctionne pas dans un seul programme, mais cela fonctionne ... Ou pas?


Ayant démarré les commandes backend manuellement, je me suis rendu compte qu'en principe, tout est en ordre, seule la version doit être transmise en "2.0", mais juste "2" en laisse. Anticipant une correction triviale en éditant une constante de chaîne, je trouve la fonction double getVersion() - mais quoi, majeur est, mineur est, même il y a un point! Cependant, au final, tout a été décidé pas beaucoup plus compliqué que prévu, je précision de sortie améliorée Transféré le type de données et transféré les lignes.


À propos de la différence entre théoriciens et praticiens


À mon avis, quelque part sur Habré j'ai déjà vu une traduction d'un article sur ce que sont les plantages minimaux au démarrage, mais toujours un programme compilé en C? int main; - il y a un symbole main , et techniquement , vous pouvez lui transférer le contrôle. sirikid a correctement remarqué que même les octets int sont superflus ici. En général, même en parlant d'un programme de 9 octets, il vaut mieux ne pas disperser les affirmations selon lesquelles il est le plus petit ... Vrai, le programme va tomber, mais cela est tout à fait conforme aux règles.


Donc, nous savons comment supprimer ce qui devrait fonctionner, mais qu'en est-il du lancement d'un non-lancement?


 $ 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 

... et libérez-le voix humaine :


 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>. 

Les programmeurs jouent au golf


Il y a tout un site sur StackExchange consacré au Code Golf - des compétitions avec le style "Résoudre ce problème avec une pénalité minimale, selon la taille du code source." Le format lui-même implique des solutions très sophistiquées, mais parfois elles deviennent très sophistiquées. Par conséquent, dans l' une des questions , une collection d'échappatoires standard interdites a été collectée. J'aime particulièrement celui-ci:


Utilisation de MetaGolfScript
MetaGolfScript est une famille de langages de programmation. Par exemple, le programme vide dans MetaGolfScript-209180605381204854470575573749277224 imprime "Hello, World!".

En une seule ligne



Enfin, d'où vient le titre de l'article? Ceci est une astuce paraphrasée à partir de la sortie du compilateur emcc d' Emscripten :


 $ emcc --help ... emcc: supported targets: llvm bitcode, javascript, NOT elf (autoconf likes to see elf above to enable shared object support) 

Source: https://habr.com/ru/post/fr437832/


All Articles