3 votes

erlang peut-il réutiliser les identifiants de processus ? Si c'est le cas, comment être sûr de l'exactitude ?

(1) Après la mort d'un processus, est-il possible que son pid est réaffecté à un autre processus créé par spawn() ?

(2) Si c'est le cas, comment une communication peut-elle être sécurisée ? Par exemple, l'envoi d'une réponse à la Pid de l'expéditeur du message. Si l'expéditeur s'est écrasé, comment pouvons-nous savoir que cette Pid n'appartient pas maintenant à un autre processus ?

(3) Quelles sont les garanties sur la réutilisation des pid ? Par exemple, y a-t-il un intervalle minimum avant de réutiliser un pid ?

(4) Que fait-on habituellement pour éviter un bogue dû à la réutilisation du pid ? Est-il simplement ignoré ?

6voto

zxq9 Points 384

Pas mal de questions...

  1. Oui. Les NIDs peuvent être réutilisés.
  2. La communication est "sûre" parce que les chances de toucher un PID réutilisé avec une certaine cohérence sont infinitésimalement faibles. Les erreurs de réseau et de matériel sont profondément plus probable que ça. Nous concevons des programmes pour qu'ils soient robustes face à cette situation. et cela implique de programmer de manière à pouvoir accepter des messages erronés et/ou des processus qui meurent spontanément (le coût d'une telle mort est intégré dans le cycle de redémarrage).
  3. Cette garantie repose, du moins d'après mon expérience avec la principale implémentation EVM (je n'en suis pas sûr avec HiPE, par exemple), sur le fait que l'espace d'affectation des entiers est assez grand et l'envoi de messages est beaucoup plus rapide que le wrapping d'entiers dans la plupart des cas.
  4. La clé pour éviter les bizarreries basées sur la réutilisation du PID est de le combiner avec quelque chose d'autre qui est également une valeur unique dans le cadre d'une utilisation typique - et cela prend généralement la forme d'une Référence Erlang .

Sans vouloir être exhaustif d'une part et trop pédant d'autre part, prenons un exemple concret : la fonction OTP. gen_server:call/2,3 .

Lorsque vous utilisez gen_server:call/2,3 le module gen_server génère une balise de message combinée qui ressemble à ceci {self(), make_ref()} en plus de surveiller le processus qui fait l'objet du message. Le processus expéditeur est assuré qu'au moins si le processus qu'il appelle meurt avant l'envoi d'une réponse, un message de sortie de surveillance sera reçu à la place de la réponse, et que le PID du processus mourant correspondra à celui qu'il vient d'appeler. Le processus récepteur qui reçoit le message recevra à la fois le PID de l'expéditeur et le PID du destinataire. et une référence Erlang qui est garantie d'être localement unique (au moins pour un temps raisonnablement long -- je crois que l'espace d'unicité est quelque part dans les milliards). Lorsqu'il envoie sa réponse, le processus récepteur doit également connaître cette référence ainsi que d'être adressé par le PID utilisé à l'origine pour l'envoi.

Il est possible (bien que extrêmement improbable) que le processus émetteur ait pu mourir et qu'un nouveau processus ait pu être relancé avec le même PID, mais il est pratiquement impossible qu'un autre processus soit relancé avec le même PID. et être bloqué sur un gen_server:call/2,3 qui se trouve avoir une référence d'exécution interne identique à celle de l'ancien appel mort.

En plus de cette quasi-impossibilité, envisageons un monde où cette chose totalement bizarre se produirait réellement et où toutes les mesures de protection échoueraient...

(de l'ordre de 2^64 * 2^64 * chance_of_failure_on_this_tiny_scale() )

Le processus d'envoi recevrait un message de réponse bizarre, et échouerait presque certainement une assertion, mourrait à la ligne suivante et redémarrerait dans un état connu. La probabilité que ce même problème se produise deux fois est probablement inférieure à celle d'un proton se désintégrant dans les prochaines minutes.

Est-ce "correct" ? Non. Il n'existe pas d'exactitude prouvable dans un système massivement concurrent. C'est comme essayer de "prouver" une seule équation qui représente toute l'humanité. La plupart des systèmes Erlang sont chaotiques par nature et défient donc généralement la preuve en tant que systèmes. Ce que vous peut prouver que les fonctions pures individuelles sont correctes, et que toutes les fonctions qu'un processus à effets secondaires peut appeler au cours de sa vie ont des conditions de fin définies pour inclure le plantage sur des données bancales . Cette dernière partie explique comment Erlang atteint une telle robustesse en tant que système (les bonnes pratiques de codage, l'adhésion aux principes fonctionnels et une forte culture d'utilisation de Dialyzer y contribuent également).

Donc... la "correction"... prouvez-la sur les fonctions, autant que vous le pouvez. C'est une bonne chose et c'est pourquoi nous avons des outils comme PropER et QuickCheck. En guise de directives générales, essayez d'écrire de votre mieux :

  • Les fonctions qui sont pures aussi souvent que possible. Faites en sorte que le code à effets secondaires soit aussi isolé que possible du code pur qui ne fait que calculer et renvoyer des valeurs.
  • Processus dont on peut prouver qu'ils peuvent tomber en panne . Faites en sorte que chaque ligne ait un = sur elle. C'est pourquoi la méthode d'Erlang = est l'affectation, l'affirmation et l'unification tout en un.
  • Protocoles dont l'état est prouvable . Vous ne pouvez pas faire en sorte que deux processus identiques s'appellent l'un l'autre de manière bloquante sans risquer un blocage, par exemple. C'est une limitation fondamentale des systèmes concurrents. Le théorème CAP en est une autre. Concevez vos systèmes en tenant compte de ces contraintes (ce qui est curieusement libérateur) et vous obtiendrez des résultats satisfaisants. choisir consciemment vos compromis .

La preuve à une échelle plus grande qu'une fonction est une erreur insensée, sauf si vous êtes dans le milieu universitaire (cette fonction peut appeler un monde énorme de choses en dessous, donc ce n'est pas vraiment une limitation). Prouver des protocoles pour des conditions impossibles ou verrouillées est également possible, et si vous avez le temps pour cela, allez-y (sinon, faites comme le reste d'entre nous, mortels, et tenez-vous en aux délais d'attente et retravaillez le code qui a effectivement dépassé les délais d'attente lors d'appels dans le passé - cela ne devrait pas être un événement régulier).

Tout cela étant dit... Steve-O est presque certain de trébucher sur un câble de données dans le centre de données et de diviser un cluster bien plus souvent au cours des deux prochaines années que quiconque est susceptible de voir un wraparound PID causer un conflit réel au cours de la prochaine décennie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X