Pourquoi est-il volatile
nécessaire en C? A quoi cela sert? Que va-t-il faire?
c
declaration
volatile
Jonathan Leffler
la source
la source
Réponses:
Volatile indique au compilateur de ne pas optimiser tout ce qui a à voir avec la variable volatile.
Il existe au moins trois raisons courantes de l'utiliser, toutes impliquant des situations dans lesquelles la valeur de la variable peut changer sans action à partir du code visible: lorsque vous vous connectez avec du matériel qui modifie la valeur elle-même; quand il y a un autre thread en cours d'exécution qui utilise également la variable; ou quand il y a un gestionnaire de signal qui pourrait changer la valeur de la variable.
Disons que vous avez un petit matériel qui est mappé quelque part en RAM et qui a deux adresses: un port de commande et un port de données:
Maintenant, vous voulez envoyer une commande:
Cela semble facile, mais il peut échouer car le compilateur est libre de modifier l'ordre dans lequel les données et les commandes sont écrites. Cela entraînerait notre petit gadget à émettre des commandes avec la valeur de données précédente. Jetez également un œil à la boucle d'attente pendant l'occupation. Celui-là sera optimisé. Le compilateur essaiera d'être intelligent, ne lira la valeur d'isbusy qu'une seule fois, puis entrera dans une boucle infinie. Ce n'est pas ce que tu veux.
Le moyen de contourner ce problème consiste à déclarer le gadget de pointeur comme volatile. De cette façon, le compilateur est obligé de faire ce que vous avez écrit. Il ne peut pas supprimer les affectations de mémoire, il ne peut pas mettre en cache les variables dans les registres et il ne peut pas non plus changer l'ordre des affectations:
Ceci est la bonne version:
la source
volatile
en C a en fait vu le jour dans le but de ne pas mettre automatiquement en cache les valeurs de la variable. Il indiquera au compilateur de ne pas mettre en cache la valeur de cette variable. Il va donc générer du code pour prendre la valeur de lavolatile
variable donnée de la mémoire principale à chaque fois qu'il la rencontre. Ce mécanisme est utilisé car à tout moment la valeur peut être modifiée par l'OS ou n'importe quelle interruption. Donc, l'utilisationvolatile
nous aidera à accéder à chaque fois à la valeur.la source
volatile
était de permettre aux compilateurs d'optimiser le code tout en permettant aux programmeurs de réaliser la sémantique qui serait obtenue sans de telles optimisations. Les auteurs de la norme s'attendaient à ce que les implémentations de qualité prennent en charge toutes les sémantiques utiles compte tenu de leurs plates-formes cibles et de leurs champs d'application, et ne s'attendaient pas à ce que les rédacteurs de compilateurs cherchent à offrir la sémantique de qualité la plus basse conforme à la norme et n'étaient pas à 100%. stupide (notez que les auteurs de la norme reconnaissent explicitement dans la justification ...Les
volatile
gestionnaires de signaux sont également utilisés . Si vous avez un code comme celui-ci:Le compilateur est autorisé à remarquer que le corps de la boucle ne touche pas la
quit
variable et convertit la boucle enwhile (true)
boucle. Même si laquit
variable est définie sur le gestionnaire de signaux pourSIGINT
etSIGTERM
; le compilateur n'a aucun moyen de le savoir.Cependant, si la
quit
variable est déclaréevolatile
, le compilateur est obligé de la charger à chaque fois, car elle peut être modifiée ailleurs. C'est exactement ce que vous voulez dans cette situation.la source
quit
, le compilateur peut l'optimiser en une boucle constante, en supposant qu'il n'y a aucun moyen dequit
changer entre les itérations. NB: Ce n'est pas nécessairement un bon substitut à une programmation threadsafe réelle.volatile
ou d'autres marqueurs, il supposera que rien en dehors de la boucle ne modifie cette variable une fois qu'elle entre dans la boucle, même s'il s'agit d'une variable globale.extern int global; void fn(void) { while (global != 0) { } }
avecgcc -O3 -S
et regardez le fichier d'assemblage résultant, sur ma machine c'est le casmovl global(%rip), %eax
;testl %eax, %eax
;je .L1
;.L4: jmp .L4
, c'est-à-dire une boucle infinie si le global n'est pas nul. Essayez ensuite d'ajoutervolatile
et de voir la différence.volatile
indique au compilateur que votre variable peut être modifiée par d'autres moyens que le code qui y accède. par exemple, il peut s'agir d'un emplacement de mémoire mappé d'E / S. Si cela n'est pas spécifié dans de tels cas, certains accès variables peuvent être optimisés, par exemple, son contenu peut être conservé dans un registre, et l'emplacement de mémoire ne peut pas être relu.la source
Voir cet article d'Andrei Alexandrescu, " volatile - le meilleur ami du programmeur multithread "
L'article s'applique à la fois à
C
etC++
.Voir également l'article " C ++ et les dangers du verrouillage à double vérification " de Scott Meyers et Andrei Alexandrescu:
la source
volatile
ne garantit pas l'atomicité.Ma simple explication est:
Dans certains scénarios, basé sur la logique ou le code, le compilateur optimisera les variables qu'il pense ne pas changer. Le
volatile
mot-clé empêche l'optimisation d'une variable.Par exemple:
A partir du code ci-dessus, le compilateur peut penser qu'il
usb_interface_flag
est défini comme 0, et que dans la boucle while, il sera à jamais nul. Après l'optimisation, le compilateur le traitera commewhile(true)
tout le temps, résultant en une boucle infinie.Pour éviter ce genre de scénarios, nous déclarons l'indicateur comme volatile, nous disons au compilateur que cette valeur peut être modifiée par une interface externe ou un autre module de programme, c'est-à-dire, ne l'optimisez pas. C'est le cas d'utilisation pour volatile.
la source
Une utilisation marginale de volatile est la suivante. Supposons que vous souhaitiez calculer la dérivée numérique d'une fonction
f
:Le problème est que ce
x+h-x
n'est généralement pas égal à enh
raison d'erreurs d'arrondi. Pensez-y: lorsque vous soustrayez des nombres très proches, vous perdez beaucoup de chiffres significatifs qui peuvent ruiner le calcul de la dérivée (pensez 1.00001 - 1). Une solution de contournement possible pourrait êtremais en fonction de votre plate-forme et des commutateurs du compilateur, la deuxième ligne de cette fonction peut être effacée par un compilateur optimisant de manière agressive. Alors tu écris à la place
pour forcer le compilateur à lire l'emplacement de mémoire contenant hh, perdant ainsi une éventuelle opportunité d'optimisation.
la source
h
ou lahh
formule dérivée? Lorsquehh
est calculé, la dernière formule l'utilise comme la première, sans différence. Peut-être que ça devrait l'être(f(x+h) - f(x))/hh
?h
ethh
est qu'ellehh
est tronquée à une puissance négative de deux par l'opérationx + h - x
. Dans ce cas,x + hh
etx
diffèrent exactement parhh
. Vous pouvez aussi prendre votre formule, elle donnera le même résultat, puisquex + h
etx + hh
sont égales (c'est le dénominateur qui est important ici).x1=x+h; d = (f(x1)-f(x))/(x1-x)
? sans utiliser le volatile.-ffast-math
ou équivalent.Il y a deux utilisations. Ceux-ci sont spécialement utilisés plus souvent dans le développement embarqué.
Le compilateur n'optimisera pas les fonctions qui utilisent des variables définies avec un mot clé volatile
Volatile est utilisé pour accéder aux emplacements de mémoire exacts dans la RAM, la ROM, etc. Il est utilisé plus souvent pour contrôler les périphériques mappés en mémoire, accéder aux registres du processeur et localiser des emplacements de mémoire spécifiques.
Voir des exemples avec la liste des assemblages. Re: Utilisation du mot-clé C «volatile» dans le développement intégré
la source
Volatile est également utile lorsque vous souhaitez forcer le compilateur à ne pas optimiser une séquence de code spécifique (par exemple pour écrire un micro-benchmark).
la source
Je mentionnerai un autre scénario où les volatils sont importants.
Supposons que vous mappiez en mémoire un fichier pour des E / S plus rapides et que ce fichier puisse changer en arrière-plan (par exemple, le fichier ne se trouve pas sur votre disque dur local, mais est plutôt servi sur le réseau par un autre ordinateur).
Si vous accédez aux données du fichier mappé en mémoire via des pointeurs vers des objets non volatils (au niveau du code source), le code généré par le compilateur peut extraire les mêmes données plusieurs fois sans que vous en soyez conscient.
Si ces données changent, votre programme peut utiliser deux ou plusieurs versions différentes des données et entrer dans un état incohérent. Cela peut conduire non seulement à un comportement logiquement incorrect du programme, mais également à des failles de sécurité exploitables dans celui-ci s'il traite des fichiers non fiables ou des fichiers provenant d'emplacements non fiables.
Si vous vous souciez de la sécurité, et vous devriez, c'est un scénario important à considérer.
la source
volatile signifie que le stockage est susceptible de changer à tout moment et d'être modifié, mais quelque chose échappant au contrôle du programme utilisateur. Cela signifie que si vous référencez la variable, le programme doit toujours vérifier l'adresse physique (c'est-à-dire une entrée mappée fifo), et ne pas l'utiliser de manière mise en cache.
la source
Le Wiki dit tout sur
volatile
:Et la doc du noyau Linux fait également une excellente notation sur
volatile
:la source
À mon avis, il ne faut pas trop en attendre
volatile
. Pour illustrer cela, regardez l'exemple de la réponse très appréciée de Nils Pipenbrinck .Je dirais que son exemple ne convient pas
volatile
.volatile
est uniquement utilisé pour: empêcher le compilateur d'effectuer des optimisations utiles et souhaitables . Il ne s'agit pas du thread safe, de l'accès atomique ou même de l'ordre de la mémoire.Dans cet exemple:
L'
gadget->data = data
avantgadget->command = command
seulement n'est garanti que dans le code compilé par le compilateur. Au moment de l'exécution, le processeur réordonne peut-être encore l'affectation des données et des commandes, en fonction de l'architecture du processeur. Le matériel peut obtenir des données incorrectes (supposons que le gadget soit mappé sur les E / S matérielles). La barrière mémoire est nécessaire entre les données et l'affectation des commandes.la source
volatile
dégrader les performances sans raison. Quant à savoir s'il est suffisant, cela dépendra d'autres aspects du système que le programmeur peut en savoir plus que le compilateur. D'un autre côté, si un processeur garantit qu'une instruction d'écrire à une certaine adresse videra le cache du CPU mais qu'un compilateur ne fournira aucun moyen de vider les variables mises en cache de registre dont le CPU ne sait rien, vider le cache serait inutile.Dans le langage conçu par Dennis Ritchie, chaque accès à un objet, autre que des objets automatiques dont l'adresse n'avait pas été prise, se comporterait comme s'il calculait l'adresse de l'objet, puis lisait ou écrivait le stockage à cette adresse. Cela a rendu le langage très puissant, mais les possibilités d'optimisation étaient très limitées.
Bien qu'il aurait pu être possible d'ajouter un qualificatif qui inviterait un compilateur à supposer qu'un objet particulier ne serait pas modifié de manière étrange, une telle hypothèse serait appropriée pour la grande majorité des objets dans les programmes C, et elle aurait été impossible d’ajouter un qualificatif à tous les objets pour lesquels une telle hypothèse serait appropriée. D'un autre côté, certains programmes doivent utiliser des objets pour lesquels une telle hypothèse ne serait pas vérifiée. Pour résoudre ce problème, la norme indique que les compilateurs peuvent supposer que les objets qui ne sont pas déclarés
volatile
n'auront pas leur valeur observée ou modifiée d'une manière qui échappe au contrôle du compilateur, ou serait en dehors de la compréhension d'un compilateur raisonnable.Étant donné que différentes plates-formes peuvent avoir des façons différentes d'observer ou de modifier des objets en dehors du contrôle d'un compilateur, il est approprié que les compilateurs de qualité pour ces plates-formes diffèrent dans leur traitement exact de la
volatile
sémantique. Malheureusement, parce que la norme n'a pas suggéré que les compilateurs de qualité destinés à la programmation de bas niveau sur une plate-forme devraient gérervolatile
d'une manière qui reconnaîtrait tous les effets pertinents d'une opération de lecture / écriture particulière sur cette plate-forme, de nombreux compilateurs sont loin de le faire. donc d'une manière qui rend plus difficile le traitement de choses comme les E / S en arrière-plan d'une manière efficace mais qui ne peut pas être interrompue par les "optimisations" du compilateur.la source
En termes simples, il indique au compilateur de ne faire aucune optimisation sur une variable particulière. Les variables qui sont mappées au registre de périphérique sont modifiées indirectement par le périphérique. Dans ce cas, volatile doit être utilisé.
la source
Un volatile peut être modifié depuis l'extérieur du code compilé (par exemple, un programme peut mapper une variable volatile à un registre mappé en mémoire.) Le compilateur n'appliquera pas certaines optimisations au code qui gère une variable volatile - par exemple, il a gagné '' t le charger dans un registre sans l'écrire dans la mémoire. Ceci est important lorsqu'il s'agit de registres matériels.
la source
Comme beaucoup le suggèrent à juste titre ici, l'utilisation populaire du mot-clé volatile consiste à ignorer l'optimisation de la variable volatile.
Le meilleur avantage qui vient à l'esprit, et qui mérite d'être mentionné après avoir lu à propos de volatile, c'est - pour éviter le retour en arrière de la variable en cas de
longjmp
. Un saut non local.Qu'est-ce que ça veut dire?
Cela signifie simplement que la dernière valeur sera conservée après avoir déroulé la pile , pour revenir à une trame de pile précédente; généralement en cas de scénario erroné.
Puisqu'il serait hors de portée de cette question, je ne vais pas entrer dans les détails de
setjmp/longjmp
ici, mais cela vaut la peine d'être lu à ce sujet; et comment la fonction de volatilité peut être utilisée pour conserver la dernière valeur.la source
il ne permet pas au compilateur de changer automatiquement les valeurs des variables. une variable volatile est à usage dynamique.
la source