© D. Mathieu
mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 21/08/2001 -
Dernière mise à jour : 24/09/2001
bool Continuer = true;
void Derout (int NumSig) { Continuer = false; } int main (int argc, char * argv [])
for (; Continuer; ); cout << "Fini" << endl; return 0; } // main() |
Compilé simplement par le compilateur g++ (et sans doute la plupart des compilateurs), ce programme semble fonctionner comme prévu. On dirait "qu'il est juste"... Cependant, si on ajoute l'option -O, qui demande une compilation optimisée, le programme ne réagit plus au signal SIGINT. La raison en est très simple : la variable Continuer est une variable globale, donc stockée dans la zone statique de la mémoire virtuelle réservée aux variables globales initialisées. Le compilateur s'aperçoit que cette variable Continuer est fréquemment utilisée dans une boucle, sans être modifiée, et considère qu'il est plus rapide d'en faire une copie stockée dans un registre du processeur, d'accès beaucoup plus rapide que la mémoire principale.
Dès lors, le traitant du signal et la fonction main() n'utilisent plus le même objet physique et le programme est faux.
Pour éviter cet inconvénient, le langage C a prévu le qualifieur volatile qui interdit au compilateur de dupliquer une variable lors d'une éventuelle optimisation de la compilation.
Il garantit ainsi l'unicité de la variable.
La première ligne de code doit donc être réécrite :
volatile bool Continuer = true; |
Ce qualifieur doit être utilisé pour toute variable dont la valeur risque d'être modifiée par quelque dispositif que ce soit externe au programme : système d'exploitation, traitant d'interruption ou de signal, etc.
|
La norme ajoute :
|
Seul le type sig_atomic_t garantit l'atomicité de l'accès ŕ l'objet. Par accès, il faut seulement entendre le transfert mémoire <--> processeur, et non une opération plus complexe comme une incrémentation par exemple. Il est généralement défini comme équivalent au type de base int.
En d'autres termes, lorsque des informations doivent être échangées entre le programme et le traitant de signal, comme le fait la
stratégie E
présentée dans le TP sur les signaux, seule l'utilisation du type sig_atomic_t assure une portabilité générale.
Le programme précédent doit donc être réécrit :
volatile sig_atomic_t Continuer = 1;
void Derout (int NumSig) { Continuer = 0; } int main (int argc, char * argv [])
for (; Continuer; ); cout << "Fini" << endl; return 0; } // main() |
pragma Atomic (local_name);
pragma Atomic_Components (array_local_name); pragma Volatile (local_name); pragma Volatile_Components (array_local_name); |
Il faut remarquer que, contrairement à C/C++, Ada permet de rendre atomique l'accès à un objet non élémentaire. En fait, alors que le C/C++ met à disposition seulement un mécanisme matériel, donc très simple, le pragma Ada doit mettre en place un dispositif logiciel, donc beaucoup plus lourd.
Le manuel de référence indique aussi, qu'une constante dans un programme Ada peut cependant être modifiée par un dispositif externe.
© D. Mathieu
mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique