NOM
pthreads - Threads POSIX
POSIX.1 décrit une série d’interfaces (fonctions et fichiers
d’en‐têtes) pour la programmation threadée, couramment appelée threads
POSIX, ou pthreads. Un unique processus peut contenir plusieurs
threads, qui exécutent tous le même programme. Ces threads partagent la
même mémoire globale (segments de données et tas), mais chaque thread a
sa propre pile (variables automatiques).
POSIX.1 requiert aussi que les threads partagent une série d’autres
attributs (ces attributs sont par processus, plutôt que par thread) :
- identifiant de processus (PID)
- identifiant de processus père (PPID)
- identifiant de groupe de processus (PGID) et identifiant de session
(SID)
- terminal de contrôle
- identifiants d’utilisateur et de groupe
- descripteurs de fichier ouverts
- verrouillages d’enregistrements (voir fcntl(2))
- gestion de signaux
- masque de création de fichier (umask(2))
- répertoire de travail (chdir(2)) et répertoire racine (chroot(2))
- temporisations d’intervalle (setitimer(2)) et temporisations POSIX
(timer_create(2))
- valeur de politesse (setpriority(2))
- limites de ressources (setrlimit(2))
- mesures de consommation de temps CPU (times(2)) et de ressources
(getrusage(2))
En plus de la pile, POSIX.1 indique que plusieurs autres attributs sont
distincts pour chaque thread, dont les suivants :
- identifiant de thread (le type de donnée pthread_t)
- masque de signaux (pthread_sigmask(3))
- la variable errno
- pile spécifique de signal (sigaltstack(2))
- politique et priorité d’ordonnancement temps réel
(sched_setscheduler(2) et sched_setparam(2))
Les caractéristiques spécifiques Linux suivantes sont également
distinctes pour chaque thread :
- capacités (voir capabilities(7))
- affinité CPU (sched_setaffinity(2))
Valeurs de retour des fonctions pthreads
La plupart des fonctions pthreads renvoient 0 en cas de succès et un
numéro d’erreur en cas d’échec. Notez que les fonctions pthreads ne
positionnent pas errno. Pour chacune des fonctions pthreads qui peuvent
produire une erreur, POSIX.1-2001 spécifie que la fonction ne peut pas
échouer avec l’erreur EINTR.
Identifiants de thread
Chacun des threads d’un processus a un unique identifiant de thread
(stocké dans le type pthread_t). Cet identifiant est renvoyé à
l’appelant de pthread_create>(3) et un thread peut obtenir son propre
identifiant de thread en utilisant pthread_self(3). Les identifiants de
thread n’ont la garantie d’être uniques qu’à l’intérieur d’un
processus. Un identifiant de thread peut être réutilisé après qu’un
thread qui s’est terminé ait été rejoint ou qu’un thread détaché se
soit terminé. Pour toutes les fonctions qui acceptent un identifiant de
thread en paramètre, cet identifiant de thread se réfère par définition
à un thread du même processus que l’appelant.
Fonctions sûres du point de vue des threads
Une fonction sûre du point de vue des threads est une fonction qui peut
être appelée en toute sûreté (c’est-à-dire qu’elle renverra le même
résultat d’où qu’elle soit appelée) par plusieurs threads en même
temps.
POSIX.1-2001 et POSIX.1-2008 exigent que toutes les fonctions indiquées
dans la norme soit sûres du point de vue des threads, excepté les
fonctions suivantes :
asctime()
basename()
catgets()
crypt()
ctermid() avec un paramètre non NULL
ctime()
dbm_clearerr()
dbm_close()
dbm_delete()
dbm_error()
dbm_fetch()
dbm_firstkey()
dbm_nextkey()
dbm_open()
dbm_store()
dirname()
dlerror()
drand48()
ecvt() [POSIX.1-2001 uniquement (fonction supprimée dans POSIX.1-2008)]
encrypt()
endgrent()
endpwent()
endutxent()
fcvt() [POSIX.1-2001 uniquement (fonction supprimée dans POSIX.1-2008)]
ftw()
gcvt() [POSIX.1-2001 uniquement (fonction supprimée dans POSIX.1-2008)]
getc_unlocked()
getchar_unlocked()
getdate()
getenv()
getgrent()
getgrgid()
getgrnam()
gethostbyaddr() [POSIX.1-2001 uniquement (fonction supprimée dans POSIX.1-2008)]
gethostbyname() [POSIX.1-2001 uniquement (fonction supprimée dans POSIX.1-2008)]
gethostent()
getlogin()
getnetbyaddr()
getnetbyname()
getnetent()
getopt()
getprotobyname()
getprotobynumber()
getprotoent()
getpwent()
getpwnam()
getpwuid()
getservbyname()
getservbyport()
getservent()
getutxent()
getutxid()
getutxline()
gmtime()
hcreate()
hdestroy()
hsearch()
inet_ntoa()
l64a()
lgamma()
lgammaf()
lgammal()
localeconv()
localtime()
lrand48()
mrand48()
nftw()
nl_langinfo()
ptsname()
putc_unlocked()
putchar_unlocked()
putenv()
pututxline()
rand()
readdir()
setenv()
setgrent()
setkey()
setpwent()
setutxent()
strerror()
strsignal() [Ajoutée dans POSIX.1-2008]
strtok()
system() [Ajoutée dans POSIX.1-2008]
tmpnam() avec un paramètre non NULL
ttyname()
unsetenv()
wcrtomb() si son dernier paramètre est NULL
wcsrtombs() si son dernier paramètre est NULL
wcstombs()
wctomb()
Points d’annulation
POSIX.1 spécifie que certaines fonctions doivent, et certaines autres
fonctions peuvent, être des points d’annulation. Si un thread est
annulable, que sont type d’annulation est avec un report (« deferred »)
et qu’une demande d’annulation est en cours pour ce thread, alors le
thread est annulé quand il appelle une fonction qui est un point
d’annulation.
POSIX.1-2001 et/ou POSIX.1-2008 exigent que les fonctions suivantes
soient des points d’annulation :
accept()
aio_suspend()
clock_nanosleep()
close()
connect()
creat()
fcntl() F_SETLKW
fdatasync()
fsync()
getmsg()
getpmsg()
lockf() F_LOCK
mq_receive()
mq_send()
mq_timedreceive()
mq_timedsend()
msgrcv()
msgsnd()
msync()
nanosleep()
open()
openat() [Ajoutée dans POSIX.1-2008]
pause()
poll()
pread()
pselect()
pthread_cond_timedwait()
pthread_cond_wait()
pthread_join()
pthread_testcancel()
putmsg()
putpmsg()
pwrite()
read()
readv()
recv()
recvfrom()
recvmsg()
select()
sem_timedwait()
sem_wait()
send()
sendmsg()
sendto()
sigpause() [POSIX.1-2001 uniquement (dans la liste des fonction pouvant être un point d’annulation dans POSIX.1-2008)]
sigsuspend()
sigtimedwait()
sigwait()
sigwaitinfo()
sleep()
system()
tcdrain()
usleep() [POSIX.1-2001 uniquement (fonction supprimée dans POSIX.1-2008)]
wait()
waitid()
waitpid()
write()
writev()
POSIX.1-2001 et/ou POSIX.1-2008 indiquent que les fonctions suivantes
peuvent être des points d’anulation :
access()
asctime()
asctime_r()
catclose()
catgets()
catopen()
chmod() [Ajoutée dans POSIX.1-2008]
chown() [Ajoutée dans POSIX.1-2008]
closedir()
closelog()
ctermid()
ctime()
ctime_r()
dbm_close()
dbm_delete()
dbm_fetch()
dbm_nextkey()
dbm_open()
dbm_store()
dlclose()
dlopen()
dprintf() [Ajoutée dans POSIX.1-2008]
endgrent()
endhostent()
endnetent()
endprotoent()
endpwent()
endservent()
endutxent()
faccessat() [Ajoutée dans POSIX.1-2008]
fchmod() [Ajoutée dans POSIX.1-2008]
fchmodat() [Ajoutée dans POSIX.1-2008]
fchown() [Ajoutée dans POSIX.1-2008]
fchownat() [Ajoutée dans POSIX.1-2008]
fclose()
fcntl() (for any value of cmd argument)
fflush()
fgetc()
fgetpos()
fgets()
fgetwc()
fgetws()
fmtmsg()
fopen()
fpathconf()
fprintf()
fputc()
fputs()
fputwc()
fputws()
fread()
freopen()
fscanf()
fseek()
fseeko()
fsetpos()
fstat()
fstatat() [Ajoutée dans POSIX.1-2008]
ftell()
ftello()
ftw()
futimens() [Ajoutée dans POSIX.1-2008]
fwprintf()
fwrite()
fwscanf()
getaddrinfo()
getc()
getc_unlocked()
getchar()
getchar_unlocked()
getcwd()
getdate()
getdelim() [Ajoutée dans POSIX.1-2008]
getgrent()
getgrgid()
getgrgid_r()
getgrnam()
getgrnam_r()
gethostbyaddr() [SUSv3 uniquement (fonction supprimée dans POSIX.1-2008)]
gethostbyname() [SUSv3 uniquement (fonction supprimée dans POSIX.1-2008)]
gethostent()
gethostid()
gethostname()
getline() [Ajoutée dans POSIX.1-2008]
getlogin()
getlogin_r()
getnameinfo()
getnetbyaddr()
getnetbyname()
getnetent()
getopt() (si opterr est non nul)
getprotobyname()
getprotobynumber()
getprotoent()
getpwent()
getpwnam()
getpwnam_r()
getpwuid()
getpwuid_r()
gets()
getservbyname()
getservbyport()
getservent()
getutxent()
getutxid()
getutxline()
getwc()
getwchar()
getwd() [SUSv3 uniquement (fonction supprimée dans POSIX.1-2008)]
glob()
iconv_close()
iconv_open()
ioctl()
link()
linkat() [Ajoutée dans POSIX.1-2008]
lio_listio() [Ajoutée dans POSIX.1-2008]
localtime()
localtime_r()
lockf() [Ajoutée dans POSIX.1-2008]
lseek()
lstat()
mkdir() [Ajoutée dans POSIX.1-2008]
mkdirat() [Ajoutée dans POSIX.1-2008]
mkdtemp() [Ajoutée dans POSIX.1-2008]
mkfifo() [Ajoutée dans POSIX.1-2008]
mkfifoat() [Ajoutée dans POSIX.1-2008]
mknod() [Ajoutée dans POSIX.1-2008]
mknodat() [Ajoutée dans POSIX.1-2008]
mkstemp()
mktime()
nftw()
opendir()
openlog()
pathconf()
pclose()
perror()
popen()
posix_fadvise()
posix_fallocate()
posix_madvise()
posix_openpt()
posix_spawn()
posix_spawnp()
posix_trace_clear()
posix_trace_close()
posix_trace_create()
posix_trace_create_withlog()
posix_trace_eventtypelist_getnext_id()
posix_trace_eventtypelist_rewind()
posix_trace_flush()
posix_trace_get_attr()
posix_trace_get_filter()
posix_trace_get_status()
posix_trace_getnext_event()
posix_trace_open()
posix_trace_rewind()
posix_trace_set_filter()
posix_trace_shutdown()
posix_trace_timedgetnext_event()
posix_typed_mem_open()
printf()
psiginfo() [Ajoutée dans POSIX.1-2008]
psignal() [Ajoutée dans POSIX.1-2008]
pthread_rwlock_rdlock()
pthread_rwlock_timedrdlock()
pthread_rwlock_timedwrlock()
pthread_rwlock_wrlock()
putc()
putc_unlocked()
putchar()
putchar_unlocked()
puts()
pututxline()
putwc()
putwchar()
readdir()
readdir_r()
readlink() [Ajoutée dans POSIX.1-2008]
readlinkat() [Ajoutée dans POSIX.1-2008]
remove()
rename()
renameat() [Ajoutée dans POSIX.1-2008]
rewind()
rewinddir()
scandir() [Ajoutée dans POSIX.1-2008]
scanf()
seekdir()
semop()
setgrent()
sethostent()
setnetent()
setprotoent()
setpwent()
setservent()
setutxent()
sigpause() [Ajoutée dans POSIX.1-2008]
stat()
strerror()
strerror_r()
strftime()
symlink()
symlinkat() [Ajoutée dans POSIX.1-2008]
sync()
syslog()
tmpfile()
tmpnam()
ttyname()
ttyname_r()
tzset()
ungetc()
ungetwc()
unlink()
unlinkat() [Ajoutée dans POSIX.1-2008]
utime() [Ajoutée dans POSIX.1-2008]
utimensat() [Ajoutée dans POSIX.1-2008]
utimes() [Ajoutée dans POSIX.1-2008]
vdprintf() [Ajoutée dans POSIX.1-2008]
vfprintf()
vfwprintf()
vprintf()
vwprintf()
wcsftime()
wordexp()
wprintf()
wscanf()
Une implémentation peut également indiquer d’autres fonctions non
spécifiées dans la norme comme étant des points d’annulation. En
particulier, une implémentation marquera probablement toute fonction
non standard qui peut bloquer comme étant un point d’annulation (ceci
inclus la plupart des fonctions qui peuvent toucher des fichiers).
Compiler sous Linux
Sous Linux, les programmes utilisant l’API pthreads doivent être
compilés avec cc -pthread.
Implémentations des threads POSIX sous Linux
Deux implémentations différentes des threads ont été fournies par la
bibliothèque C de GNU sous Linux :
LinuxThreads
Il s’agit de l’implémentation des Pthreads originelle. Depuis la
glibc 2.4, cette implémentation n’est plus prise en charge.
NPTL (Native POSIX Threads Library)
Il s’agit de l’implémentation moderne des Pthreads. Par rapport
à LinuxThreads, NPTL se conforme mieux aux exigences de la norme
POSIX.1, et une meilleure performance lors de la création d’un
grand nombre de threads. NPTL est disponible depuis la
glibc 2.3.2, et nécessite des fonctionnalités présentes dans le
noyau Linux 2.6.
Ces deux implémentation sont dit de type 1:1, ce qui veut dire que
chaque thread correspond à une entité d’ordonnancement du noyau. Les
deux implémentations utilisent l’appel système clone(2) de Linux. Dans
NPTL, les primitives de synchronisation de threads (mutexes, jonction
de thread, etc.) sont implémentées avec l’appel système futex(2) de
Linux.
LinuxThreads
Les fonctionnalités importantes de cette implémentation sont les
suivantes:
- En plus du thread principal (initial) et des threads créés par le
programme avec pthread_create(3), l’implémentation crée un thread de
gestion. Ce thread s’occupe de la création et de la terminaison des
threads. (Des problèmes peuvent survenir si ce thread est tué de
façon imprévue.)
- Les signaux sont utilisés en interne par l’implémentation. Sous
Linux 2.2 et suivants, les trois premiers signaux temps-réel sont
utilisés (voir aussi signal(7)). Sous les noyaux plus anciens,
LinuxThreads utilise SIGUSR1 et SIGUSR2. Les applications doivent
éviter d’utiliser les signaux utilisés par l’implémentation.
- Les threads ne partagent pas leur identifiant de processus. (En
fait, les threads LinuxThreads sont implémentés comme des processus
partageant plus d’informations qu’à l’habitude, mais pas leur
identifiant de processus.) Les threads LinuxThreads (y compris le
thread de gestion) sont visibles comme des processus différents avec
ps(1).
L’implémentation LinuxThreads s’écarte de la spécification POSIX.1 par
plusieurs aspects, dont les suivants :
- Les appels à getpid(2) renvoient une valeur distincte dans chaque
thread.
- Les appels à getppid(2) dans les threads autres que le thread
principal renvoient l’identifiant de processus du thread de
gestion ; getppid(2) dans ces threads devrait renvoyer la même
valeur que dans le thread principal.
- Lorsqu’un thread crée un nouveau processus fils avec fork(2),
n’importe quel thread devrait pouvoir utiliser wait(2) pour attendre
la terminaison de ce fils. Cependant, l’implémentation ne permet
qu’au thread ayant créé le fils d’appeler wait(2) pour l’attendre.
- Lorsqu’un thread appelle execve(2), tous les autres threads sont
terminés (comme le prescrit POSIX.1). Cependant, le processus
résultant a le même PID que le thread ayant appelé execve(2) : il
devrait avoir le même PID que le thread principal.
- Les threads ne partagent pas leurs identifiants d’utilisateur et de
groupe. Ceci peut causer des complications pour les programmes
setuid et provoquer des erreurs dans les fonctions pthreads si une
application change d’identifiant avec seteuid(2) et consorts.
- Les threads ne partagent pas l’identifiant de session et de groupe
de processus.
- Les threads ne partagent pas les verrouillages d’enregistrements
créés avec fcntl(2).
- L’information renvoyée par times(2) et getrusage(2) est par thread
au lieu d’être par processus.
- Les threads ne partagent pas les valeurs « undo » de sémaphores
(voir semop(2)).
- Les threads ne partagent pas les temporisations d’intervalles.
- Les threads ne partagent pas leur valeur de politesse.
- POSIX.1 distingue les notions de signal envoyé au processus dans son
ensemble, et de signal envoyé à un thread individuellement. Selon
POSIX.1, un signal envoyé au processus (par exemple avec kill(2))
sera géré par un thread choisi arbitrairement au sein du processus.
LinuxThreads ne permet pas d’envoyer un signal au processus, mais
seulement à un thread spécifique.
- Les threads ont des paramètres de pile spécifique de signal
distincts. Cependant, les paramètres de pile spécifique d’un nouveau
thread sont copiés à partir du thread qui l’a créé, ce qui veut dire
que les threads partagent initialement une même pile spécifique de
signaux. (Un nouveau thread devrait démarrer sans pile spécifique de
signaux. Si deux threads gèrent un signal sur leur pile spécifique
au même moment, des échecs imprévisibles du programme risquent de se
produire.)
NPTL
Avec NPTL, tous les threads d’un processus sont placés dans le même
groupe de threads. Tous les membres d’un groupe de threads partagent le
même PID. NPTL n’utilise pas de thread de gestion. NPTL utilise en
interne les deux premiers signaux temps‐réel (voir aussi signal(7)) ;
ces signaux ne peuvent pas être utilisés dans les applications.
NPTL a encore au moins une non conformité à POSIX.1 :
- Les threads ne partagent pas leur valeur de politesse.
Certaines non conformités n’apparaissent qu’avec des noyaux plus
anciens :
- L’information renvoyée par times(2) et getrusage(2) est par thread
au lieu d’être globale au processus (corrigé dans le noyau 2.6.9).
- Les threads ne partagent pas les limites de ressources (corrigé dans
le noyau 2.6.10).
- Les threads ne partagent pas les temporisations d’intervalles
(corrigé dans le noyau 2.6.12).
- Seul le thread principal est autorisé à démarrer une nouvelle
session avec setsid(2) (corrigé dans le noyau 2.6.16).
- Seul le thread principal est autorisé à rendre le processus leader
de son groupe de processus avec setpgid(2) (corrigé dans le noyau
2.6.16).
- Les threads ont des paramètres de pile spécifique de signaux
distincts. Cependant, les paramètres de pile spécifique d’un nouveau
thread sont copiés sur ceux du thread qui l’a créé, et les threads
partagent donc initialement leur pile spécifique de signaux (corrigé
dans le noyau 2.6.16).
Veuillez noter les points suivants à propos de l’implémentation NPTL :
- Si la limite souple de taille de pile (voir dans setrlimit(2) la
description de RLIMIT_STACK) est différente de unlimited, cette
valeur détermine la taille de pile par défaut pour les nouveaux
threads. Pour avoir un effet, cette limite doit être fixée avant le
démarrage du programme, par exemple en utilisant la commande ulimit
-s du shell (limit stacksize dans csh).
Déterminer l’implémentation des threads utilisée
Depuis glibc 2.3.2, la commande getconf(1) peut être utilisée pour
déterminer l’implémentation de threads du système, par exemple :
bash$ getconf GNU_LIBPTHREAD_VERSION
NPTL 2.3.4
Avec des versions plus anciennes de la glibc, une commande comme la
suivante devrait être suffisante pour déterminer l’implémentation de
threads par défaut :
bash$ $( ldd /bin/ls | grep libc.so | awk '{print $3}' ) | \
egrep -i 'threads|nptl'
Native POSIX Threads Library by Ulrich Drepper et al
Choisir l’implémentation des threads : LD_ASSUME_KERNEL
Sur les systèmes avec une glibc fournissant à la fois LinuxThreads et
NPTL (i.e. glibc 2.3.x), la variable d’environnement LD_ASSUME_KERNEL
peut être utilisée pour écraser le choix par défaut d’implémentation de
threads fait par l’éditeur de liens dynamique. Cette variable indique à
l’éditeur de liens dynamique qu’il doit faire comme s’il était exécuté
avec une version particulière du noyau. En indiquant une version du
noyau ne fournissant pas les fonctionnalités nécessitées par NPTL, on
peut forcer l’utilisation de LinuxThreads. (La raison la plus probable
pour cela est d’exécuter une application (boguée) qui dépend d’un
comportement de LinuxThreads non conforme à la spécification.) Par
exemple :
bash$ $( LD_ASSUME_KERNEL=2.2.5 ldd /bin/ls | grep libc.so | \
awk '{print $3}' ) | egrep -i 'threads|ntpl'
linuxthreads-0.10 by Xavier Leroy
VOIR AUSSI
clone(2), futex(2), gettid(2), proc(5), futex(7), signal(7),
et diverses pages de manuel Pthreads, par exemple :
pthread_attr_init(3), pthread_atfork(3), pthread_cancel(3),
pthread_cleanup_push(3), pthread_cond_signal(3), pthread_cond_wait(3),
pthread_create(3), pthread_detach(3), pthread_equal(3),
pthread_exit(3), pthread_key_create(3), pthread_kill(3),
pthread_mutex_lock(3), pthread_mutex_unlock(3), pthread_once(3),
pthread_setcancelstate(3), pthread_setcanceltype(3),
pthread_setspecific(3), pthread_sigmask(3) et pthread_testcancel(3)
COLOPHON
Cette page fait partie de la publication 3.23 du projet man-pages
Linux. Une description du projet et des instructions pour signaler des
anomalies peuvent être trouvées à l’adresse
http://www.kernel.org/doc/man-pages/.
TRADUCTION
Cette page de manuel a été traduite et mise à jour par Christophe
Blaess <http://www.blaess.fr/christophe/> entre 1996 et 2003, puis par
Alain Portal <aportal AT univ-montp2 DOT fr> jusqu’en 2006, et mise à
disposition sur http://manpagesfr.free.fr/.
Les mises à jour et corrections de la version présente dans Debian sont
directement gérées par Julien Cristau <jcristau@debian.org> et l’équipe
francophone de traduction de Debian.
Veuillez signaler toute erreur de traduction en écrivant à
<debian-l10n-french@lists.debian.org> ou par un rapport de bogue sur le
paquet manpages-fr.
Vous pouvez toujours avoir accès à la version anglaise de ce document
en utilisant la commande « man -L C <section> <page_de_man> ».