Skip to content
Extraits de code Groupes Projets
Valider e9ea81fe rédigé par CharlyBVO's avatar CharlyBVO
Parcourir les fichiers

Revert "Restauration avant modif de la section organisation mémoire et liens vers ressources"

This reverts commit b5fdd6f7.
parent b5fdd6f7
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
......@@ -85,7 +85,7 @@ convention préfixé par ``0x`` tandis qu'un nombre entier en notation
binaire est préfixé par ``0b``. Ainsi, les déclarations ci-dessous
correspondent toutes à la même valeur.
.. literalinclude:: /C/S2-src/num.c
.. literalinclude:: /_static/src/C/S2-src/num.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -109,7 +109,7 @@ contrairement à la notation hexadécimale qui fait partie du langage.
``65 et 53 sont différents`` car le compilateur C interprète la ligne
``j=065;`` comme contenant un entier en notation octale et non décimale.
.. literalinclude:: /C/S2-src/octal.c
.. literalinclude:: /_static/src/C/S2-src/octal.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -240,7 +240,7 @@ peut causer des erreurs dans certains calculs. Par exemple,
voici un petit programme qui affiche les 10 premières puissances de
cinq et dix.
.. literalinclude:: /C/S2-src/short.c
.. literalinclude:: /_static/src/C/S2-src/short.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -248,7 +248,7 @@ cinq et dix.
Lorsqu'il est exécuté, ce programme affiche la sortie suivante.
.. literalinclude:: /C/S2-src/short.out
.. literalinclude:: /_static/src/C/S2-src/short.out
:encoding: utf-8
:language: console
......@@ -281,7 +281,7 @@ est décomposée en trois parties [#fexemple]_ :
- `e` bits sont réservés pour stocker l'exposant [#fexposant]_.
- Les `f` bits de poids faible servent à stocker la partie fractionnaire du nombre réel.
.. figure:: /C/svg/Float_example.png
.. figure:: /_static/figures/C/svg/Float_example.png
:align: center
:scale: 100
......@@ -316,7 +316,7 @@ Les tableaux
En langage C, les tableaux permettent d'agréger des données d'un même type. Il est possible de définir des vecteurs et des matrices en utilisant la syntaxe ci-dessous.
.. literalinclude:: /C/S2-src/array.c
.. literalinclude:: /_static/src/C/S2-src/array.c
:language: c
:encoding: utf-8
:start-after: ///AAA
......@@ -326,7 +326,7 @@ Les premières versions du langage C ne permettaient que la définition de table
Un tableau à une dimension peut s'utiliser avec une syntaxe similaire à celle utilisée par Java. Dans un tableau contenant ``n`` éléments, le premier se trouve à l'indice ``0`` et le dernier à l'indice ``n-1``. L'exemple ci-dessous présente le calcul de la somme des éléments d'un vecteur.
.. literalinclude:: /C/S2-src/array.c
.. literalinclude:: /_static/src/C/S2-src/array.c
:language: c
:encoding: utf-8
:start-after: ///CCC
......@@ -334,7 +334,7 @@ Un tableau à une dimension peut s'utiliser avec une syntaxe similaire à celle
Le langage C permet aussi la manipulation de matrices carrées ou rectangulaires qui sont composées d'éléments d'un même type. L'exemple ci-dessous calcule l'élément minimum d'une matrice rectangulaire. Il utilise la constante ``FLT_MAX`` qui correspond au plus grand nombre réel représentable avec un ``float`` et qui est définie dans `float.h`_.
.. literalinclude:: /C/S2-src/array.c
.. literalinclude:: /_static/src/C/S2-src/array.c
:language: c
:encoding: utf-8
:start-after: ///EEE
......@@ -374,7 +374,7 @@ caractères. À titre d'exemple, une fonction `toupper(3)`_ permettant
de transformer un caractère représentant une minuscule dans le
caractère représentant la majuscule correspondante peut s'écrire :
.. literalinclude:: /C/S2-src/toupper.c
.. literalinclude:: /_static/src/C/S2-src/toupper.c
:language: c
:encoding: utf-8
:start-after: ///AAA
......@@ -441,7 +441,7 @@ fonctions de manipulation de chaînes de
caractères. À titre d'exemple, la fonction ci-dessous calcule
la longueur d'une chaîne de caractères.
.. literalinclude:: /C/S2-src/strlen.c
.. literalinclude:: /_static/src/C/S2-src/strlen.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -556,7 +556,7 @@ donnée. Les adresses de données en mémoire sont rarement affichées,
mais quand c'est le cas, on utilise la notation hexadécimale comme
dans l'exemple ci-dessous.
.. literalinclude:: /C/S2-src/ptr.c
.. literalinclude:: /_static/src/C/S2-src/ptr.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -564,7 +564,7 @@ dans l'exemple ci-dessous.
L'exécution de ce fragment de programme produit la sortie suivante.
.. literalinclude:: /C/S2-src/ptr.out
.. literalinclude:: /_static/src/C/S2-src/ptr.out
:encoding: utf-8
:language: console
......@@ -591,7 +591,7 @@ l'expression ``*ptr``. Il est
représenté dans l'exemple ci-dessous.
.. literalinclude:: /C/S2-src/ptrex.c
.. literalinclude:: /_static/src/C/S2-src/ptrex.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -599,7 +599,7 @@ représenté dans l'exemple ci-dessous.
L'exécution de ce fragment de programme produit la sortie suivante.
.. literalinclude:: /C/S2-src/ptrex.out
.. literalinclude:: /_static/src/C/S2-src/ptrex.out
:encoding: utf-8
:language: console
......@@ -609,7 +609,7 @@ En pratique en C, les notations ``char*`` et ``char[]`` sont
les pointeurs, la fonction de calcul de la longueur d'une chaîne de
caractères peut se réécrire comme suit.
.. literalinclude:: /C/S2-src/strlenptr.c
.. literalinclude:: /_static/src/C/S2-src/strlenptr.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -618,7 +618,7 @@ caractères peut se réécrire comme suit.
Les pointeurs sont fréquemment utilisés dans les programmes écrits en langage C et il est important de bien comprendre leur fonctionnement. Un point important à bien comprendre est ce que l'on appelle l'`arithmétique des pointeurs`, c'est-à-dire la façon dont les opérations sur les pointeurs sont exécutées en langage C. Pour cela, il est intéressant de considérer la manipulation d'un tableau d'entiers à travers des pointeurs.
.. literalinclude:: /C/src/ptr_arith.c
.. literalinclude:: /_static/src/C/src/ptr_arith.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -626,7 +626,7 @@ Les pointeurs sont fréquemment utilisés dans les programmes écrits en langage
En mémoire, ce tableau est stocké en utilisant trois mots consécutifs de 32 bits comme le montre l'exécution du programme ci-dessous :
.. literalinclude:: /C/src/ptr_arith.c
.. literalinclude:: /_static/src/C/src/ptr_arith.c
:encoding: utf-8
:language: c
:start-after: ///CCC
......@@ -640,7 +640,7 @@ En mémoire, ce tableau est stocké en utilisant trois mots consécutifs de 32 b
La même sortie est produite avec le fragment de programme suivant qui utilise un pointeur.
.. literalinclude:: /C/src/ptr_arith.c
.. literalinclude:: /_static/src/C/src/ptr_arith.c
:encoding: utf-8
:language: c
:start-after: ///EEE
......@@ -658,7 +658,7 @@ Après l'exécution de la première ligne, ``ptr`` va contenir l'adresse de l'é
Il est intéressant pour terminer cette première discussion de l'arithmétique des pointeurs, de considérer l'exécution du fragment de code ci-dessous.
.. literalinclude:: /C/src/ptr_arith.c
.. literalinclude:: /_static/src/C/src/ptr_arith.c
:encoding: utf-8
:language: c
:start-after: ///GGG
......@@ -686,7 +686,7 @@ Outre les types de données décrits ci-dessus, les programmes informatiques doi
C permet la définition de structures qui combinent différents types de données simples ou structurés. Contrairement aux langages orientés objet, il n'y a pas de méthode directement associée aux structures qui sont définies. Une structure est uniquement un type de données. Voici quelques exemples de structures simples en C.
.. literalinclude:: /C/S2-src/struct.c
.. literalinclude:: /_static/src/C/S2-src/struct.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -698,7 +698,7 @@ La structure ``struct fract`` définit une fraction qui est composée de deux en
Les structures permettent de facilement regrouper des données qui sont logiquement reliées entre elles et doivent être manipulées en même temps. C permet d'accéder facilement à un élément d'une structure en utilisant l'opérateur '``.``'. Ainsi, la structure ``point`` dont nous avons parlé ci-dessus aurait pu être initialisée par les trois expressions ci-dessous :
.. literalinclude:: /C/S2-src/struct.c
.. literalinclude:: /_static/src/C/S2-src/struct.c
:encoding: utf-8
:language: c
:start-after: ///CCC
......@@ -724,7 +724,7 @@ Les structures sont également fréquemment utilisées pour représenter des for
La définition de ``struct timeval`` utilise une fonctionnalité fréquemment utilisée du C : la possibilité de définir des alias pour des noms de type de données existants. Cela se fait en utilisant l'opérateur ``typedef``. En C, il est possible de renommer des types de données existants. Ainsi, l'exemple ci-dessous utilise ``typedef`` pour définir ``Entier`` comme alias pour le type ``int`` et ``Fraction`` pour la structure ``struct fraction``.
.. literalinclude:: /C/S2-src/typedef.c
.. literalinclude:: /_static/src/C/S2-src/typedef.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -739,7 +739,7 @@ Les types ``Entier`` et ``int`` peuvent être utilisés de façon interchangeabl
``typedef`` est souvent utilisé pour avoir des identifiants de types de données plus courts. Par exemple, il est très courant de remplacer le types ``unsigned`` par les abréviations ci-dessous.
.. literalinclude:: /C/S2-src/typedef.c
.. literalinclude:: /_static/src/C/S2-src/typedef.c
:encoding: utf-8
:language: c
:start-after: ///EEE
......@@ -747,7 +747,7 @@ Les types ``Entier`` et ``int`` peuvent être utilisés de façon interchangeabl
Soyez prudents si vous utilisez des ``typedef`` pour redéfinir des pointeurs. En C, il est tout à fait valide d'écrire les lignes suivantes.
.. literalinclude:: /C/S2-src/typedef.c
.. literalinclude:: /_static/src/C/S2-src/typedef.c
:encoding: utf-8
:language: c
:start-after: ///CCC
......@@ -758,7 +758,7 @@ Les types ``Entier`` et ``int`` peuvent être utilisés de façon interchangeabl
Les pointeurs sont fréquemment utilisés lors de la manipulation de structures. Lorsqu'un pointeur pointe vers une structure, il est utile de pouvoir accéder facilement aux éléments de la structure. Le langage C supporte deux notations pour représenter ces accès aux éléments d'une structure. La première notation est ``(*ptr).elem`` où ``ptr`` est un pointeur et ``elem`` l'identifiant d'un des éléments de la structure pointée par ``ptr``. Cette notation est en pratique assez peu utilisée. La notation la plus fréquente est ``ptr->elem`` dans laquelle ``ptr`` et ``->elem`` sont respectivement un pointeur et un identifiant d'élément. L'exemple ci-dessous illustre l'initialisation de deux fractions en utilisant ces notations.
.. literalinclude:: /C/S2-src/structptr.c
.. literalinclude:: /_static/src/C/S2-src/structptr.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -774,7 +774,7 @@ Comme la plupart des langages, le C permet de faciliter la compréhension d'un p
en le découpant en de nombreuses fonctions. Chacune réalise une
tâche simple. Tout comme Java, C permet la définition de fonctions qui ne retournent aucun résultat. Celles-ci sont de type ``void`` comme l'exemple trivial ci-dessous.
.. literalinclude:: /C/S2-src/fct.c
.. literalinclude:: /_static/src/C/S2-src/fct.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -782,7 +782,7 @@ tâche simple. Tout comme Java, C permet la définition de fonctions qui ne reto
La plupart des fonctions utiles retournent un résultat qui peut être une donnée d'un des types standard ou une structure. Cette utilisation est similaire à ce que l'on trouve dans des langages comme Java. Il faut cependant être attentif à la façon dont le langage C traite les arguments des fonctions. Le langage C utilise le `passage par valeur` des arguments. Lorsqu'une fonction est exécutée, elle reçoit les valeurs de ces arguments. Ces valeurs sont stockées dans une zone mémoire qui est locale à la fonction. Toute modification faite sur la valeur d'une variable à l'intérieur d'une fonction est donc locale à cette fonction. Les deux fonctions ci-dessous ont le même résultat et aucune des deux n'a d'effet de bord.
.. literalinclude:: /C/S2-src/fct.c
.. literalinclude:: /_static/src/C/S2-src/fct.c
:encoding: utf-8
:language: c
:start-after: ///CCC
......@@ -790,7 +790,7 @@ La plupart des fonctions utiles retournent un résultat qui peut être une donn
Il faut être nettement plus attentif lorsque l'on écrit des fonctions qui utilisent des pointeurs comme arguments. Lorsqu'une fonction a un argument de type pointeur, celui-ci est passé par valeur, mais connaissant la valeur du pointeur, il est possible à la fonction de modifier le contenu de la zone mémoire pointée par le pointeur. Ceci est illustré par l'exemple ci-dessous.
.. literalinclude:: /C/S2-src/fct.c
.. literalinclude:: /_static/src/C/S2-src/fct.c
:encoding: utf-8
:language: c
:start-after: ///EEE
......@@ -798,13 +798,13 @@ Il faut être nettement plus attentif lorsque l'on écrit des fonctions qui util
Lors de l'exécution de la fonction ``f``, le programme ci-dessus affiche à la console la sortie suivante :
.. literalinclude:: /C/S2-src/fct.out
.. literalinclude:: /_static/src/C/S2-src/fct.out
:encoding: utf-8
:language: console
Cet exemple illustre aussi une contrainte imposée par le langage C sur l'ordre de définition des fonctions. Pour que les fonctions ``times_two`` et ``timestwo`` puissent être utilisées à l'intérieur de la fonction ``f``, il faut qu'elles aient été préalablement définies. Dans l'exemple ci-dessus, cela s'est fait en plaçant la définition des deux fonctions avant leur utilisation. C'est une règle de bonne pratique utilisable pour de petits programmes composés de quelques fonctions. Pour des programmes plus larges, il est préférable de placer au début du code source la signature des fonctions qui y sont définies. La signature d'une fonction comprend le type de valeur de retour de la fonction, son nom et les types de ses arguments. Généralement, ces déclarations sont regroupées à l'intérieur d'un :term:`fichier header` dont le nom se termine par ``.h``.
.. literalinclude:: /C/S2-src/fct.h
.. literalinclude:: /_static/src/C/S2-src/fct.h
:encoding: utf-8
:language: c
:start-after: ///HHH
......@@ -812,7 +812,7 @@ Cet exemple illustre aussi une contrainte imposée par le langage C sur l'ordre
Les fonctions peuvent évidemment recevoir également des tableaux comme arguments. Cela permet par exemple d'implémenter une fonction qui calcule la longueur d'une chaîne de caractères en itérant dessus jusqu'à trouver le caractère de fin de chaîne.
.. literalinclude:: /C/S2-src/strlenptr.c
.. literalinclude:: /_static/src/C/S2-src/strlenptr.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -821,12 +821,12 @@ Les fonctions peuvent évidemment recevoir également des tableaux comme argumen
.. spelling::
ième
Tout comme cette fonction peut accéder au `ième` caractère de la chaîne passée en argument, elle peut également et sans aucune restriction modifier chacun des caractères de cette chaîne. Par contre, comme le pointeur vers la chaîne de caractères est passé par valeur, la fonction ne peut pas modifier la zone mémoire qui est pointée par l'argument.
Un autre exemple de fonctions qui manipulent les tableaux sont des fonctions mathématiques qui traitent des vecteurs par exemple.
.. literalinclude:: /C/S2-src/fctargs.c
.. literalinclude:: /_static/src/C/S2-src/fctargs.c
:encoding: utf-8
:language: c
:start-after: ///CCC
......@@ -835,7 +835,7 @@ Un autre exemple de fonctions qui manipulent les tableaux sont des fonctions mat
Ces deux fonctions peuvent être utilisées par le fragment de code ci-dessous :
.. literalinclude:: /C/S2-src/fctargs.c
.. literalinclude:: /_static/src/C/S2-src/fctargs.c
:encoding: utf-8
:language: c
:start-after: ///EEE
......@@ -846,7 +846,7 @@ Ces deux fonctions peuvent être utilisées par le fragment de code ci-dessous :
Certains langages comme Java sont fortement typés et le compilateur contient de nombreuses vérifications, notamment sur les types de données utilisés, qui permettent d'éviter un grand nombre d'erreurs. Le langage C est lui nettement plus libéral. Les premiers compilateurs C étaient très permissifs notamment sur les types de données passés en arguments. Ainsi, un ancien compilateur C accepterait probablement sans broncher les appels suivants :
.. literalinclude:: /C/S2-src/fctargs.c
.. literalinclude:: /_static/src/C/S2-src/fctargs.c
:encoding: utf-8
:language: c
:start-after: ///GGG
......@@ -863,7 +863,7 @@ Ces deux fonctions peuvent être utilisées par le fragment de code ci-dessous :
Pour terminer, mentionnons que les fonctions écrites en C peuvent utiliser des structures et des pointeurs vers des structures comme arguments. Elles peuvent aussi retourner des structures comme résultat. Ceci est illustré par deux variantes de fonctions permettant d'initialiser une fraction et de déterminer si deux fractions sont égales [#fegal]_.
.. literalinclude:: /C/S2-src/struct.c
.. literalinclude:: /_static/src/C/S2-src/struct.c
:encoding: utf-8
:language: c
:start-after: ///EEE
......@@ -873,7 +873,7 @@ Considérons d'abord les fonctions ``init`` et ``equal``. ``init`` est une fonct
Les fonctions ``initptr`` et ``equalptr`` utilisent toutes les deux des pointeurs vers des ``struct fraction`` comme arguments. Ce faisant, elles ne peuvent modifier la valeur de ces pointeurs puisqu'ils sont passés comme valeurs. Par contre, les deux fonctions peuvent bien entendu modifier les éléments de la structure qui se trouvent dans la zone de mémoire pointée par le pointeur. C'est ce que ``initptr`` fait pour initialiser la structure. ``equalptr`` par contre se contente d'accéder aux éléments des structures passées en argument sans les modifier. Le fragment de code ci-dessous illustre comment ces fonctions peuvent être utilisées en pratique.
.. literalinclude:: /C/S2-src/struct.c
.. literalinclude:: /_static/src/C/S2-src/struct.c
:encoding: utf-8
:language: c
:start-after: ///GGG
......@@ -932,7 +932,7 @@ A B A XOR B
.. spelling::
De Morgan
Ces opérations peuvent être combinées entre elles. Pour des raisons technologiques, les circuits logiques implémentent plutôt les opérations NAND (qui équivaut à AND suivi de NOT) ou NOR (qui équivaut à OR suivi de NOT). Il est également important de mentionner les lois formulées par De Morgan qui peuvent se résumer par les équations suivantes :
......@@ -941,13 +941,13 @@ Ces opérations peuvent être combinées entre elles. Pour des raisons technolog
Ces opérations binaires peuvent s'étendre à des séquences de bits. Voici quelques exemples qui permettent d'illustrer ces opérations sur des octets.
.. literalinclude:: /C/S2-src/exprbin.out
.. literalinclude:: /_static/src/C/S2-src/exprbin.out
:encoding: utf-8
:language: console
En C, ces expressions logiques s'utilisent comme dans le fragment de code suivant. En général, elles s'utilisent sur des représentations non signées, souvent des ``unsigned char`` ou des ``unsigned int``.
.. literalinclude:: /C/S2-src/exprbin.c
.. literalinclude:: /_static/src/C/S2-src/exprbin.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -955,7 +955,7 @@ En C, ces expressions logiques s'utilisent comme dans le fragment de code suivan
En pratique, les opérations logiques sont utiles pour effectuer des manipulations au niveau des bits de données stockées en mémoire. Une utilisation fréquente dans certaines applications réseaux ou systèmes est de forcer certains bits à prendre la valeur ``0`` ou ``1``. La conjonction logique permet de forcer facilement un bit à zéro tandis que la disjonction logique permet de forcer facilement un bit à un. L'exemple ci-dessous montre comment forcer les valeurs de certains bits dans un ``unsigned char``. Il peut évidemment se généraliser à des séquences de bits plus longues.
.. literalinclude:: /C/S2-src/exprbin.c
.. literalinclude:: /_static/src/C/S2-src/exprbin.c
:encoding: utf-8
:language: c
:start-after: ///CCC
......@@ -964,10 +964,10 @@ En pratique, les opérations logiques sont utiles pour effectuer des manipulatio
.. spelling::
Vernam
L'opération XOR joue un rôle important dans certaines applications. La plupart des méthodes de chiffrement et de déchiffrement utilisent de façon extensive cette opération. Une des propriétés intéressantes de l'opération XOR est que :math:`(A \oplus B) \oplus B=A`. Cette propriété est largement utilisée par les méthodes de chiffrement. La méthode développée par Vernam au début du vingtième siècle s'appuie sur l'opération XOR. Pour transmettre un message `M` de façon sûre, elle applique l'opération XOR bit à bit entre tous les bits du message `M` et une clé `K` doit avoir au moins le même nombre de bits que `M`. Si cette clé `K` est totalement aléatoire et n'est utilisée qu'une seule fois, alors on parle de *one-time-pad*. On peut montrer que dans ce cas, la méthode de chiffrement est totalement sûre. En pratique, il est malheureusement difficile d'avoir une clé totalement aléatoire qui soit aussi longue que le message à transmettre. Le programme ci-dessous implémente cette méthode de façon triviale. La fonction `memfrob(3)`_ de la librairie :term:`GNU` utilise également un chiffrement via un XOR.
.. literalinclude:: /C/S2-src/xor.c
.. literalinclude:: /_static/src/C/S2-src/xor.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -993,7 +993,7 @@ Pour terminer, le langage C supporte des expressions permettant le décalage à
Ces opérations de décalage permettent différentes manipulations de bits. À titre d'exemple, la fonction ``int2bin`` utilise à la fois des décalages et des masques pour calculer la représentation binaire d'un entier non signé et la placer dans une chaîne de caractères.
.. literalinclude:: /C/S2-src/exprbin.c
.. literalinclude:: /_static/src/C/S2-src/exprbin.c
:encoding: utf-8
:language: c
:start-after: ///EEE
......@@ -1016,7 +1016,7 @@ Ces opérations de décalage permettent différentes manipulations de bits. À t
.. spelling::
IP
.. [#freseau] Parmi les exemples simples, on peut citer la structure ``struct ipv6hdr`` qui correspond à l'entête du protocole IP version 6 et est définie dans `linux/ipv6.h`_.
.. [#fegal] Cette définition de l'égalité entre fractions suppose que les fractions à comparer sont sous forme irréductible. Le lecteur est invité à écrire la fonction générale permettant de tester l'égalité entre fractions réductibles.
......@@ -33,7 +33,7 @@ Un premier exemple sont les pointeurs vers des fonctions. Comme nous l'avons vu
Cette application qui supporte plusieurs niveaux de débogage utilise pourtant toujours le même appel pour afficher l'information de débogage : ``(debug_print[debug_level])(...);``. Cet appel profite des pointeurs vers les fonctions. Le tableau ``debug_print`` est un tableau de pointeurs vers des fonctions qui chacune prend comme argument un ``char *``. La variable globale ``debug_level`` est initialisée sur base de l'argument passé au programme.
.. literalinclude:: /C/S5-src/fctptr.c
.. literalinclude:: /_static/src/C/S5-src/fctptr.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -49,7 +49,7 @@ Ce n'est pas la seule utilisation des pointeurs vers des fonctions. Il y a notam
Le premier est un pointeur vers le début de la zone mémoire à trier. Le second est le nombre d'éléments à trier. Le troisième contient la taille des éléments stockés dans le tableau. Le quatrième argument est un pointeur vers la fonction qui permet de comparer deux éléments du tableau. Cette fonction retourne un entier négatif si son premier argument est inférieur au second et positif ou nul sinon. Un exemple de fonction de comparaison est la fonction `strcmp(3)`_ de la librairie standard. Un autre exemple est repris ci-dessous avec une fonction de comparaison simple qui permet d'utiliser `qsort(3)`_ pour trier un tableau de ``double``.
.. literalinclude:: /C/S5-src/qsort.c
.. literalinclude:: /_static/src/C/S5-src/qsort.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -60,7 +60,7 @@ Il est utile d'analyser en détails les arguments de la fonction de comparaison
Le second type de pointeurs que nous n'avons pas encore abordé en détails sont les pointeurs vers des pointeurs. En fait, nous les avons utilisés sans vraiment le savoir dans la fonction ``main``. En effet, le second argument de cette fonction est un tableau de pointeurs qui pointent chacun vers des chaînes de caractères différentes. La notation ``char *argv[]`` est équivalente à la notation ``char **argv``. ``**argv`` est donc un pointeur vers une zone qui contient des pointeurs vers des chaînes de caractères. Ce pointeur vers un pointeur doit être utilisé avec précaution. ``argv[0]`` est un pointeur vers une chaîne de caractères. La construction ``&(argv[0])`` permet donc d'obtenir un pointeur vers un pointeur vers une chaîne de caractères, ce qui correspond bien à la déclaration ``char **``. Ensuite, l'utilisation de ``*p`` pourrait surprendre. ``*p`` est un pointeur vers une chaîne de caractères. Il peut donc être comparé à ``NULL`` qui est aussi un pointeur, incrémenté et la chaîne de caractères qu'il référence peut être affichée par `printf(3)`_.
.. literalinclude:: /C/S5-src/ptrptr.c
.. literalinclude:: /_static/src/C/S5-src/ptrptr.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -82,14 +82,14 @@ L'utilisation principale de `strtol(3)`_ est de convertir une chaîne de caract
`strtol(3)`_ est un exemple de fonction qui doit retourner deux types d'informations. Tout d'abord, `strtol(3)`_ retourne un résultat (dans ce cas un nombre). Si la chaîne de caractères à convertir est erronée, `strtol(3)`_ convertit le début de la chaîne et retourne un pointeur indiquant le premier caractère en erreur. Pour bien comprendre le fonctionnement de `strtol(3)`_, considérons l'exemple ci-dessous.
.. literalinclude:: /C/S5-src/strtol.c
.. literalinclude:: /_static/src/C/S5-src/strtol.c
:encoding: utf-8
:language: c
:start-after: ///AAA
Lors de son exécution, ce programme affiche la sortie suivante.
.. literalinclude:: /C/S5-src/strtol.out
.. literalinclude:: /_static/src/C/S5-src/strtol.out
:encoding: utf-8
:language: console
......@@ -97,7 +97,7 @@ L'appel à `strtol(3)`_ prend trois arguments. Tout d'abord un pointeur vers la
Une implémentation partielle de `strtol(3)`_ pourrait être la suivante.
.. literalinclude:: /C/S5-src/mystrtol.c
.. literalinclude:: /_static/src/C/S5-src/mystrtol.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -116,7 +116,7 @@ Lorsque l'on développe de grands programmes en C, il est préférable de décou
Pour comprendre l'utilisation de ces modules, considérons d'abord un programme trivial composé de deux modules. Le premier module est celui qui contient la fonction ``main``. Tout programme C doit contenir une fonction ``main`` pour pouvoir être exécuté. C'est en général l'interface avec l'utilisateur. Le second module contient une fonction générique qui est utilisée par le module principal.
.. literalinclude:: /C/S5-src/main.c
.. literalinclude:: /_static/src/C/S5-src/main.c
:encoding: utf-8
:language: c
......@@ -126,7 +126,7 @@ Un module d'un programme C est en général décomposé en deux parties. Tout d'
- les constantes qui sont utilisées à l'intérieur du module et doivent être visibles en dehors de celui-ci, notamment par les modules qui utilisent les fonctions du module. Ces constantes peuvent être définies en utilisant des directives ``#define`` du préprocesseur
- les variables globales qui sont utilisées par les fonctions du module et doivent être accessibles en dehors de celui-ci
.. literalinclude:: /C/S5-src/min.h
.. literalinclude:: /_static/src/C/S5-src/min.h
:encoding: utf-8
:language: c
......@@ -134,7 +134,7 @@ Un module d'un programme C est en général décomposé en deux parties. Tout d'
L'exemple de :term:`fichier header` ci-dessus illustre une convention courante dans l'écriture de ces fichiers. Parfois, il est nécessaire d'inclure un :term:`fichier header` dans un autre fichier header. Suite à cela, il est possible que les mêmes définitions d'un :term:`fichier header` soient incluses deux fois ou plus dans le même module. Cela peut causer des erreurs de compilation qui risquent de perturber certains programmeurs. Une règle de bonne pratique pour éviter ce problème est d'inclure le contenu du :term:`fichier header` de façon conditionnelle comme présenté ci-dessus. Une constante, dans ce cas ``_MIN_H_``, est définie pour le :term:`fichier header` concerné. Cette constante est définie dans la première ligne effective du :term:`fichier header`. Celui-ci n'est inclus dans un module que si cette constante n'a pas été préalablement définie. Si cette constante est connue par le préprocesseur, cela indique qu'un autre :term:`fichier header` a déjà inclus les définitions de ce fichier et qu'elles ne doivent pas être incluses une seconde fois.
.. literalinclude:: /C/S5-src/min.c
.. literalinclude:: /_static/src/C/S5-src/min.c
:encoding: utf-8
:language: c
......@@ -144,7 +144,7 @@ Un module d'un programme C est en général décomposé en deux parties. Tout d'
Lorsque l'on doit compiler un programme qui fait appel à plusieurs modules, quelle que soit sa taille, il est préférable d'utiliser `make(1)`_ pour automatiser sa compilation. Le fichier ci-dessous est un petit exemple de :term:`Makefile` utilisable pour un tel projet.
.. literalinclude:: /C/S5-src/Makefile2
.. literalinclude:: /_static/src/C/S5-src/Makefile2
:encoding: utf-8
:language: makefile
:start-after: ###AAA
......@@ -154,7 +154,7 @@ La compilation d'un tel programme se déroule en plusieurs étapes. La première
La figure ci-dessous représente graphiquement les différentes étapes de compilation des modules ``min.c`` et ``main.c``.
.. figure:: /C/figures/figures-007-c.png
.. figure:: /_static/figures/C/figures/figures-007-c.png
:align: center
:scale: 60
......@@ -176,17 +176,17 @@ Il faut noter que ``static`` peut aussi précéder des déclarations de fonction
Afin d'illustrer l'utilisation de ``static`` et ``extern``, considérons le programme ``prog.c`` ci-dessous qui inclut le module ``module.c`` et également le module ``min.c`` présenté plus haut.
.. literalinclude:: /C/S5-src/module.h
.. literalinclude:: /_static/src/C/S5-src/module.h
:encoding: utf-8
:language: c
.. literalinclude:: /C/S5-src/module.c
.. literalinclude:: /_static/src/C/S5-src/module.c
:encoding: utf-8
:language: c
Ce module contient deux fonctions, ``vmin`` et ``min``. ``vmin`` est accessible depuis n'importe quel module. Sa signature est reprise dans le :term:`fichier header` ``module.h``. La fonction ``min`` par contre est déclarée comme étant ``static``. Cela implique qu'elle n'est utilisable qu'à l'intérieur de ce module et invisible de tout autre module. La variable globale ``num1`` est accessible depuis n'importe quel module. La variable ``num2`` également, mais elle est initialisée dans un autre module. Enfin, la variable ``num3`` n'est accessible qu'à l'intérieur de ce module.
.. literalinclude:: /C/S5-src/prog.c
.. literalinclude:: /_static/src/C/S5-src/prog.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -195,7 +195,7 @@ Ce module inclus les fichiers ``min.h`` et ``module.h`` qui contiennent les sign
La fonction ``f`` mérite que l'on s'y attarde un peu. Cette fonction contient la définition de la variable ``static n``. Même si cette variable est locale à la fonction ``f`` et donc invisible en dehors de cette fonction, le compilateur va lui réserver une place dans la même zone que les variables globales. La valeur de cette variable ``static`` sera initialisée une seule fois : au démarrage du programme. Même si cette variable parait être locale, elle ne sera jamais réinitialisée lors d'un appel à la fonction ``f``. Comme cette variable est stockée en dehors de la pile, elle conserve sa valeur d'une invocation à l'autre de la fonction ``f``. Ceci est illustré par l'exécution du programme qui produit la sortie suivante.
.. literalinclude:: /C/S5-src/prog.out
.. literalinclude:: /_static/src/C/S5-src/prog.out
:encoding: utf-8
:language: console
......@@ -211,7 +211,7 @@ Les systèmes Unix utilisent la variable globale :term:`errno` pour résoudre ce
A titre d'exemple, le programme ci-dessous utilise `strerror(3)`_ pour afficher un message d'erreur plus parlant lors d'appels erronés à la fonction `setenv(3)`_.
.. literalinclude:: /C/S5-src/errno.c
.. literalinclude:: /_static/src/C/S5-src/errno.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......
Ce diff est replié.
......@@ -30,7 +30,7 @@ Les systèmes Unix supportent différents mécanismes d'authentification. Le plu
username
inode
inodes
Lorsqu'un utilisateur se connecte sur un système Unix, il fournit son nom d'utilisateur ou `username`. Ce nom d'utilisateur est une chaîne de caractères qui est facile à mémoriser par l'utilisateur. D'un point de vue implémentation, un système d'exploitation préfère manipuler des nombres plutôt que des chaînes de caractères. Unix associe à chaque utilisateur un identifiant qui est stocké sous la forme d'un nombre entier positif. La table de correspondance entre l'identifiant d'utilisateur et le nom d'utilisateur est le fichier `/etc/passwd`. Ce fichier texte, comme la grande majorité des fichiers de configuration d'un système Unix, comprend pour chaque utilisateur l'information suivante :
- nom d'utilisateur (`username`)
......@@ -191,18 +191,18 @@ Il existe plusieurs appels systèmes et fonctions de la librairie standard qui p
char d_name[256]; /* filename */
};
.. spelling::
l'inode
métadonnée
Cette structure comprend le numéro de l'inode, c'est-à-dire la métadonnée qui contient les informations relatives au fichier/répertoire, la position de l'entrée ``dirent`` qui suite, la longueur de l'entrée, son type et le nom de l'entrée dans le répertoire. Chaque appel à `readdir(3)`_ retourne un pointeur vers une structure de ce type.
L'extrait de code ci-dessous permet de lister tous les fichiers présents dans le répertoire ``name``.
.. literalinclude:: /Fichiers/src/readdir.c
.. literalinclude:: /_static/src/Fichiers/src/readdir.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -345,7 +345,7 @@ Ces deux appels systèmes prennent trois arguments. Le premier est le `descripte
Il est important de noter que `read(2)`_ et `write(2)`_ permettent de lire et d'écrire des séquences contiguës d'octets. Lorsque l'on écrit ou lit des chaînes de caractères dans lesquels chaque caractère est représenté sous la forme d'un byte, il est possible d'utiliser `read(2)`_ et `write(2)`_ pour lire et écrire d'autres types de données que des octets comme le montre l'exemple ci-dessous.
.. literalinclude:: /Fichiers/src/read.c
.. literalinclude:: /_static/src/Fichiers/src/read.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -353,7 +353,7 @@ Il est important de noter que `read(2)`_ et `write(2)`_ permettent de lire et d'
Lors de son exécution, ce programme affiche la sortie ci-dessous.
.. literalinclude:: /Fichiers/src/read.out
.. literalinclude:: /_static/src/Fichiers/src/read.out
:encoding: utf-8
:language: console
......@@ -367,13 +367,13 @@ Le second problème est que les fabricants de processeurs ne se sont pas mis d'a
Pour comprendre ces deux techniques, regardons comment l'entier 16 bits ``0b1111111100000000`` est stocké en mémoire. En :term:`big endian`, le byte ``11111111`` sera stocké à l'adresse `x` et le byte ``00000000`` à l'adresse `x+1`. En :term:`little endian`, c'est le byte ``00000000`` qui est stocké à l'adresse `x` et le byte ``11111111`` qui est stocké à l'adresse `x+1`. Il en va de même pour les entiers encodés sur 32 bits comme illustré dans les deux figures ci-dessous [#fendianfig]_.
.. figure:: /Fichiers/fig/500px-Big-Endian.png
.. figure:: /_static/figures/Fichiers/fig/500px-Big-Endian.png
:align: center
:scale: 40
Ecriture d'un entier 32 bits en mémoire en `big endian`
.. figure:: /Fichiers/fig/500px-Little-Endian.png
.. figure:: /_static/figures/Fichiers/fig/500px-Little-Endian.png
:align: center
:scale: 40
......@@ -442,3 +442,6 @@ Cet appel système prend trois arguments. Le premier est le :term:`descripteur d
.. [#flimit] Il y a une limite maximale au nombre de fichiers qui peuvent être ouverts par un processus. Cette limite peut être récupérée avec l'appel système `getdtablesize(2)`_.
.. [#fendianfig] Source : http://en.wikipedia.org/wiki/Endianness
......@@ -110,7 +110,7 @@ Les sémaphores permettent de résoudre de nombreux problèmes classiques. Le pr
Les sémaphores peuvent être utilisés pour d'autres types de synchronisation. Par exemple, considérons une application découpée en threads dans laquelle la fonction ``after`` ne peut jamais être exécutée avant la fin de l'exécution de la fonction ``before``. Ce problème de coordination peut facilement être résolu en utilisant un sémaphore qui est initialisé à la valeur ``0``. La fonction ``after`` doit démarrer par un appel à `sem_wait(3)`_ sur ce sémaphore tandis que la fonction ``before`` doit se terminer par un appel à la fonction `sem_post(3)`_ sur ce sémaphore. De cette façon, si le thread qui exécute la fonction ``after`` est trop rapide, il sera bloqué sur l'appel à `sem_wait(3)`_. S'il arrive à cette fonction après la fin de la fonction ``before`` dans l'autre thread, il pourra passer sans être bloqué. Le programme ci-dessous illustre cette utilisation des sémaphores POSIX.
.. literalinclude:: /Threads/S7-src/pthread-sem-before.c
.. literalinclude:: /_static/src/Threads/S7-src/pthread-sem-before.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -131,7 +131,7 @@ Le problème des producteurs-consommateurs est un problème extrêmement fréque
Ces deux types de threads communiquent en utilisant un buffer qui a une capacité limitée à `N` places comme illustré dans la figure ci-dessous.
.. figure:: /Threads/figures/figures-S7-001-c.png
.. figure:: /_static/figures/Threads/figures/figures-S7-001-c.png
:align: center
:scale: 80
......@@ -247,7 +247,7 @@ Il reste cependant quelques concepts qu'il est utile de connaître lorsque l'on
movl %eax, v+4
ret
Le qualificatif ``volatile`` force le compilateur à recharger la variable depuis la mémoire avant chaque utilisation. Ce qualificatif est utile lorsque le contenu stocké à une adresse mémoire peut être modifié par une autre source que le programme lui-même. C'est le cas dans les threads, mais marquer les variables partagées par des threads comme ``volatile`` ne suffit pas. Si ces variables sont modifiées par certains threads, il est nécessaire d'utiliser des :term:`mutex` ou d'autres techniques de coordination pour réguler l'accès en ces variables partagées. En pratique, la documentation du programme devra spécifier quelles variables sont partagées entre les threads et la technique de coordination éventuelle qui est utilisée pour en réguler les accès. L'utilisation du qualificatif ``volatile`` permet de forcer le compilateur à recharger le contenu de la variable depuis la mémoire avant toute utilisation. C'est une règle de bonne pratique qu'il est utile de suivre. Il faut cependant noter que dans l'exemple ci-dessus, l'utilisation du qualificatif ``volatile`` augmente le nombre d'accès à la mémoire et peut donc dans certains cas réduire les performances.
Le qualificatif ``volatile`` force le compilateur à recharger la variable depuis la mémoire avant chaque utilisation. Ce qualificatif est utile lorsque le contenu stocké à une adresse mémoire peut être modifié par une autre source que le programme lui-même. C'est le cas dans les threads, mais marquer les variables partagées par des threads comme ``volatile`` ne suffit pas. Si ces variables sont modifiées par certains threads, il est nécessaire d'utiliser des :term:`mutex` ou d'autres techniques de coordination pour réguler l'accès en ces variables partagées. En pratique, la documentation du programme devra spécifier quelles variables sont partagées entre les threads et la technique de coordination éventuelle qui est utilisée pour en réguler les accès. L'utilisation du qualificatif ``volatile`` permet de forcer le compilateur à recharger le contenu de la variable depuis la mémoire avant toute utilisation. C'est une règle de bonne pratique qu'il est utile de suivre. Il faut cependant noter que dans l'exemple ci-dessus, l'utilisation du qualificatif ``volatile`` augmente le nombre d'accès à la mémoire et peut donc dans certains cas réduire les performances.
Variables spécifiques à un thread
---------------------------------
......@@ -260,7 +260,7 @@ Une deuxième solution serait d'avoir un tableau global qui contiendrait des poi
Pour résoudre ce problème, deux solutions sont possibles. La première combine une extension au langage C qui est supportée par `gcc(1)`_ avec la librairie threads POSIX. Il s'agit du qualificatif ``__thread`` qui peut être utilisé avant une déclaration de variable. Lorsqu'il est utilisé dans la déclaration d'une variable globale, il indique au compilateur et à la libraire POSIX qu'une copie de cette variable doit être créée pour chaque thread. Cette variable est initialisée au démarrage du thread et est utilisable uniquement à l'intérieur de ce thread. Le programme ci-dessous illustre cette utilisation du qualificatif ``__thread``.
.. literalinclude:: /Threads/S7-src/pthread-specific.c
.. literalinclude:: /_static/src/Threads/S7-src/pthread-specific.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -268,7 +268,7 @@ Pour résoudre ce problème, deux solutions sont possibles. La première combine
Lors de son exécution, ce programme affiche la sortie suivante sur :term:`stdout`. Cette sortie illustre bien que les variables dont la déclaration est précédée du qualificatif ``__thread`` sont utilisables uniquement à l'intérieur d'un thread.
.. literalinclude:: /Threads/S7-src/pthread-specific.out
.. literalinclude:: /_static/src/Threads/S7-src/pthread-specific.out
:encoding: utf-8
:language: console
......@@ -285,7 +285,7 @@ Il faut noter que la fonction `pthread_key_create(3posix)`_ associe en pratique
L'exemple ci-dessous illustre l'utilisation de cette API. Elle est nettement plus lourde à utiliser que le qualificatif ``__thread``. Dans ce code, chaque thread démarre par la fonction ``f``. Celle-ci crée une variable spécifique de type ``int`` qui joue le même rôle que la variable ``__thread int count;`` dans l'exemple précédent. La fonction ``g`` qui est appelée sans argument peut accéder à la zone mémoire créée en appelant ``pthread_getspecific(count)``. Elle peut ensuite exécuter ses calculs en utilisant le pointeur ``count_ptr``. Avant de se terminer, la fonction ``f`` libère la zone mémoire qui avait été allouée par `malloc(3)`_. Une alternative à l'appel explicite à `free(3)`_ aurait été de passer ``free`` comme second argument à `pthread_key_create(3posix)`_ lors de la création de la clé ``count``. En effet, ce second argument est la fonction à appeler à la fin du thread pour libérer la mémoire correspondant à cette clé.
.. literalinclude:: /Threads/S7-src/pthread-specific2.c
.. literalinclude:: /_static/src/Threads/S7-src/pthread-specific2.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -305,7 +305,7 @@ Dans un programme séquentiel, il n'y a qu'un thread d'exécution et de nombreux
Pour comprendre le problème, il est intéressant de comparer plusieurs implémentations d'une fonction simple. Considérons le problème de déterminer l'élément maximum d'une structure de données contenant des entiers. Si la structure de données est un tableau, une solution simple est de le parcourir entièrement pour déterminer l'élément maximum. C'est ce que fait la fonction ``max_vector`` dans le programme ci-dessous. Dans un programme purement séquentiel dans lequel le tableau peut être modifié de temps en temps, parcourir tout le tableau pour déterminer son maximum n'est pas nécessairement la solution la plus efficace. Une alternative est de mettre à jour la valeur du maximum chaque fois qu'un élément du tableau est modifié. Les fonctions ``max_global`` et ``max_static`` sont deux solutions possibles. Chacune de ces fonctions doit être appelée chaque fois qu'un élément du tableau est modifié. ``max_global`` stocke dans une variable globale la valeur actuelle du maximum du tableau et met à jour cette valeur à chaque appel. La fonction ``max_static`` fait de même en utilisant une variable statique. Ces deux solutions sont équivalentes et elles pourraient très bien être intégrées à une librairie utilisée par de nombreux programmes.
.. literalinclude:: /Threads/S7-src/reentrant.c
.. literalinclude:: /_static/src/Threads/S7-src/reentrant.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......
......@@ -17,7 +17,7 @@ Les performances des microprocesseurs se sont continuellement améliorées depui
intel
.. figure:: /Threads/figures/534px-Transistor_Count_and_Moore's_Law_-_2011.png
.. figure:: /_static/figures/Threads/figures/534px-Transistor_Count_and_Moore's_Law_-_2011.png
:align: center
Evolution du nombre de transistors par microprocesseur
......@@ -27,7 +27,7 @@ Cette évolution avait été prédite par Gordon Moore dans les années 1960s [S
Le fonctionnement d'un microprocesseur est régulé par une horloge. Celle-ci rythme la plupart des opérations du processeur et notamment le chargement des instructions depuis la mémoire. Pendant de nombreuses années, les performances des microprocesseurs ont fortement dépendu de leur vitesse d'horloge. Les premiers microprocesseurs avaient des fréquences d'horloge de quelques centaines de :term:`kHz`. A titre d'exemple, le processeur intel 4004 avait une horloge à 740 kHz en 1971. Aujourd'hui, les processeurs rapides dépassent la fréquence de 3 :term:`GHz`. La figure ci-dessous présente l'évolution de la fréquence d'horloge des microprocesseurs depuis les années 1970s [#fperf]_. On remarque une évolution rapide jusqu'aux environs du milieu de la dernière décennie. La barrière des 10 MHz a été franchie à la fin des années 1970s. Les 100 :term:`MHz` ont étés atteints en 1994 et le GHz aux environs de l'an 2000.
.. figure:: /Threads/figures/figures-001-c.png
.. figure:: /_static/figures/Threads/figures/figures-001-c.png
:align: center
Evolution de la vitesse d'horloge des microprocesseurs
......@@ -45,7 +45,7 @@ Si pendant longtemps la fréquence d'horloge d'un microprocesseur a été une bo
Une autre façon de mesurer les performances d'un microprocesseur est de comptabiliser le nombre d'instructions qu'il exécute par seconde. On parle en général de Millions d'Instructions par Seconde (ou :term:`MIPS`). Si les premiers microprocesseurs effectuaient moins de 100.000 instructions par seconde, la barrière du MIPS a été franchie en 1979. Mesurées en MIPS, les performances des microprocesseurs ont continué à augmenter durant les dernières années malgré la barrière des 3 GHz comme le montre la figure ci-dessous.
.. figure:: /Threads/figures/figures-002-c.png
.. figure:: /_static/figures/Threads/figures/figures-002-c.png
:align: center
Evolution des performances des microprocesseurs en MIPS
......@@ -67,9 +67,9 @@ Cette progression continue des performances en MIPS a été possible grâce à l
La notion de thread d'exécution est très importante dans un système informatique. Elle permet non seulement de comprendre comme un ordinateur équipé d'un seul microprocesseur peut exécuter plusieurs programmes simultanément, mais aussi comment des programmes peuvent profiter des nouveaux processeurs capables d'exécuter plusieurs threads simultanément.
..
Pour comprendre cette notion, il est intéressant de revenir à nouveau sur l'exécution d'une fonction en langage assembleur.
Pour comprendre cette notion, il est intéressant de revenir à nouveau sur l'exécution d'une fonction en langage assembleur.
Considérons la fonction ``f`` :
......@@ -122,7 +122,7 @@ Pour qu'un processeur puisse exécuter cette séquence d'instructions, il faut q
- à la mémoire contenant les données manipulées par cette séquence d'instruction. Pour rappel, cette mémoire est divisée en plusieurs parties :
- la zone contenant les variables globales
- le tas
- le tas
- la pile
......@@ -194,7 +194,7 @@ Le premier argument de `pthread_join(3)`_ est la structure ``pthread_t`` corresp
L'exemple ci-dessous illustre une utilisation simple des fonctions `pthread_create(3)`_, `pthread_join(3)`_ et `pthread_exit(3)`_.
.. literalinclude:: /Threads/S5-src/pthread.c
.. literalinclude:: /_static/src/Threads/S5-src/pthread.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -203,7 +203,7 @@ Dans ce programme, la fonction ``main`` lance deux threads. Le premier exécute
Afin d'illustrer la possibilité de passer des arguments à un thread et d'en récupérer la valeur de retour, considérons l'exemple ci-dessous.
.. literalinclude:: /Threads/S5-src/pthread-neg.c
.. literalinclude:: /_static/src/Threads/S5-src/pthread-neg.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......
......@@ -10,7 +10,7 @@ Communication entre threads
Lorsque un programme a été décomposé en plusieurs threads, ceux-ci ne sont en général pas complètement indépendants et ils doivent communiquer entre eux. Cette communication entre threads est un problème complexe comme nous allons le voir. Avant d'aborder ce problème, il est utile de revenir à l'organisation d'un processus et de ses threads en mémoire. La figure ci-dessous illustre schématiquement l'organisation de la mémoire après la création d'un thread POSIX.
.. figure:: /Threads/S6-fig/figures-001-c.png
.. figure:: /_static/figures/Threads/S6-fig/figures-001-c.png
:align: center
:scale: 80
......@@ -84,7 +84,7 @@ Malheureusement les difficultés surviennent lorsque deux threads exécutent en
La taille de la pile d'un thread POSIX est l'un des attributs qui peuvent être modifiés lors de l'appel à `pthread_create(3)`_ pour créer un nouveau thread. Cet attribut peut être fixé en utilisant la fonction `pthread_attr_setstackaddr(3posix)`_ comme illustré dans l'exemple ci-dessous [#fpthreadc]_ (où ``thread_first`` est la fonction qui sera appelée à la création du thread). En général, la valeur par défaut choisie par le système suffit, sauf lorsque le programmeur sait qu'un thread devra par exemple allouer un grand tableau auquel il sera le seul à avoir accès. Ce tableau sera alors alloué sur la pile qui devra être suffisamment grande pour le contenir.
.. literalinclude:: /Threads/S6-src/pthread.c
.. literalinclude:: /_static/src/Threads/S6-src/pthread.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -95,7 +95,7 @@ Ce problème d'accès concurrent à une zone de mémoire par plusieurs threads e
Le fragment de code ci-dessus présente une autre illustration d'une section critique. Dans cet exemple, la fonction ``main`` (non présentée), créée deux threads. Le premier exécute la fonction ``inc`` qui incrémente la variable ``global``. Le second exécute la fonction ``is_even`` qui teste la valeur de cette variable et compte le nombre de fois qu'elle est paire. Après la terminaison des deux threads, le programme affiche le contenu des variables ``global`` et ``even``.
.. literalinclude:: /Threads/S6-src/pthread-test-if.c
.. literalinclude:: /_static/src/Threads/S6-src/pthread-test-if.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -172,7 +172,7 @@ Le deuxième type d'événement est l'exécution d'un appel système bloquant. U
Ces interactions entre les threads et le système d'exploitation sont importantes. Pour bien les comprendre, il est utile de noter qu'un thread peut se trouver dans trois états différents du point de vue de son interaction avec le système d'exploitation. Ces trois états sont illustrés dans la figure ci-dessous.
.. figure:: /Threads/S6-fig/figures-003-c.png
.. figure:: /_static/figures/Threads/S6-fig/figures-003-c.png
:align: center
:scale: 80
......@@ -274,7 +274,7 @@ En C, cela se fait en utilisant les fonctions `pthread_mutex_lock(3posix)`_ et `
L'exemple ci-dessous reprend le programme dans lequel une variable globale est incrémentée par plusieurs threads.
.. literalinclude:: /Threads/S6-src/pthread-mutex.c
.. literalinclude:: /_static/src/Threads/S6-src/pthread-mutex.c
:encoding: utf-8
:language: c
:start-after: ///AAA
......@@ -303,9 +303,9 @@ Pour montrer que la propriété de vivacité est bien respectée, il faut montre
.. rubric:: Footnotes
.. [#fexemple] Le programme complet est accessible via :download:`/Threads/S5-src/pthread-test.c`
.. [#fexemple] Le programme complet est accessible via :download:`/_static/src/Threads/S5-src/pthread-test.c`
.. [#fpthreadc] Le programme complet est accessible via :download:`/Threads/S6-src/pthread.c`
.. [#fpthreadc] Le programme complet est accessible via :download:`/_static/src/Threads/S6-src/pthread.c`
.. .. [#fframes] Il existe différents standards pour le nombre d'images par seconde en cinéma et en télévision. Les plus courants sont 24, 25 et 30 images par seconde. Voir http://en.wikipedia.org/wiki/Frame_rate
......@@ -317,4 +317,4 @@ Pour montrer que la propriété de vivacité est bien respectée, il faut montre
.. [#fstaticinit] Linux supporte également la macro ``PTHREAD_MUTEX_INITIALIZER`` qui permet d'initialiser directement un ``pthread_mutex_t`` déclaré comme variable globale. Dans cet exemple, la déclaration aurait été : ``pthread_mutex_t global_mutex=PTHREAD_MUTEX_INITIALIZER;`` et l'appel à `pthread_mutex_init(3posix)`_ aurait été inutile. Comme il s'agit d'une extension spécifique à Linux, il est préférable de ne pas l'utiliser pour garantir la portabilité du code.
.. [#fphilo] Le programme complet est :download:`/Threads/S6-src/pthread-philo.c`
.. [#fphilo] Le programme complet est :download:`/_static/src/Threads/S6-src/pthread-philo.c`
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter