11
votes

Pourquoi POSIX POSIX ne renvoie-t-il pas un vide volatile *?

MMAP renvoie un vide *, mais pas un vide volatile * . Si j'utilise du Mmeap pour mapper la mémoire partagée, un autre processus pourrait écrire sur cette mémoire, ce qui signifie que deux lectures ultérieures de la même pièce de mémoire peuvent générer des valeurs différentes - la situation exacte est signifiée à la situation. Alors pourquoi ne renvoie-t-il pas un vide volatile *?

Ma meilleure estimation est que si vous avez un processus qui écrit exclusivement sur le segment de mémoire partagé, il n'a pas besoin de regarder la mémoire partagée via des pointeurs volatils car il aura toujours la bonne compréhension de ce qui est présent; Toute optimisation du compilateur pour empêcher les lectures redondantes ne compteront pas, car il n'y a rien d'autre écriture et modifiant les valeurs sous ses pieds. Ou y a-t-il une autre raison historique? Je suis enclin à dire retourner volatilité volatile * serait une valeur par défaut plus sûre, et ceux qui souhaitent que cette optimisation puisse se lancer manuellement à vide *.

POSIX MMAP Description: http://opengroup.org/onlinepubs/007908775/xsh/ mmap.html


0 commentaires

6 Réponses :


2
votes

Je ne pense pas volatile fait ce que vous pensez.

Fondamentalement, il indique simplement au compilateur de ne pas optimiser la variable en stockant sa valeur dans un registre. Cela force à récupérer la valeur à chaque fois que vous y réfléchissez, ce qui est une bonne idée si un autre fil (ou autre) aurait pu le mettre à jour dans l'intervalle.

La fonction renvoie un vide *, mais il ne sera pas mis à jour, alors l'appelant volatil n'a pas de sens. Même si vous avez attribué la valeur à un vide volatile local *, rien ne serait gagné.


19 commentaires

Downvoted, vous devez expliquer votre réponse pour être utile. Je pense que vous supposez que je pense que volatile agit comme une barrière mémoire ou a quelque chose à voir avec le filetage, je suis pleinement conscient que ce n'est pas le cas.


@Joseph, volatile vous protégerait contre des modifications multithreadées à la valeur du Void * elle-même. Cela ne vous protège pas contre les modifications apportées aux données pointées vers.


Ah, vous avez édité votre réponse. "Mais ça ne sera pas mis à jour" <- Qu'est-ce qui ne sera pas mis à jour? La mémoire pointue de la mémoire va certainement être mise à jour et "Vide volatile *" signifie "pointeur à la mémoire pouvant changer".


@ JS BANGS: Non. Vous avez mal interprété le type. Ce serait le cas pour un "vide * volatile" non un "vide volatile *".


@Joseph: Comme JS a souligné, qu'est-ce qui ne sera pas mis à jour est la valeur renvoyée à vous. C'est juste une copie de la valeur stockée dans la structure de données.


@ Steven / JS: J'ai mis des balises de code autour du "vide volatile *" pour tenter d'indiquer que je veux dire que je veux dire de ce type, c'est-à-dire un pointeur à quelque chose qui est volatile, pas un volatile pointeur à quelque chose.


@Joseph: UHM, si vous allez en désespeciller le vide *, vous aurez besoin de baisser sur un type spécifique, auquel cas vous appliqueriez la volatilité là-bas. Malgré tout, pour les raisons données par JS et d'autres, je ne crois pas que cela fonctionnerait comme vous l'imaginez.


@Steven: Vous obtiendrez un avertissement si vous essayez de jeter la volatilité avec la plupart des compilateurs, mais vous n'obtiendrez pas de moulage d'avertissement d'un vide volatile * à volatile quelque_specific_type *. Si vous continuez strictement aux coulées de style C ++, vous ne pouvez pas le faire du tout sans explicitement en utilisant const_cast.


Joseph, pourquoi sur Terre devrait-il élargir sa réponse au lieu d'élargir votre question? Pourquoi sur Terre devrions-nous deviner ce que vous êtes au courant et ce qui n'est pas? Votre compréhension de volatile est fausse. L'homme investit son temps à vous expliquer, et au lieu d'essayer de travailler à travers ses explications ou de demander des éclaircissements, vous venez de la descendre silencieusement.


@Steven Il n'essaie pas de faire le pointeur retourné volatil, mais il veut que la région de la mémoire a souligné être volatilité, de sorte que si un autre thread / processus écrit à elle, les nouvelles valeurs seront relies.


@Roman: Regardez l'historique de modification de son poste. La réponse originale n'avait aucune explication du tout, c'était juste "Je ne pense pas que volatile fait ce que tu penses."


@Mark: Et ça ne va tout simplement pas fonctionner. Il n'y a aucune raison de croire que les modifications apportées à la structure de données entraîneront la valeur du pointeur retourné pour référence à une nouvelle cible. Il pourrait tout aussi facilement allouer le nouveau pointeur à la nouvelle valeur dans une nouvelle entrée dans sa table de hachage ou son arbre.


@Roman: J'ai initialement posté ma première phrase seulement avant d'expliquer davantage. Cela explique probablement pourquoi il la descendit initialement. Cependant, cela n'explique pas les trois bowvotes actuellement connectés à cette réponse.


@Roman Cheplylaka: sa compréhension du vide volatile * est correct. Vide volatile * est identique à celui de Void volatile * et non vide * volatile . Et void * volatile comme une valeur de retour d'une fonction n'aurait vraiment pas beaucoup de sens. vide volatile * est parfaitement logique et pourquoi cela n'est pas choisi pour MMAP peut être vu de NOS Réponse.


@Jens Gustedt: Vide volatile * n'a aucun sens. Cela signifie que quels que soient le pointeur pointe est volatile, mais qu'est-ce qu'un vide * pointe-t-il?


@Jens Gustedt: des pointeurs de données génériques qui ne peuvent pas être déréférencés. Depuis, un Void * Points sur Données non types non spécifiques (vos mots), pourquoi un qualificatif de type à Données non typées non spécifiques a-t-elle un sens?


@ atterminus: un vide volatile * devrait être baissé, dire, un volatile int * avant la déséroférance. Un casting normal peut ajouter volatile mais pas le supprimer, donc avoir volatile dans le retour de la fonction serait avoir un effet. Voir ailleurs pour un débat en bonne santé sur le fait que cet effet soit le souhaité.


@Steven Sudit Pouvez-vous indiquer quelques bits de spécification C qui indiquent qu'une distribution volatil ne peut être supprimée? Ce programme trivial C lorsqu'il est compilé avec gcc -wall -wextra volatile.c ne se plaint pas: volatile char * x; int main () {non signé Char * Y = (sans signé Char *) x; printf ("% c \ n", * y); retour 0; }


@Terminus: en C ++, vous ne pouvez supprimer qu'un const ou volatile à l'aide de const_cast , sauf si vous utilisez une distribution de style C dangereuse, comme dans vos exemples. Je me rends compte que la question mentionne C, mais toute ma programmation C de nos jours est techniquement C ++, ce qui me permet de tirer parti des améliorations de la langue même si je n'utilise pas les fonctionnalités ou les bibliothèques de l'OOOL.



7
votes

L'hypothèse profondément tenue à travers de nombreux systèmes logiciels est que la plupart des programmeurs sont des programmeurs séquentiels. Cela n'a que récemment commencé à changer.

mmap a des dizaines d'utilisations non liées à la mémoire partagée. Dans le cas où un programmeur écrit un programme multithreadé, ils doivent prendre leurs propres étapes pour assurer la sécurité. Protéger chaque variable avec un mutex n'est pas la valeur par défaut. De même, MMAP n'est pas supposons qu'un autre thread fera des accès contentieux sur le même segment de mémoire partagée, ou même qu'un segment si mappé sera accessible par un autre fil. < / p>

Je suis également non convaincue que marquer le retour du MMAP comme volatile aura un effet sur ceci. Un programmeur devrait toujours assurer la sécurité dans l'accès à la région mappée, non?


5 commentaires

Un accès à la mémoire ne serait "pas contentieux" dans ce sens si deux processus n'ont jamais accédé au même octet dans le segment, qui vaincre le point d'utilisation de la mémoire partagée. Vous devez toujours assurer la sécurité de l'accès à la région partagée, mais c'est un problème différent.


@Joseph Garvin: En réalité, il existe des situations valides en écriture à une fois écrits - de nombreuses situations où les accès ne sont pas conflictuels et la mémoire partagée est toujours un paradigme utile. Par exemple, la région de mémoire pourrait être créée et écrite et alors d'autres processus créés pour la lire. Depuis qu'ils existent après la commande d'écriture dans le programme, ils ne peuvent pas voir l'ancienne valeur.


True, si le processus d'écrivain termine son écriture avant que les lecteurs soient déjà créés, les lecteurs ne verront que la nouvelle valeur. Je n'avais pas examiné cette affaire. Je pense toujours que c'est la mauvaise valeur par défaut;)


Très retardé accepte: P Je pense que les cas d'utilisation de la mémoire non partagée de MMAP sont probablement la raison, et si C avait un système de type plus fort que vous souhaiteriez probablement du Mme divisé dans différentes fonctions (la mémoire de mémoire partagée pouvait donc avoir un type plus informatif).


On peut utiliser mmap pour mapper la mémoire physique dans plusieurs pages de mémoire virtuelle dans le même processus en code unique. Ce qui signifie qu'au cours de l'exécution, on peut avoir deux pointeurs, avec deux adresses virtuelles différentes, pointant vers la même adresse de mémoire physique, de telle sorte que les modifications effectuées par un pointeur d'écriture à travers un pointeur puisse être vue de l'autre. Si ces pointeurs étaient volatils, tout irait bien, mais comme ils ne le sont pas, les compilateurs peuvent optimiser le code à une non-sens.



4
votes

Être volatile ne couvrirait qu'une seule lecture (qui dépend de l'architecture pourrait être 32 bits ou quelque chose d'autre, et donc être assez limitatif. Souvent, vous devrez écrire plus d'un mot de machine, et vous aurez de toute façon introduire une sorte de verrouillage.

Même si elle était volatile, vous pouvez facilement disposer de 2 processus de lecture de valeurs différentes de la même mémoire, tout ce qu'il faut est un processus pour écrire à la mémoire dans la nanoseconde entre la lecture du 1. processus et la lecture à partir du processus 2. Sauf si vous pouvez garantir les 2 processus de lecture de la même mémoire dans presque exactement les mêmes cycles d'horloge.

Ainsi, il est assez inutile pour le MMAP () d'essayer de gérer ces choses et est mieux laissé au programmeur comment gérer l'accès à la mémoire et marquer le pointeur comme volatile si nécessaire - si la mémoire est partagée. - Vous doit que toutes les parties impliquées soient coopératives et conscientes de la manière dont ils peuvent mettre à jour la mémoire par rapport à l'autre - quelque chose hors de portée du MMAP, et quelque chose de volérable ne résoudra pas.


18 commentaires

Vide volatile * et Void volatile * sont exactement les mêmes. La version différente est void * volatile .


"Être volatile ne couvrirait qu'une seule lecture" <- Je ne suis pas sûr de ce que vous voulez dire. Être volatil signifie que chaque utilisation de la variable forcera une nouvelle lecture. Comment cela ne couvrirait-il qu'une seule lecture?


Cela signifie simplement que l'ajout de volatilité, vous pouvez lire une valeur 32 bits et toujours obtenir un résultat constant (sur la plupart des machines 32 bits de toute façon) même si quelqu'un d'autre met à jour la valeur 32 bits, tout en lisant une valeur de 64 bits que vous pourriez obtenir un résultat incohérent - Volatile ne va pas "protéger" vous ou vous garantir que vous voyez la valeur la plus récente dans ce cas. Et ce n'est que le bon cas, d'autres processus pourraient être manuilés des morceaux arbitroyés d'octets.


@nos: Ce n'est pas ma compréhension de volatile. Si je fais une structure de 100 octets et que je le déclare volatil, on lit de toute partie de celle-ci ne sera pas mise en cache. Je ne pense pas que ce soit une architecture spécifique. La volatille partage la même sémantique de propagation que Const.


@nos: Droite, la lecture doit être plus grande que la largeur du bus, qui signifie généralement 64 bits ces jours-ci. Ajoutez des complications pour l'alignement et la réponse est autrement correcte.


@Joseph: J'ai vu volatile utilisé sur des choses comme des entiers, pas des pointeurs. Je n'ai aucune raison de croire que le compilateur honorera la façon dont vous imaginez.


@Steven: Les normes C et C ++ permettent tous deux la totalité de volatile d'être utilisée sur des types arbitraires. La volatilité est implicitement appliquée aux membres des structures et des classes, tout comme le const est.


@Joseph Garvin Vous avez raison de ne pas être mis en cache (bien que sur une machine multicœur x86, elle pourrait être mise en cache dans le cache L1 / L2, à moins que l'écriture ne soit préfixée avec l'opcode de verrouillage - qui ajoutant une volatilité ne fera pas), mais rien ne cesse Quelqu'un d'autre de changer le 2. octet dans l'un de vos entiers dans cette structure pendant que vous le lisez - le point est que le point de retour de MMAP, un pointeur volatil résout presque rien, il appartient donc au programmeur de faire ce pointeur volatil si nécessaire et son au programmeur pour traiter (la question beaucoup plus grande) de l'accès de verrouillage / coopératif.


@Joseph: Je pense que nos Nos répondit pour moi.


@nos Le préfixe de verrouillage n'a rien à voir avec la mise en cache. Il ne sert que d'instruire le processeur que le prochain opcode nécessite un accès exclusif à l'emplacement de la mémoire.


@Terminus il sera serveur comme barrière de mémoire.


@nos Oui, il servirait de barrière. Pourquoi devrait-il importer la mise en cache dans L1 / L2?


@Terminus, vous serez donc assuré qu'un autre processus modifiant la mémoire le rend visible pour tous les autres.


@ Steven / NOS: une barrière de mémoire ne résoudra pas la question sauf si elle est combinée à la volatilité, disant donc qu'il résoue que rien ne se trompe. Si vous n'utilisez que volatile, il ne sera que garantit qu'il n'est pas mis en cache dans un registre par le compilateur, de sorte que les utilisations ultérieures de la variable iront vraiment à la mémoire / cache. Si vous utilisez une barrière de mémoire, les modifications de la mémoire / cache seront reflétées à travers les noyaux, mais que / ne comporteront pas / si la valeur a été stockée dans un registre pour une utilisation ultérieure et que le compilateur a généré le code qui réduit simplement ce registre .


@ Steven / NOS: également probablement lorsque le MMAP a été créé un noyau unique était la norme, où la question de la volatilité était présente, mais le problème de la cohérence de cache n'était pas.


@Joseph: volatile est, dans la pratique, un moyen de désactiver une optimisation non désirée. À savoir une fois qu'une variable est référencée, le compilateur est libre de supposer qu'il n'y a pas d'alias asynchrone et donc le cache dans un registre. Si la variable est un pointeur sur un octet, ce qui en fait un pointeur à un octet volatil, c'est juste que le compilateur ne doit pas cacher les résultats de la déséroférance. Mais aurait-il fait en premier lieu? Ce n'est pas une question rhétorique, mais une pratique pratique. De travailler avec optimisé C ++, je soupçonne que la plupart des compilateurs n'ont aucune optimisation de ce type pour désactiver.


@Steven: Whaaaat? La plupart des compilateurs mettront définitivement cacher des valeurs chargées dans des registres. L'allocation de registre est l'un des aspects les plus importants et les plus recherchés d'un compilateur.


@Joseph: Je ne crois pas que vous comprenez ce que j'ai dit à propos de la mise en cache. Tout d'abord, cela n'a rien à voir avec si la valeur est conservée dans un registre ou en mémoire. Le problème est de savoir si appeler a = p [x]; , où x est une variable, sera mis en cache d'une manière qui évite le désherbage p La prochaine fois que l'expression est évaluée.



0
votes

Il est probablement fait de cette façon pour des raisons de performance, ne donnant aucun supplément par défaut. Si vous savez que sur votre architecture particulière qui écrit / lit ne sera pas réorganisée par le processeur, vous n'avez peut-être pas besoin de volatilité du tout (éventuellement en conjonction avec une autre synchronisation). EDIT: C'était juste un exemple - il peut y avoir une variété d'autres cas où vous savez que vous n'avez pas besoin de forcer une relecture chaque fois que la mémoire est accessible.

Si vous devez vous assurer que toutes les adresses sont lues de la mémoire à chaque fois qu'elles sont accessibles, Const_cast (ou C-style Cast) volatil sur la valeur de retour vous-même.


1 commentaires

Êtes-vous sûr que ça aura un effet?



1
votes

Le type VOID volatile * ou VOID * volatile est absurde: vous ne pouvez pas désexcorer un vide * , donc il n'a pas de sens spécifier des qualificateurs de type.

Et, puisque vous avez quand même besoin d'une distribution sur char * ou quel que soit votre type de données, c'est peut-être le bon endroit pour spécifier la volatilité. Ainsi, l'API telle que défini bien les étapes secondaires la responsabilité de marquer la mémoire modifiable-sous-vos-pieds / volatile.

Cela dit, d'une grande image POV, je suis d'accord avec vous: MMAP doit avoir un type de retour indiquant que le compilateur ne doit pas mettre en cache cette plage.


3 commentaires

Les électeurs de bas-temps pourraient-ils faire remarquer pourquoi cela est révolutionnaire?


Je ne t'ai pas bownviche, mais je soupçonne que c'est parce que votre réponse donne sur les problèmes couverts par la CAF et que cela a été mis en place dans un commentaire que vous avez apporté.


Vous êtes probablement descendu parce que votre premier point ne tient pas. Vide volatile * a ses utilisations. Principalement, il communique clairement que la lecture de plusieurs fois à travers ce pointeur pourrait renvoyer différentes valeurs. Essayer d'assigner un pointeur vide volatile à un pointeur vide non qualifié donne un avertissement de compilateur, car vous supprimez ce qualificatif. Ideme va pour l'utiliser comme un argument à une fonction. Ainsi, si cette propriété est importante pour vous, un "code> VOI volatile" / code> Le pointeur augmente votre sécurité.



8
votes

Mise en œuvre de la mémoire partagée n'est qu'un petit sous-ensemble des utilisations de MMAP () . En fait, les utilisations les plus courantes créent des mappages privés, à la fois anonymes et reculés par fichier. Cela signifie que, même si nous avons accepté votre affirmation concernant la nécessité d'un point de point -QualIfidifié pour l'accès à la mémoire partagée, un tel qualificateur serait superflu dans le cas général.

N'oubliez pas que vous pouvez toujours Ajouter qualifications finales à un type de pointeur sans coulée, mais vous ne pouvez pas les supprimer. Ainsi, avec la déclaration actuelle MMAP () , vous pouvez le faire à la fois ce: xxx

et ceci: xxx < / Pré>

Avec votre suggestion, les utilisateurs de l'affaire Common devraient jeter l'état volatile.


0 commentaires