web-dev-qa-db-fra.com

Quelle est la différence entre une variable globale statique et une variable volatile statique?

J'ai utilisé une variable globale statique et une variable volatile statique dans la portée du fichier,

les deux sont mis à jour par un ISR et une boucle principale et une boucle principale vérifient la valeur de la variable.

ici, lors de l'optimisation, ni la variable globale ni la variable volatile ne sont optimisées. Ainsi, au lieu d'utiliser une variable volatile, une variable globale résout le problème.

Est-il donc bon d'utiliser une variable globale au lieu de volatile?

Une raison spécifique pour utiliser volatile statique ??

Tout exemple de programme serait appréciable.

Merci d'avance..

28
Manoj Doubts

Ce sont des choses différentes. Je ne suis pas un expert en sémantique volatile. Mais je pense que cela a du sens ce qui est décrit ici.

Global

Global signifie simplement que l'identifiant en question est déclaré à portée de fichier. Il existe différentes portées, appelées fonction (où les goto-labels sont définis dans), fichier (où résident les globaux), block (où résident les variables locales normales) et prototype de fonction (où résident les paramètres de fonction). Ce concept n'existe que pour structurer la visibilité des identifiants. Cela n'a rien à voir avec les optimisations.

Statique

static est une durée de stockage (nous ne l'examinerons pas ici) et un moyen de donner un nom déclaré dans le lien interne de la portée du fichier. Cela peut être fait pour des fonctions ou des objets uniquement requis dans une unité de traduction. Un exemple typique pourrait être une fonction help imprimant les paramètres acceptés, et qui n'est appelée qu'à partir de la fonction main définie dans le même fichier .c.

6.2.2/2 dans un projet C99:

Si la déclaration d'un identificateur de portée de fichier pour un objet ou une fonction contient le spécificateur de classe de stockage statique, l'identificateur a une liaison interne.

Le lien interne signifie que l'identifiant n'est pas visible en dehors de l'unité de traduction actuelle (comme la fonction help ci-dessus).

Volatil

La volatilité est une chose différente: ( 6.7.3/6 )

Un objet dont le type est volatile peut être modifié de manières inconnues de l'implémentation ou avoir d'autres effets secondaires inconnus. Par conséquent, toute expression faisant référence à un tel objet doit être évaluée strictement selon les règles de la machine abstraite, comme décrit au 5.1.2.3. En outre, à chaque point de séquence, la dernière valeur stockée dans l'objet doit correspondre à celle prescrite par la machine abstraite, sauf modification par les inconnues mentionnées précédemment.

La norme fournit un excellent exemple pour un exemple où volatile serait redondant ( 5.1.2.3/8):

Une implémentation pourrait définir une correspondance biunivoque entre la sémantique abstraite et la sémantique réelle: à chaque point de séquence, les valeurs des objets réels seraient en accord avec celles spécifiées par la sémantique abstraite. Le mot clé volatile serait alors redondant.

Les points de séquence sont des points où l'effet des effets secondaires concernant la machine abstraite est terminé (c'est-à-dire que les conditions externes comme les valeurs des cellules de mémoire ne sont pas incluses). Entre la droite et la gauche de && Et ||, Après ; Et retour d'un appel de fonction se trouvent des points de séquence par exemple.

La sémantique abstraite est ce que le compilateur peut déduire en ne voyant que la séquence de code dans un programme particulier. Les effets des optimisations ne sont pas pertinents ici. sémantique réelle inclut l'effet des effets secondaires effectués en écrivant sur des objets (par exemple, changement de cellules de mémoire). Qualifier un objet de volatile signifie que l'on obtient toujours la valeur d'un objet directement de la mémoire ("telle que modifiée par les inconnues"). La norme ne mentionne nulle part les threads, et si vous devez vous fier à l'ordre des modifications ou à l'atomicité des opérations, vous devez utiliser des moyens dépendants de la plateforme pour vous en assurer.

Pour un aperçu facile à comprendre, Intel a un excellent article à ce sujet ici .

Qu'est-ce que je devrais faire maintenant?

Continuez à déclarer vos données de portée de fichier (globales) comme volatiles. Les données globales en elles-mêmes ne signifient pas que la valeur des variables sera égale à la valeur stockée en mémoire. Et statique ne fait que vos objets locaux à l'unité de traduction actuelle (les fichiers .c Actuels et tous les autres fichiers # inclus par elle).

21

Permettez-moi tout d'abord de mentionner qu'une variable globale statique est identique à une variable globale, sauf que vous limitez la variable à la portée du fichier. C'est à dire. vous ne pouvez pas utiliser cette variable globale dans d'autres fichiers via le mot clé extern.

Vous pouvez donc réduire votre question à des variables globales vs des variables volatiles.

Maintenant sur volatile:

Comme const, volatile est un modificateur de type.

Le mot clé volatile a été créé pour empêcher les optimisations du compilateur susceptibles de rendre le code incorrect, en particulier en cas d'événements asynchrones.

Les objets déclarés comme volatile ne peuvent pas être utilisés dans certaines optimisations.

Le système lit toujours la vraie valeur actuelle d'un objet volatil au point où il est utilisé, même si une instruction précédente demandait une valeur au même objet. De plus, la valeur de l'objet est écrite immédiatement lors de l'affectation. Cela signifie qu'il n'y a pas de mise en cache d'une variable volatile dans un registre CPU.

Le Dr Jobb a un excellent article sur les produits volatils .

Voici un exemple tiré de l'article du Dr Jobb:

class Gadget
{
public:
    void Wait()
    {
        while (!flag_)
        {
            Sleep(1000); // sleeps for 1000 milliseconds
        }
    }
    void Wakeup()
    {
        flag_ = true;
    }
    ...
private:
    bool flag_;
};

Si le compilateur voit que Sleep() est un appel externe, il supposera que Sleep() ne peut pas changer la valeur de la variable flag_. Le compilateur peut donc stocker la valeur de flag_ Dans un registre. Et dans ce cas, cela ne changera jamais. Mais si un autre thread appelle le réveil, le premier thread lit toujours à partir du registre du CPU. Wait() ne se réveillera jamais.

Alors pourquoi ne pas simplement mettre les variables en cache dans les registres et éviter complètement le problème? Il s'avère que cette optimisation peut vraiment vous faire gagner beaucoup de temps dans l'ensemble. C/C++ vous permet donc de le désactiver explicitement via le mot clé volatile.

Le fait ci-dessus que flag_ Était une variable membre, et non une variable globale (ni globale statique) n'a pas d'importance. L'explication après l'exemple donne le raisonnement correct même si vous traitez avec des variables globales (et des variables globales statiques).

Une idée fausse courante est que déclarer une variable volatile est suffisant pour garantir la sécurité des threads. Les opérations sur la variable ne sont toujours pas atomiques, même si elles ne sont pas "mises en cache" dans les registres

volatile avec des pointeurs:

Volatile avec des pointeurs, fonctionne comme const avec des pointeurs.

Une variable de type volatile int * Signifie que la variable vers laquelle pointe le pointeur est volatile.

Une variable de type int * volatile Signifie que le pointeur lui-même est volatile.

42
Brian R. Bondy

Le mot clé "volatile" suggère au compilateur de ne pas effectuer certaines optimisations sur le code impliquant cette variable; si vous utilisez simplement une variable globale, rien n'empêche le compilateur d'optimiser à tort votre code.

Exemple:

#define MYPORT 0xDEADB33F

volatile char *portptr = (char*)MYPORT;
*portptr = 'A';
*portptr = 'B';

Sans "volatile", la première écriture peut être optimisée.

14
Gabriele D'Antona

Le mot clé volatile indique au compilateur de s'assurer que la variable ne sera jamais mise en cache. Tous les accès à celui-ci doivent être effectués de manière cohérente afin d'avoir une valeur cohérente entre tous les threads. Si la valeur de la variable doit être modifiée par un autre thread pendant que vous avez une boucle vérifiant le changement, vous voulez que la variable soit volatile car il n'y a aucune garantie qu'une valeur de variable régulière ne sera pas mise en cache à un moment donné et la boucle supposera simplement qu'il reste le même.

Variable volatile sur Wikipedia

4
Krunch

J'ai +1 à la réponse de Friol. Je voudrais ajouter quelques précisions car il semble y avoir beaucoup de confusions dans les différentes réponses: C volatile n'est pas Java volatile.

Donc tout d'abord, les compilateurs peuvent faire beaucoup d'optimisations en fonction du flux de données de votre programme, volatile en C empêche cela, il s'assure que vous chargez/stockez vraiment à l'emplacement à chaque fois (au lieu d'utiliser des registres d'effacement par exemple) . Il est utile lorsque vous avez une mémoire mappée IO port, comme l'a souligné Friol.

Volatile en C n'a RIEN à voir avec les caches matériels ou le multithreading. Il n'insère pas de barrières mémoire, et vous n'avez absolument aucune garantie sur l'ordre des opérations si deux threads y accèdent. Le mot clé volatile de Java fait exactement cela cependant: insérer des clôtures de mémoire là où c'est nécessaire.

3
Piotr Lesnicki

Ils peuvent ne pas être différents dans votre environnement actuel, mais des changements subtils peuvent affecter le comportement.

  • Matériel différent (plus de processeurs, architecture de mémoire différente)
  • Une nouvelle version du compilateur avec une meilleure optimisation.
  • Variation aléatoire du timing entre les threads. Un problème ne peut se produire qu'une seule fois sur 10 millions.
  • Différents paramètres d'optimisation du compilateur.

Il est beaucoup plus sûr à long terme d'utiliser des constructions multithreading appropriées dès le début, même si les choses semblent fonctionner pour l'instant sans elles.

Bien sûr, si votre programme n'est pas multi-thread, cela n'a pas d'importance.

3
Darron