Skip to content

Linux Attitude

Le libre est un état d'esprit

Archive

Tag: Code

Niveau :      
Résumé :

Aujourd'hui un thème un peu polémique mais pas trop quand même.

Le XML est un descendant du SGML. C'est un format structuré de données comme l'est l'ASN1 ou le LDIF.

Tous ces formats ont été inventés dans une seule optique : être facile à parser par une machine. Et pourtant quand on voit les subtilités du XML et les difficultés à faire un parseur, on peut avoir des doutes... Mais de ce fait ils sont peu lisibles.

Ne vous méprenez pas, le XML a tout à fait sa place dans les échange de données, dans la communication entre machines. Le format est bien défini et fonctionne quel que soit le type de machine. Il est d'ailleurs très utilisé car on trouve des parseurs partout, dans presque tous les langages, et dans presque tous les frameworks.

Mais à force d'en user on en abuse.

Non le XML n'est PAS fait pour les humains ! Le xml n'est pas à sa place dans les fichiers de configuration, à moins que vous n'ayez pour objectif que l'utilisateur ne touche jamais à ces fichiers et que vous ayez le temps de développer une interface entre l'utilisateur et la modification (texte ? graphique ? sonore ?).

Le XML est pratique car il permet au développeur d'éviter l'usage de protocoles binaires incompréhensibles et mal parsés entre deux applications. Il est pratique car un autre développeur peut lire ce protocole sans l'avoir appris. Mais vous remarquerez que je ne parle que de développeurs, pas d'utilisateurs.

Alors par pitié arrêtez le XML dans les fichiers destinés aux HUMAINS. Il existe bien d'autres formats ayant des parseurs et tout aussi lisibles :

Mieux, il existe LUA qui vous permet à la fois d'avoir un fichier de configuration simple et lisible, mais qui en plus autorise l'utilisateur à faire des choses que vous n'auriez pas prévu, comme des configuration conditionnelles !

Niveau :      
Résumé :

Maintenant que nous savons nous servir d'un interpréteur postscript, nous allons pouvoir développer. Postscript est un langage complet au sens de Turing. C'est à dire qu'on peut tout faire avec, même faire décoller une fusée. Mais bon, avant d'en arriver là, on va essayer de faire quelque chose d'utile.

Pour vous présenter le langage, je vous propose un exemple basé sur la fourmi de Langton. C'est une fourmi qui a ceci de particulier qu'elle ne sait que tourner. Son univers ressemble beaucoup à celui du jeu de la vie, c'est-à-dire une grille infinie de cases noires ou blanches. Lorsque la fourmi est sur une case noire, elle tourne à droite, lorsqu'elle est sur une case blanche, elle tourne à gauche. De plus elle inverse la couleur de la case sur laquelle elle se trouve.

Le concept est assez simple, et lorsqu'on lance la fourmi sur une grille blanche, son comportement a l'air aléatoire. Et pourtant au bout d'un moment elle fabrique ce qui ressemble à une autoroute.

Tout ça pour dire que le postscript c'est bien. C'est un langage à pile, qui fonctionne en notation inversée. Les possesseurs de HP48 et amateurs de RPL y retrouveront leurs petits (ou de vieux souvenirs). Les développeurs forth (faite sous savoir si vous êtes dans la salle) apprécieront aussi probablement.

La base

Les commentaires

Toute ligne commençant par un % est un commentaire. Exemple :

% Rien

Les commandes

Les commandes sont des mots simples. Étant donné la notation inversée, la commande se trouve après ses arguments. Par exemple une addition :

5 9 add

A cette notation est associée une pile. 5 pose 5 sur la pile, 9 pose 9 sur la pile, add retire les 2 derniers éléments (donc 9 et 5) puis additionne et repose le résultat sur la pile. D'où la représentation de la pile :

    %avant la ligne de commande
5   % 5 
5 9 % 9
    %pendant le add (invisible)
14  % après le add 

Les commandes peuvent être séparées par des espaces ou des retours à la ligne.


continue reading...

Niveau :      
Résumé : perl

Quelques trucs et astuces de perlistes en vrac. Pour des vrais astuces de pro régulières, n'hésitez pas à visiter le site des moines de perl.

Initialiser un tableau de chaînes sans avec une syntaxe légère, très lisible quand on n'a que des mots, le séparateur étant les caractères blancs, incluant le retour à la ligne :

@tableau = qw( mot1 mot2 mot3 );

Passer du mode script de quelques lignes au mode développement long. Cela vous oblige à déclarer et initialiser toutes vos variables entre autre :

use strict;

Le debug facile :

use Data::Dumper;
# variable peut être n'importe quoi, toutes les sous-sous parties sont écrites :
print Dumper($variable,\@variable);
print Dumper(\%variable);

L'aide en ligne facile, ça fait tout de suite classe dans un script (format) :

use Pod::Usage;
# imprime le contenu de SYNOPSIS et termine le programme avec un code d'erreur 1
pod2usage(1);
# attention, pod est chatouilleux sur les retours à la ligne avant et après les =

=head1 SYNOPSIS

commande  [-o|--option=<valeur>]

=cut

La page de manuel facile (pas seulement le SYNOPSIS comme précédemment) :

# où fichier.pl est le code précédent
$ pod2man fichier.pl > fichier.1
$ man ./fichier.1

Et le dernier pour la route, le parsing d'options facile, très classe, surtout en combinaison avec pod2usage (format) :

use Getopt::Long;
my $options = {
        option => 'defaut'
};
GetOptions( $options, qw(
        option|o=s
));

Niveau :      
Résumé : SSE

Aujourd'hui l'avenir (et un peu le passé aussi). Vous souvenez-vous des cray ? Ces machines surpuissantes qui avaient au moins la puissance de calcul d'une TI92. Comment faisaient-ils pour avoir une telle puissance ? Il faisaient des calculs identiques en parallèle et multipliaient ainsi par n le nombre d'opérations. C'est ce qu'on appelle une architecture vectorielle.

Intel dans sa grande bonté a commencé à mettre en place une telle architecture dans ses processeurs. Tout d'abord avec les instructions MMX. Ces instructions utilisent les registres servant au calcul en virgule flottante (quelques transistors de gagnés), lesquels font 64 bits. On peut ainsi exécuter des instructions identiques simultanément sur 4 fois 1 octets ou sur 2 fois 2 octets. Ce qui est bien, mais pas top.

Le SSE est une amélioration du concept. Tout d'abord on utilise de nouveaux registres (ce qui libère les calculs en virgule flottante) et on leur donne une taille de 128bits. Du coup on peut manipuler deux fois plus d'entiers et cela devient intéressant pour les flottants. On peut caser 4 flottants simple précision et 2 flottants double précision dans 128 bits. Le SSE s'arrête aux 4 flottants et le SSE2 ajoute quasiment toutes les autres combinaisons possibles (flottants et entiers).

Version normale

Comme nous sommes joueurs, nous allons jouer à améliorer notre code de calcul de Mandelbrot. Mais cette fois, nous allons le laisser tourner sous linux. Comme ne sommes plus dans notre monde, mais sur un vrai système, nous allons devoir faire quelques modifications. Nous allons continuer à écrire directement en mémoire, donc on garde nos fonctions d'écriture de pixels, mais cette fois dans une mémoire allouée avec malloc. Ajoutons le support de l'écriture d'un des format d'image les plus simples, le BMP, pour stocker notre image dans un fichier et pouvoir admirer le résultat. Pour cela on prend la première documentation venue.

/* Ecriture de l'image dans un fichier bitmap (on écrit l'en-tête kivabien puis les données) */
void writebitmap(char* filename, char*bitmap, int width, int height);

Et voilà, cela nous donne une première version du code quasiment identique à la précédente et donc non optimisée pour le SSE (disponible à la fin de l'article).


continue reading...

Niveau :      
Résumé : grub ; noyau ; multiboot ; mandelbrot

Suite à de précédentes bidouilles, on veut faire mieux.

Multiboot

Donc grub peut booter plusieurs formats, l'un d'entre eux est le multiboot, un format de fichier bootable défini par les développeurs de grub espérant qu'il soit adopté par différentes distributions. Il souffre d'un certain nombre de faiblesses et n'est pas encore vraiment un standard. D'autant que grub lui-même ne supporte pas encore complètement la spécification pourtant très courte. Si vous ne voulez pas lire toute la spécification, allez directement au chapitre 4 où on trouve un code d'exemple prêt à compiler pour vous lancer dans le développement.

Vous trouverez un autre exemple chez quelqu'un qui s'est amusé à coder un space invaders bootable directement à partir de la spécification multiboot.

Alors c'est parti, reprenons notre projet précédent. Faites attention, le processeur est dans un état particulier lorsqu'on vous laisse la main (lisez le chapitre 3.2), à vous de vous en accommoder.

Mode graphique

Tout d'abord on voudrait changer le mode graphique, et si possible pour mieux que la première fois. Pour cela, on va utiliser le standard vesa 2.0 qui standardise des modes avec une plus grande définition et qui ajoute de nouvelles fonctions au bios pour manipuler la carte.

Choisissons le mode 0x118 (1024x768x24) avec 16 millions de couleurs. Plus besoin de gérer un palette puisqu'on écrit directement les couleurs RGB à utiliser pour le pixel. On aura tout de même une fonction de palette puisqu'on n'utilise qu'un petit nombre de couleurs placées sur une échelle.

Le problème c'est que vesa n'a pas prévu qu'on puisse appeler les fonctions de la carte en mode protégé sans passer au moins une fois par une interruption en mode réel. Multiboot est bien gentil, il a prévu le cas, malheureusement, grub n'implémente pas les fonctions qu'il devrait pour le faire pour nous. Un patch est disponible pour ceux qui voudrait quand même cette fonctionnalité.


continue reading...

Niveau :      
Résumé : grub ; noyau ; mandelbrot

Vous souvenez-vous du bon vieux temps ? Je vous parle du temps de l'autoexec.bat et des driver de quelques ko, le temps où on écrivait directement en mémoire vidéo, le temps de l'assembleur, du mode réel et des interruptions.

Hé bien cette époque bien que révolue nous a laissé des traces. Il est toujours possible de coder sans noyau, de faire son propre noyau, de se passer de système d'exploitation ou de booter directement sur une application en quelques secondes.

Boot loader

Choisissons un bootloader, grub par exemple, nous allons lui demander de lancer notre programme. Avec grub il existe plusieurs méthodes pour charger un os, entre autres :

  • Chainloader charge 512 octets, met le processeur en mode en mode réel, tel qu'il serait après le passage du bios. Pratique pour partir de zéro, mais 512 octet c'est un peu petit et nous n'avons pas vraiment envie de gérer le chargement de fichier sur le disque en plus.
  • Linux charge un noyau au format zimage ou bzimage et lui passe le contrôle en mode réel 16 bits. Il est possible de charger des fichiers de plusieurs méga avec le format bzimage.
  • Multiboot charge un noyau au format multiboot et lui passe le contrôle en mode protégé 32 bits après un certain nombre d'initialisations. On utilisera cette méthode une prochaine fois.

zImage

On commence par une solution simple à mettre en place, un exécutable au format zImage. Le format est décrit dans la documentation des sources du noyau linux : /usr/src/linux-2.X.XX/Documentation/i386/boot.txt. C'est dans ce format que nous allons développer un binaire bootable directement.

Le bootloader (grub) lit l'en-tête du fichier zImage puis pose la première section (attention, taille limitée à quelques dizaines de ko) à l'adresse 0x90000, Ensuite, il lit le reste du fichier et le place en 0x1000 (pour une zImage, limitée à 512ko) ou en 0x100000 (pour une bzImage). Ensuite, dans le cas de linux, le noyau s'amuse à redéplacer tout ça selon un processus plus ou moins compliqué décrit ici et ici. Attention, ne prenez pas ces documents pour argent comptant, ils sont un peu dépassés. En pratique nous n'irons pas si loin puisque nous resterons en mode réel et que 512ko nous seront largement suffisant (640Ko ça devrait suffire pour tout le monde !).

Nous allons donc faire simple et récupérer le code de boot de linux pour lancer notre petit projet. Le fichier qui contient le code de lancement ainsi que les en-têtes qui vont bien s'appelle header.S.

Attention, il est écrit en assembleur GNU (gas), celui-ci diffère beaucoup de l'assembleur intel. Le point le plus important est que les arguments sont inversés (source puis destination). header.S Apportons quelques modifications au fichier pour qu'il corresponde à nos besoins :


continue reading...