Comprendre le mot-clé "volatile" en langage C (et C++)
Le mot-clé volatile
en C est souvent mal compris ou sous-estimé. Pourtant il joue un role crucial dans certains contextes, notamment dans le domaine de l’embarqué.
Dans cet article, nous allons explorer les cas d’utilisation du mot-clé volatile
, pourquoi il est nécessaire et comment l’utiliser correctement.
Qu'est-ce que le mot-clé volatile
?
Avant de voir comment on l’utilise, voyons déjà ce qu’est le mot-clé volatile
.
Il s'agit d'un qualificateur de type, tout comme le mot-clé const
par exemple. Il sert à informer le compilateur que la valeur d’une variable peut changer de manière imprévisible, c’est-à-dire par une action extérieure au code qui est en train d'être exécuté (typiquement la fonction courante). En informant de cette façon le compilateur, on lui indique qu’il ne doit pas faire d’hypothèses sur la valeur de cette variable, et doit toujours lire sa valeur au moment de l’utiliser.
Pourquoi utiliser le mot-clé volatile
?
Les compilateurs modernes effectuent de nombreuses optimisations pour améliorer les performances d’exécution des programmes. L’une des optimisations possibles est de ne lire qu’une seule fois la valeur d’une variable, même si on l’utilise à plusieurs reprises dans une fonction. Cette optimisation permet d’économiser des accès au bus de mémoire, qui peuvent être coûteux sur des systèmes aux ressources limitées.
Cela est une bonne chose et fonctionne parfaitement si la variable en question n’est effectivement modifiée que par la fonction en cours d'exécution, mais cela peut avoir des effets indésirables si la variable peut être modifiée dans un autre contexte (voir les cas décris ci-dessous). Dans de tels cas, la fonction pourrait manquer un changement d’état de la variable.
Mémoire partagée par plusieurs threads
Dans les applications avec plusieurs threads (parfois aussi appelés tâches), certaines variables peuvent être accédées par plusieurs de ces threads. Cela peut servir de mécanisme de notification entre deux threads.
Par exemple, un thread A peut constamment monitorer (surveiller la valeur) un flag (aussi appelé "drapeau" ou "fanion" en français) pour effectuer une certaine action, alors qu’un thread B va modifier ce flag dans certaines situations pour notifier le thread A.
Mémoire partagée entre l'application et une routine d'interruption
De manière analogue au cas ci-dessus, certaines parties de la mémoire peuvent être partagées entre l’application et une routine d’interruption. Cela est très fréquent dans les systèmes embarqués, afin qu’une routine d’interruption notifie un événement à l’application, qui exécute une boucle principale (infinie).
Par exemple, dans le cas où une entrée GPIO passera à 1, la routine d’interruption correspondante va mettre un flag à 1. Ce flag est constamment surveillé par l’application qui, dans le cas où cette variable passe à 1, effectue une certaine action.
Registres matériels
Dans les systèmes embarqués, il est très commun que l’application surveille l’état d’un registre d’un périphérique, en particulier lorsque ce périphérique est utilisé en mode "polling". Dans ce cas aussi, la valeur lue peut changer à tout moment, de manière non-prévisible, car elle ne dépend pas du code, mais d’événements extérieurs. Dans ce cas également, le mot-clé volatile
sera utilisé.
Exemple d'utilisation
Dans l’exemple basique ci-dessous, admettons que l’on souhaite effectuer une action si un certain type d’interruption se produit, appelant la routine d’interruption interrupt_handler()
. Dans cette routine d’interruption, on mettra flag
à 1 (le 'U' indiquant qu'il s'agit d'une valeur non-signée).
En parallèle de ceci, la boucle principale de l’application va constamment surveiller l’état de flag
. Si flag
est détecté à 1, l’action représentée par le commentaire "Do something" est effectuée, et flag
est remis à zéro.
volatile uint8_t flag = 0U; // Shared variable
void interrupt_handler()
{
flag = 1U; // Notify the application
}
int main()
{
while (1) { // Main loop
if (flag == 1U) {
// Do something
Flag = 0U; // Once used, clear flag
}
}
}
Conclusion
Dans cet article, nous avons décrit ce qu’est le mot-clé volatile
et dans quels cas il est utilisé.
L'omission du mot-clé volatile
là où il est nécessaire peut créer des bugs parfois difficiles à trouver, en particulier dans le cas d’applications complexes. Il est donc essentiel d’avoir le réflexe de l’utiliser lors des cas d'utilisation décrits dans cet article.