nov
22
2009
2

Installez les drivers ATI sous Linux Mint (Gloria)

Linux Mint est une distribution Linux basée sur Ubuntu. La version utilisée dans cet article est Gloria qui n’est rien d’autre qu’une Ubuntu Jaunty (9.04).

Les drivers ATI propriétaires sont disponibles directement dans les dépôts Ubuntu/Mint mais à l’heure où j’écris ces lignes, le dit driver n’est pas compatible avec le serveur Xorg 1.6/1.7.

Nous allons donc downgrader (utiliser une version antérieure à la version actuellement installée) le serveur et installer la version disponible pour Ubuntu Intreprid (8.10). Ainsi nous bénéficierons d’une version fonctionnelle.

Voici donc la procédure à appliquer :

Sauvegarde du source.list qui contient la liste des dépôts que nous utilisons :

1
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

Modification du source.list pour remplacer les dépôts Jaunty par les dépôts Intrepid (temporairement) :

1
sudo nano /etc/apt/sources.list

Mise à jour de la liste des paquets disponibles afin de récupérer la liste des paquets pour Intrepid :

1
sudo aptitude update

Installation des paquets dont notre fameux serveur Xorg :

1
sudo aptitude install autoremove xserver-xorg gnome-session fast-user-switch-applet

Avant de réutiliser les dépôts Jaunty, nous devons bloquer un certains nombres de paquets afin qu’ils ne soient pas mis à jour automatiquement (lors d’un aptitude install ou via le Gestionnaire de paquets).

Pour cela, il faut ouvrir le Gestionnaire de paquets, sélectionnez les paquets dont le nom figure dans la liste ci-dessous, puis dans le menu Paquet, choisir Bloquer la version.

  • tous les paquets commençant par xserver-xorg*
  • tous les paquets commençant par fglrx*
  • xorg-driver-fglrx
  • libdrm2
  • gnome-session
  • fast-user-switch-applet

Restauration de notre vieux source.list :

1
sudo mv /etc/apt/sources.list.bak /etc/apt/sources.list

Sauvegardons la configuration de notre serveur Xorg :

1
sudo cp /etc/X11/xorg.conf /etc/X11/xorg.conf.OK

Utilisons l’utilitaire aticonfig pour générer une configuration correcte pour notre serveur X :

1
sudo aticonfig --initial -f

Il suffit maintenant de redémarrer et le driver utilisé sera fglrx (ATI) !

Vous pouvez maintenant jouer avec la 3D, avec Compiz par exemple…

Note: Vous pouvez aussi utiliser le driver libre radeonhd (xserver-xorg-video-radeonhd) qui est très performant et gère aussi la 3D mais qui n’est pas encore supporté par Compiz.

Share and Enjoy:
  • RSS
  • Fuzz
  • del.icio.us
  • Scoopeo
  • Facebook
  • Google Bookmarks
  • DiggFR.com
  • Blogasty
  • Digg France
  • blogmarks
  • BlogMemes Fr
  • Blogosphere News
  • Digg
  • LinkedIn
  • MisterWong Fr
  • StumbleUpon
  • Technorati
  • Tutmarks
  • TwitThis
Envoyez à un ami Envoyez à un ami
Written by Alternux in: Linux, Logiciels, Mint | Mots-clefs :, , , ,
mai
23
2009
3

Patchez un noyau Linux sans redémarrer !

Redémarrage dune machine sous Gentoo

Redémarrage d'une machine sous Gentoo

C’est le rêve de tout bon administrateur système! Imaginez, pouvoir enfin patcher et par la même occasion sécuriser un serveur dit critique, c’est à dire un serveur qui n’est redémarré qu’une fois tous les 10 ans. Régulièrement des failles découvertes dans le noyau linux sont exploitées de façon extrêmement simple par les pirates pour la simple et bonne raison que l’administrateur n’a pas pu appliquer un patch noyau qui nécessite un redémarrage de la machine.

Dans cet article nous allons donc patcher un noyau à chaud. Pour cela nous allons utiliser l’outil Ksplice développé par Waseem Daher durant ses études au MIT. Côté système, nous allons utiliser la distribution Ubuntu 9.04 Desktop 64 bits. Le fait que cette distribution soit 64 bits n’a aucune importance étant donné que Ksplice est disponible pour les architectures x86-32 et x86-64.

Préparation d’Ubuntu

Pour patcher le noyau encore faut-il disposer des sources! Ubuntu n’étant pas fourni avec les sources du noyau (par défaut), nous allons les récupérer grâce à aptitude. Mais tout d’abord récupérons la version du noyau qui tourne :

1
2
uname -a
= Linux ubuntu 2.6.28-11-generic

Recherchons les sources du noyau qui sont fournies par le paquet linux-source :

1
2
3
4
sudo aptitude search linux-source
= linux-source
= linux-source-2.6
= linux-source-2.6.28

Enfin, installons le bon paquet :

1
sudo aptitude install linux-source-2.6.28

Les sources sont maintenant disponibles dans le dossier /usr/src

Créons maintenant un répertoire de travail dans notre dossier home et copions-y les sources de notre noyau :

1
2
3
4
mkdir ~/ksplice_work
sudo cp /usr/src/linux-source-2.6.28.tar.bz2 ~/ksplice_work
cd ~/ksplice_work
chown $(whoami) linux-source-2.6.28.tar.bz2

Il ne nous reste plus qu’à les décompresser (dans notre dossier de travail) :

1
tar jxf linux-source-2.6.28.tar.bz2

Nous avons maintenant un dossier nommé linux-source-2.6.28 qui contient les sources du noyau qui tourne sur notre machine.

Installation de Ksplice

Ksplice n’est pas disponible dans les dépôts par défaut d’Ubuntu 9.04. Nous allons donc le compiler à partir des sources.

Sur le site officiel, nous lisons :

Installing the Ksplice utilities from the source tarball requires that your machine have the GNU BFD library, which is part of GNU Binutils. The GNU BFD  library is available in Debian’s binutils-dev package and is also available in other Linux distributions.

Nous allons donc installer le paquet binutils-dev. Nous avons également besoin d’outil patch :

1
2
sudo aptitude install binutils-dev
sudo aptitude install patch

Téléchargeons les sources (the source tarball) et décompressons les dans notre dossier de travail ~/ksplice_work :

1
tar xzf ksplice-0.9.7-src.tar.gz

Compilons et installons :

1
2
3
4
cd ksplice-0.9.7-src
./configure
make
sudo make install

Préparation du patch du noyau

Nous allons créer un dossier nommé ksplice dans le dossier qui contient les sources du noyau. Enfin nous avons besoin des fichiers .config et System.map qui se trouvent dans le dossier /boot de notre système. Il faut noter que certaines distributions fournissent également ces fichiers dans les sources du noyaux.

1
2
3
mkdir ~/ksplice_work/linux-source-2.6.28/ksplice
cp /boot/config-2.6.28-11-generic ~/ksplice_work/linux-source-2.6.28/ksplice/.config
cp /boot/System.map-2.6.28-11-generic ~/ksplice_work/linux-source-2.6.28/ksplice/System.map

Enfin il faut créer un lien symbolique vers les en-têtes du noyau (kernel headers) :

1
ln -s /lib/modules/2.6.28-11-generic/build ~/ksplice_work/linux-source-2.6.28/ksplice/build

Patch de la fonction printk

Nous allons patcher la fonction printk afin d’afficher le message « Hello world! » à chaque fois que celle-ci est appelée.

Voici le patch :

1
2
3
4
5
6
7
8
9
10
11
12
13
--- linux-2.6/kernel/printk.c   2008-06-28 22:26:15.000000000 -0400
+++ linux-2.6-new/kernel/printk.c       2008-06-29 00:19:16.000000000 -0400

@@ -609,6 +609,7 @@

va_list args;
int r;

+       vprintk("Hello world!\n", NULL);

va_start(args, fmt);
r = vprintk(fmt, args);
va_end(args);

Nous créons donc un fichier patch qui illustre cette modification. Vous trouverez le fichier patch ici.

Application du patch

Nous supposerons que nous avons enregistré le fichier .patch dans le dossier ksplice_work. Nous allons donc préparer le patch du noyau avec ksplice :

1
2
cd ~/ksplice_work
ksplice-create --patch=printk.patch ~/ksplice_work/linux-source-2.6.28

Cette opération risque de durer un petit moment…

Note : Sous Ubuntu, j’ai appliqué deux fixes : pour les chipset rt2860 et rt2870 et pour le fichier compile.h : solution provisoire à la fin de l’article (Note_FIX et Note_FIX2).

Et voici le résultat de la dernière commande :

1
2
3
4
5
6
7
8
Starting kernel builds (this process might take a long time)...
HOSTCC  scripts/basic/fixdep
HOSTCC  scripts/basic/docproc
HOSTCC  scripts/basic/hash
HOSTCC  scripts/kconfig/conf.o
...
make: Leaving directory `/usr/src/linux-headers-2.6.28-11-generic'
Ksplice update tarball written to ksplice-eczm2q7u.tar.gz

Et enfin, appliquons le patch :

1
sudo ksplice-apply ./ksplice-eczm2q7u.tar.gz

Résultat

Pour observer le résultat de la modification, il suffit de regarder le résultat de l’outil dmesg :

1
sudo dmesg | tail -n2

Ou encore :

1
2
3
sudo rmmod floppy
sudo modprobe floppy
sudo dmesg | tail -n2

La dernière commande affiche :

1
2
[18379.290811] Hello world!
[18379.290816] FDC 0 is a post-1991 82077

Ça marche !

Annuler la modification

Encore plus fort que le patch du noyau à chaud, l’annulation du patch du noyau !

Pour cela, nous allons utiliser l’outil ksplice-view qui liste les patchs appliqués avec ksplice :

1
sudo ksplice-view

Nous voyons donc l’identifiant du patch que nous venons d’appliquer, dans mon cas : eczm2q7u.

Pour avoir plus d’informations et pour être sûr qu’il s’agit du bon :

1
sudo ksplice-view --id=eczm2q7u

Et voici le retour :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Ksplice id eczm2q7u is present in the kernel and is applied.

Here is the source code patch associated with this update:

--- linux-2.6/kernel/printk.c   2008-06-28 22:26:15.000000000 -0400
+++ linux-2.6-new/kernel/printk.c       2008-06-29 00:19:16.000000000 -0400

@@ -609,6 +609,7 @@

va_list args;
int r;

+       vprintk("Hello world!\n", NULL);

va_start(args, fmt);
r = vprintk(fmt, args);
va_end(args);

Annulons le patch et observons que tout s’est bien déroulé :

1
2
3
sudo ksplice-undo eczm2q7u

sudo dmesg | tail -n1

Et le retour de la dernière commande :

1
[18509.258361] ksplice: Update eczm2q7u reversed successfully

Pour finir, sachez qu’il existe une autre solution baptisée Ginseng.

A vous de jouer !

Source : securityvibes.com qui m’a inspiré cet article. Le site officiel de Ksplice.

Note_FIX : Sous Ubuntu, je n’ai pas réussi à compiler le driver propriétaire pour les chipset rt2860 et rt2870 :

1
2
3
make[1]: *** No rule to make target `ubuntu/rt2860.o.KSPLICE', needed by `__ksplice'.  Stop.
make: *** [ubuntu] Error 2
Aborting: Prebuild failed at /usr/local/bin/ksplice-create line 188.

Et étant donné que je n’en ai pas besoin, je les ai supprimés à la compilation via le Makefile :

1
nano linux-source-2.6.28/ubuntu/Makefile

Commentez les lignes (ajoutez le caractère # devant chaque ligne comme montré ici) :

1
2
#obj-$(CONFIG_RT2860)            += ../drivers/staging/rt2860
#obj-$(CONFIG_RT2870)            += ../drivers/staging/rt2870

Note_FIX2 : Sous Ubuntu, je n’ai pas réussi à compiler kmod :

1
2
3
4
5
6
7
8
9
10
11
make: Entering directory `/usr/src/linux-headers-2.6.28-11-generic'
LD      /tmp/ksplice-tmp-Eh87HK/kmodsrc/built-in.o
CC      /tmp/ksplice-tmp-Eh87HK/kmodsrc/offsets.o
/tmp/ksplice-tmp-Eh87HK/kmodsrc/offsets.c:18:27: error: linux/compile.h: No such file or directory
/tmp/ksplice-tmp-Eh87HK/kmodsrc/offsets.c:192: error: ‘UTS_VERSION’ undeclared here (not in a function)
/tmp/ksplice-tmp-Eh87HK/kmodsrc/offsets.c:193: error: ‘UTS_MACHINE’ undeclared here (not in a function)
make[1]: *** [/tmp/ksplice-tmp-Eh87HK/kmodsrc/offsets.o] Error 1
make: *** [_module_/tmp/ksplice-tmp-Eh87HK/kmodsrc] Error 2
make: Leaving directory `/usr/src/linux-headers-2.6.28-11-generic'

Child exited with status 2
Failed during: make -rR -C ~/ksplice_work/linux-source-2.6.28/ksplice/build M=/tmp/ksplice-tmp-Eh87HK/kmodsrc KSPLICE_KID=wc3rm8wr KSPLICE_VERSION=0.9.7 map_printk=ffffffff8069becf KSPLICE_STANDALONE=1

Le fichier compile.h n’est pas fourni avec le paquet kernel-header mais il est founi par le paquet linux-kernel-source que nous avons installé. Il nous suffit donc d’ajouter ce fichier au kernel-header :

1
sudo cp ~/ksplice_work/linux-source-2.6.28/include/linux/compile.h /usr/src/linux-headers-2.6.28-11-generic/include/linux/

Attention : le dossier est bien linux-headers-2.6.28-11-generic : par défaut, il existe aussi un dossier de même nom sans le suffixe generic mais c’est le dossier -generic qui est utilisé pour la compilation.

Share and Enjoy:
  • RSS
  • Fuzz
  • del.icio.us
  • Scoopeo
  • Facebook
  • Google Bookmarks
  • DiggFR.com
  • Blogasty
  • Digg France
  • blogmarks
  • BlogMemes Fr
  • Blogosphere News
  • Digg
  • LinkedIn
  • MisterWong Fr
  • StumbleUpon
  • Technorati
  • Tutmarks
  • TwitThis
Envoyez à un ami Envoyez à un ami
Written by Alternux in: Linux | Mots-clefs :, , ,
mai
02
2009
3

Threading Building Blocks (TBB) et la programmation parallèle

Dans cet article, nous allons mettre en place un environnement de développement pour développer des applications en utilisant le parallèlisme. Pour ce faire, j’ai choisi Threading Building Blocks (TBB) qui est la librairie proposée par Intel.

Téléchargement de TBB

Rendons-nous sur le site officiel, rubrique « Download« .
Plusieurs versions s’offrent à nous. Nous allons récupérer la dernière version stable (Stable Release) mais rien ne vous empêche de récupérer une autre version, l’installation reste la même. La première version proposée est la dernière sortie.
Au moment où j’écris ces lignes il s’agit de la version 2.1 nommée : « tbb21_20080605oss ». Sur la nouvelle page, nous allons récupérer la version *lin.tgz.
Vous pouvez aussi récupérer les sources et les compiler sur votre machine (un simple make fait l’affaire).
Une fois récupéré, décompressons le fichier TGZ et préparons l’environnement de développement. Notez que nous allons utiliser la version Open Source, ceci peut avoir son importance vis-à-vis de la documentation.

Installation

TBB est composé de trois éléments :

  • deux fichiers binaires qui définissent l’environnement
  • les librairies (x86 et x64)
  • les header nécessaires

Commençons par les binaires. La documentation (PDF : Getting Started Guide) recommande d’installer les binaires dans le répertoire suivant :

1
/opt/intel/tbb/<version>/bin

Parmi les répertoires que vous avez obtenu après la décompression de l’archive, vous devriez voir em64t et ia32. Selon votre architecture vous choisirez em64t pour une architecture 64 bits ou ia32 pour une architecture 32 bits.
Dans ces répertoires vous trouverez plusieurs répertoires cc<version>_libc<version>_kernel<version>. Utilisez la version qui se rapproche le plus de la version de votre noyau. Si votre noyau est plus récent, utilisez simplement la dernière version. En cas de problème, tentez une compilation à partir des sources, cela devrait fonctionner. Pour récupérer la version de votre noyau :

1
uname -r

Vous voilà face à deux nouveaux répertoires, un répertoire bin/ qui contient les fameux binaires et un répertoire /lib qui contient nos librairies.
Copions donc les binaires dans le répertoires /opt/intel/tbb/<versions>/bin et attribuons leurs les droits en exécutions :

1
chmod u+x tbbvars.*

Enfin, exécutions le binaire approprié : tbbvars.sh pour bash et tbbvars.csh pour Csh.

Installons maintenant les librairies. Selon votre système, les librairies utilisateurs peuvent se trouver dans différents répertoires. Nous choisissons de les placer dans le répertoire /usr/lib/ et /usr/lib64 si vous travaillez sur une architecture 64 bits. Les librairies se trouvent dans le répertoire lib/ que nous avons trouvé tout à l’heure.
Ce répertoire contient différentes librairies : la librairie classique pour nos programmes finaux (release), la librairie debug pour le débogage et la librairie malloc. Nous allons utiliser la première librairie mais vous pouvez copier tous les fichiers.

Ca y est, nous sommes prêt à compiler notre premier programme !

J’oubliais, vous avez également besoin de g++, le compilateur C++ de GNU !

Premier programme

Nous allons utiliser le code test de l’article sur la programmation parallèle :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include "tbb/task_scheduler_init.h"
#include "tbb/parallel_for.h"
#include "tbb/blocked_range.h"

using namespace tbb;
using namespace std;

class Action {
float *const my_a;
public:
void operator()( const blocked_range<size_t>& r ) const {
float *a = my_a;
for( size_t i=r.begin(); i!=r.end(); ++i )
a[i]=1.;
}
Action( float a[] ) : my_a(a)
{}
};

void FonctionQuiBoucle( float a[], size_t n ) {
parallel_for(blocked_range<size_t>(0,n,10), Action(a) );
}

int main() {
task_scheduler_init init;

float a[3] = { 5., 2., 4. };

FonctionQuiBoucle(a, 3);

for(int i=0 ; i < 3 ; ++i)
cout << a[i] << " - ";

return 0;
}

Et pour compiler :

1
g++ main.c -o main -ltbb

Pour aller plus loin, je vous renvoie vers la documentation sur le site officiel de TBB. Vous y trouverez, notamment des exemples de codes. Enfin, je vous conseille vivement de lire le PDF « Tutorial », sur la page documentation (séction Open Source bien sûr!), il comporte les spécifications pour tous les éléments qui peuvent être utilisés pour la parallelisation de votre programme avec un exemple expliqué.

Share and Enjoy:
  • RSS
  • Fuzz
  • del.icio.us
  • Scoopeo
  • Facebook
  • Google Bookmarks
  • DiggFR.com
  • Blogasty
  • Digg France
  • blogmarks
  • BlogMemes Fr
  • Blogosphere News
  • Digg
  • LinkedIn
  • MisterWong Fr
  • StumbleUpon
  • Technorati
  • Tutmarks
  • TwitThis
Envoyez à un ami Envoyez à un ami
mai
01
2009
2

Programmation parallèle / multi-threads

Intel Quad Core Chopper

Intel "Quad Core" Chopper

Beaucoup de gens confondent la programmation avec la programmation multi-threads. Même si ces concepts sont proches, leur différence est très importante lors de la réalisation d’un projet qui utilise plusieurs processeurs (logiques ou physiques).

Avec l’arrivée des processeurs multi-coeurs, de nombreuses discussions fleurissent sur le sujet sur les forums et on peut souvent lire des erreurs liées à la confusion entre les deux termes.

La programmation multi-threads a pour but de séparer un programme en plusieurs threads plus ou moins indépendants. Plus ces threads sont indépendants, plus ont de chance d’être élus pour être exécutés sur différents processeurs. En effet, deux threads qui sont fortement liés ne peuvent pas être exécuté sur deux fils d’exécution différents. Le mot est lâche, les threads peuvent être exécutés différents processeurs mais ce n’est pas obligatoire. C’est la raison pour laquelle ils s’exécuteront parfaitement sur une architecture mono processeur. Rien ne garantit donc que les threads soient exécutés en même temps sur plusieurs processeurs (ou coeurs).

La programmation parallèle est aussi basée sur les threads mais force l’exécution des threads sur les différents processeurs. On a l’assurance d’utiliser au maximum nos processeurs.

Multi-threads

Multi-threads

Comment programme-t-on une application multi-threads ?

Il suffit de déclarer plusieurs threads dans votre programme. C’est le système d’exploitation qui affectera par la suite vos threads sur tel ou tel processeur. C’est là toute la différence avec la programmation parallèle.

La création de threads dans vos programmes doit être mûrement réfléchi car cela implique une augmentation de la compléxité et de la maintenabilité des programmes.

Voici un exemple de programme en C qui utilise les threads à travers la librairie pthread (Linux) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main (void) {
pthread_t filsA, filsB;

if (pthread_create(&filsA, NULL, affichage, "AA")) {
perror("pthread_create");
exit(EXIT_FAILURE);
}

if (pthread_create(&filsB, NULL, lire, "BB")) {
perror("pthread_create");
exit(EXIT_FAILURE);
}

if (pthread_join(filsA, NULL))
perror("pthread_join");

if (pthread_join(filsB, NULL))
perror("pthread_join");

printf("Fin du pere\n");
return (EXIT_SUCCESS);
}

Vous trouverez le programme complet (notamment la définition des fonctions) ici.

Brièvement, une fois le thread créé, celui-ci execute la fonciton qui lui est associée (ici : affichage ou lire).

Comment utilise-t-on la programmation parallèle ?

Il existe quelques bibliothèques qui permettent de faire de la programmation parallèle.
La plus simple, selon moi, est celle distribuée par openMP.
Son utilisation peut paraître un peu étrange au début, mais on s’y fait assez vite, d’autant que la documentation est bien fournie.

Voici un petit exemple d’utilisation, nous allons parallèliser une boucle FOR :

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
float a[3] = { 2., 4., 8. };

#pragma omp parallel for
for (i=0; i < 3; i++) /* i is private by default */
{
a[i] = 1.;
}

return 0;

}

Vous noterez que son utilisation est très simple.

Pour aller plus loin, je vous suggère d’aller lire les spécifications (Complete Specifications). Le PDF fournit des exemples très simples qui vous permettront d’avancer. Vous trouverez aussi un petit PDF mémento (Summary Card C/C++) très bien fait.

Toutefois, vous constaterez que cette bibliothèque a ses limites. Pour aller plus loin, vous pouvez vous tourner vers des bibliothèques plus fournies.

C’est le cas de la bibliothèque fourni par Intel. Elle est disponible depuis peu sous la license GPL et est très portable.
Son petit nom est TBB (Threading Building Blocks) et vous la trouverez sur le site officiel.

La documentation est remplie de petits tutoriels et d’exemples qui vous aideront à mieux maitriser cette librairie. Je vous conseille de lire le PDF Tutorial de la séction Open Source Documenation.

Vous constaterez tout de suite que cette librairie, bien que plus puissante que la précédente, est aussi bien plus complexe à utiliser. Reprenons notre exemple et adaptons le à notre nouvelle librairie :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include "tbb/task_scheduler_init.h"
#include "tbb/parallel_for.h"
#include "tbb/blocked_range.h"

using namespace tbb;
using namespace std;

class Action {
float *const my_a;
public:
void operator()( const blocked_range<size_t>& r ) const {
float *a = my_a;
for( size_t i=r.begin(); i!=r.end(); ++i )
a[i]=1.;
}
Action( float a[] ) : my_a(a)
{}
};

void FonctionQuiBoucle( float a[], size_t n ) {
parallel_for(blocked_range<size_t>(0,n,10), Action(a) );
}

int main() {
task_scheduler_init init;

float a[3] = { 5., 2., 4. };

FonctionQuiBoucle(a, 3);

for(int i=0 ; i < 3 ; ++i)
cout << a[i] << " - ";

return 0;
}

Notez que le code fourni est en C++ et non pas en C car on ne peut pas utiliser TBB avec du C.
Nous utilisons une classe pour effectuer des opérations sur notre tableau d’entiers.

Pour finir, sachez qu’il exsite d’autres librairies. La plupart sont portables, donc que des bonnes choses en perspective. Vous trouverez une petite liste sur ce blog.
Enfin, si vous êtres un fervent utilisateur du Framework .NET de Microsoft, il existe un excellent tutoriel sur developpez.com qui propose d’utiliser les « parallel extensions ».

Share and Enjoy:
  • RSS
  • Fuzz
  • del.icio.us
  • Scoopeo
  • Facebook
  • Google Bookmarks
  • DiggFR.com
  • Blogasty
  • Digg France
  • blogmarks
  • BlogMemes Fr
  • Blogosphere News
  • Digg
  • LinkedIn
  • MisterWong Fr
  • StumbleUpon
  • Technorati
  • Tutmarks
  • TwitThis
Envoyez à un ami Envoyez à un ami
mai
01
2009
3

Développer un programme 64 bits

AMD64

Tout d’abord, 64 bits, qu’est-ce que ça veut dire ?

Quand on parle de 64 bits, on parle en réalité d’une architecture et non pas de logiciels. Certes les logiciels compilés pour une architecture ne tournent que sur ces architectures mais se sont bien les architectures qui sont 64 bits. Je vous renvoie vers la définition de Wikipedia, courte mais claire. Vous l’aurez compris, les processeurs 64 bits, c’est pas nouveau. Mais alors pourquoi en parle-t-on aujourd’hui ?

La réponse est simple, ce n’est qu’aujourd’hui que nous pouvons exploiter pleinement les capacités du 64 bits. Je dis pleinement mais il faut savoir qu’elles sont sous sous-exploitées aujourd’hui.

En réalité, les développeurs semblent un peu perdu avec cette architecture et il n’est pas rare de lire sur internet des informations erronées. C’est encore pire chez nous, petits développeurs français, où les articles sont très peu nombreux. Il n’y a qu’à voir les résultats sur google : développer 64 bits
alors que nos amis anglophones ont des résultats bien plus pertinents : developping 64 bits

On notera toutefois une discussion intéressante sur developpez.com mais là encore de nombreuses informations fuses de tout côté sans que celles-ci ne soient vraiment vérifiées. Par contre, vous trouverez deux articles très intéressant sur le décodage d’instructions x86 x64.

Donc finalement, qu’est-ce que ça change ?

Pour des applications simples, la seule différence sera la possibilité d’utiliser plus de 4 Go de mémoire et le changement de taille des différents types de variables (int, long, etc.). Attention, ces nouvelles règles peuvent entraîner des soucis, d’où la bonne idée de programmer de façon stricte et propre, nous verrons ça à la fin.

Dans cet article, je vous propose de développer un petit programme en C et d’étudier deux versions, l’une compilée pour une architecture 32 bits, l’autre pour une architecture 64 bits. Enfin, nous finirons par quelques petites pistes pour aller plus loin dans le développement 64 bits.

Prenons ce petit main.c, tout simple :

1
2
3
int main() {
return 0;
}

Vous constatez que ce programme ne fait rien, mais il le fait bien. En réalité cela nous suffit pour voir quelques différences en les architectures.

Demandons à notre charmant compilateur de nous donner le code assembleur de ce programme :

1
gcc -s main.c

Etudions ce code :

1
more main.s

Voici le code que j’obtiens, vous devriez avoir sensiblement le même, du moins pour la partie qui nous intéresse (le main) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    .file    "main.c"
.text
.globl main
.type    main, @function
main:
.LFB2:
pushq    %rbp
.LCFI0:
movq    %rsp, %rbp
.LCFI1:
movl    $0, %eax
leave
ret
.LFE2:
.size    main, .-main
.section    .eh_frame,"a",@progbits
.Lframe1:
.long    .LECIE1-.LSCIE1
.LSCIE1:
.long    0x0
.byte    0x1
.string    "zR"
.uleb128 0x1
.sleb128 -8
.byte    0x10
.uleb128 0x1
.byte    0x3
.byte    0xc
.uleb128 0x7
.uleb128 0x8
.byte    0x90

La partie qui nous intéresse commence ligne 5 et termine ligne 13. Il s’agit de l’équivalent en assembleur du code en C que j’ai écrit plus haut.

Pour les initiés, vous noterez la présence de registres étranges, où sont EBP et ESP ? Ouf EAX est toujours là!
Pour les non-initiés, vous ne remarquez rien et vous vous posez quelques questions, détaillons donc ce code :

main:
=> nom de la fonction

pushq    %rbp
=> sauvegarde en haut de la pile, le registre RBP. RBP est l’équivalent 64 bits du registre EBP.

movq    %rsp, %rbp
=> RSP est l’équivalent 64 bits du registre ESP (i386). ESP est le pointeur de pile, l’adresse qu’il contient pointe sur le haut de la pile. On sauvegarde RSP dans RBP. Vous l’avez deviné, gcc veut construire un cadre de pile, étonnant non ?

movl    $0, %eax
=> on met la valeur 0 dans le registre EAX.

leave
=> restaure RSP à partir de RBP et dépile RBP.

ret
=> fin de la fonction main.

Vous voyez donc que GCC utilise les registres 64 bits, nous avons bien un programme 64 bits. Vous trouverez une petite liste des autres registres 64 bits sur developpez.com.
Notez que, juste pour me contredire, GCC utilise le registre EAX et non pas RAX. Ceci ne fait aucune différence bien entendue.

Demandons maintenant à GCC de nous donner le code assembleur générer pour une architecture 32 bits :

1
gcc -m32 -s main.c

Et voici le code assembleur :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    .file    "main.c"
.text
.globl main
.type    main, @function
main:
leal    4(%esp), %ecx
andl    $-16, %esp
pushl    -4(%ecx)
pushl    %ebp
movl    %esp, %ebp
pushl    %ecx
movl    $0, %eax
popl    %ecx
popl    %ebp
leal    -4(%ecx), %esp
ret
.size    main, .-main
.ident    "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section    .note.GNU-stack,"",@progbits

On peut noter que le code est sensiblement plus long. Ceci dit, le travail effectué est sensiblement le même. GCC constuit un cadre de pile, charge 0 dans le registre EAX ,restaure la pile et enfin quitte la fonction (ret).

Ce qui est intéressant, c’est l’utilisation des registres ESP et EBP, nos bons vieux registres x386.

En assembleur x64, vous pouvez aussi utiliset les registres ESP et EBP mais vous n’accéderez qu’au premier 32 bits des registres RSP et RBP, ce qui posera des problèmes dans le cas de la création d’un cadre de pile puisque le registre RSP contient une adresse codée sur 64 bits.

Notez que le programme 32 bits ne peut pas être compilé tel que sur une distribution Linux 64 bits. Nous avons besoin des headers 32 bits et nous devons indiquer à l’éditeur de liens où se trouvent ces headers pour qu’il puisse compiler notre programme. On parle alors de cross-compilation.

Pour faire une pierre de coup, voici le code assembleur obtenu en demandant une optimisation de niveau 3 à GCC :

1
gcc -S main.c -O3

Et le code :

1
2
3
main:
xorl    %eax, %eax
ret

L’instruction xorl %eax, %eax effectue l’opération XOR sur la valeur de deux registres, ce qui équivaut à mettre le registre EAX à 0. Notez que cette instruction est plus rapide que MOVL %eax,0.

Au final, pour un programme très très simple, vous pouvez noter que l’optimisation de niveau 3 n’est pas négligeable et simplifie beaucoup le code.

Pour finir, je vous propose un peu de lecture très intéressante :

Share and Enjoy:
  • RSS
  • Fuzz
  • del.icio.us
  • Scoopeo
  • Facebook
  • Google Bookmarks
  • DiggFR.com
  • Blogasty
  • Digg France
  • blogmarks
  • BlogMemes Fr
  • Blogosphere News
  • Digg
  • LinkedIn
  • MisterWong Fr
  • StumbleUpon
  • Technorati
  • Tutmarks
  • TwitThis
Envoyez à un ami Envoyez à un ami
Written by Alternux in: Développement | Mots-clefs :, , , , ,

Powered by WordPress | Aeros Theme | admin