Les rthreads sont la nouvelle implémentation visant à remplacer l'actuel système de thread sous OpenBSD.

1. Quelques informations

1.1 Les threads

Les threads (ou processus légers) donnent une illusion d'exécution parallèle (ou, la permet, dans le cas d'un système multi-processeurs). Ils sont similaires aux processus car ils représentent une file d'instructions à exécuter, néanmoins contrairement à eux ils ne possèdent pas de mémoire virtuelle propre, ils partagent celle du processus parent. Par contre ils ont leur propre stack (ou pile).
La norme POSIX définit une API : pthread, mais l'implémentation qu'il y aura derrière est propre au système d'exploitation.

Un système de thread se doit de fournir deux fonctionnalités essentielles :

1.2 Implémentation actuelle

A ce jour les threads sont exécutés en userland (aucun code dans le noyau) grâce à la libpthread (donc l'API + le code). Les limites sont donc simples à imaginer, un processus et tous ses threads sont vus comme un seul et même processus au niveau du système, ils partageront donc ce temps d'exécution.

Avantages :


Inconvenients :

1.3 Quelques commandes :

Après avoir lancé thunderbird depuis un terminal on peut très facilement avoir la liste de ses threads et de leurs états :

[20:11:23] dbd@OpenDbD:~ $ pkill -INFO thunderbird

[20:11:22] dbd@OpenDbD:~ $ thunderbird
 0x82ea8000 mutex_wait  15 -----W-d-f 0000
 0x7df25000 select_wait 15 -c--RW-d-f 0000
 0x87771800 cond_wait   15 -c-C-W-d-f 0000
 0x86f28000 cond_wait   15 -c-C-W---f 0000
 0x86a15400 cond_wait   15 -c-C-W---f 0000
 0x82d83400 cond_wait   15 -c-C-W-d-f 0000
 0x89b9b000 cond_wait   15 -c-C-W-d-f 0000
 0x887dd400 cond_wait   15 -c-C-W-d-f 0000
 0x7e0bf800 cond_wait   15 -c-C-W---f 0000
 0x8008b800 poll_wait   15 -c--RW---f 0000
 0x81bec000 poll_wait   15 -c--RW---f 0000 main

1.4 La solution choisie

Une solution simple et efficace appellée “Light Thread Implementation” ou “Direct 1:1 Mapping” consiste a créer un processus par thread. Sous OpenBSD l'appel système rfork est déjà implémenté (il permet de créer des processus avec un espace mémoire partagé), il convient donc très bien.

2. Niveau kernel

2.1 Les appels système

src/sys/kern/syscalls.master

#ifdef RTHREADS
299     STD             { pid_t sys_getthrid(void); }
300     STD             { int sys_thrsleep(void *ident, int timeout, void *lock); }
301     STD             { int sys_thrwakeup(void *ident, int n); }
302     STD             { int sys_threxit(int rval); }
303     STD             { int sys_thrsigdivert(sigset_t sigmask); }
#else
299     UNIMPL
300     UNIMPL
301     UNIMPL
302     UNIMPL
303     UNIMPL
#endif

Nous voyons clairement que 5 appels système ont été ajoutés.

2.2 Le code de rfork()

src/sys/kern/kern_fork.c

int
sys_rfork(struct proc *p, void *v, register_t *retval)
{
        struct sys_rfork_args /* {
                syscallarg(int) flags;
        } */ *uap = v;

        int rforkflags;
        int flags;

        flags = FORK_RFORK;
        rforkflags = SCARG(uap, flags);

        if ((rforkflags & RFPROC) == 0)
                return (EINVAL);

        switch(rforkflags & (RFFDG|RFCFDG)) {
        case (RFFDG|RFCFDG):
                return EINVAL;
        case RFCFDG:
                flags |= FORK_CLEANFILES;
                break;
        case RFFDG:
                break;
        default:
                flags |= FORK_SHAREFILES;
                break;
        }

        if (rforkflags & RFNOWAIT)
                flags |= FORK_NOZOMBIE;

        if (rforkflags & RFMEM)
                flags |= FORK_SHAREVM;
#ifdef RTHREADS
        if (rforkflags & RFTHREAD)
                flags |= FORK_THREAD;
#endif

        return (fork1(p, SIGCHLD, flags, NULL, 0, NULL, NULL, retval, NULL));
}

Nous avons vu que rfork() été utilisé pour la création du thread. Ainsi, un flag a été ajouté : FORK_THREAD pour spécifier au noyau que ce nouveau processus fait partie du processus parent.

3. La librthread

3.1 L'interface

Nous avons vu que pthread était une API, la librthread implémente donc ces fonctions et la manipulation est totalement transparente pour l'utilisateur.

3.2 Compatible

Il faut aussi savoir quelles sont “Binary Compatible”, c'est à dire que l'on peut remplacer la librairie pthread actuelle par rthread sans recompiler tout l'userland.

4. Test et état actuel

4.1 Activation dans le noyau

Ajout de “option RTHREADS” dans GENERIC puis recompilation de celui-ci

# cd /usr/src/sys/arch/`machine`/conf
# config GENERIC
# cd ../compile/GENERIC
# make clean && make depend && make
    [...Pas mal d'affichage...]
# make install

On redémarre sur le nouveau noyau et on controle :

$ grep getthrid /bsd
Binary file /bsd matches

Si vous obtenez ce message c'est que le noyau supporte l'appel système getthrid donc qu'il est bien compilé avec les rthreads.

4.2 Compilation et reconfiguration de ldd

Compilation :

# cd /usr/src/lib/librthread && make obj && make && make install

On crée un lien dans /usr/lib de libpthread.so.x.y (y actuel+1) vers librthread.so.x.y, dans mon cas :

# cd /usr/lib && ln -s librthread.so.1.0 libpthread.so.9.1

Puis on lance ldconfig pour que ce soit pris en compte :

# ldconfig /usr/lib /usr/local/lib /usr/X11R6/lib

Si vous souhaitez désactiver les rthreads rien de plus simple :

# rm /usr/lib/libpthread.so.9.1 && ldconfig

4.3 Résultats

/ A faire /

Remerciements

Bibliographie