La condition de course aux données de mise en réseau de l'enfer
J'écrivais un client / serveur de mise en réseau (Windows XP / C #) pour travailler avec une application similaire sur une très ancienne station de travail (Encore 32/77) écrite par un autre développeur.
L'application a essentiellement consisté à partager / manipuler certaines données sur l'hôte pour contrôler le processus hôte exécutant le système avec notre interface utilisateur tactile multi-écrans sophistiquée basée sur PC.
Il l'a fait avec une structure à 3 couches. Le processus de communication a lu / écrit des données vers / depuis l'hôte, a effectué toutes les conversions de format nécessaires (endianness, format à virgule flottante, etc.) et a écrit / lu les valeurs vers / depuis une base de données. La base de données a servi d'intermédiaire de données entre les communications et les interfaces utilisateur tactiles. L'interface tactile de l'interface utilisateur a généré des interfaces d'écran tactile en fonction du nombre de moniteurs connectés au PC (il l'a détecté automatiquement).
Dans le laps de temps donné, un paquet de valeurs entre l'hôte et notre PC ne pouvait envoyer que 128 valeurs maximum sur le fil à la fois avec une latence maximale de ~ 110 ms par aller-retour (UDP a été utilisé avec une connexion Ethernet x-over directe entre les ordinateurs). Ainsi, le nombre de variables autorisées en fonction du nombre variable d'écrans tactiles connectés était sous contrôle strict. De plus, l'hôte (bien qu'ayant une architecture multiprocesseur assez complexe avec un bus de mémoire partagée utilisé pour l'informatique en temps réel) avait environ 1 / 100e de la puissance de traitement de mon téléphone portable, il a donc été chargé de faire le moins de traitement possible et c'est le serveur / client devait être écrit en assembleur pour garantir cela (l'hôte exécutait une simulation en temps réel complète qui ne pouvait pas être affectée par notre programme).
Le problème était. Certaines valeurs, lorsqu'elles sont modifiées sur l'écran tactile, ne prennent pas uniquement la valeur nouvellement entrée, mais alternent de manière aléatoire entre cette valeur et la valeur précédente. Cela et seulement sur quelques valeurs spécifiques sur quelques pages spécifiques avec une certaine combinaison de pages a jamais présenté le symptôme. Nous avons presque complètement manqué le problème jusqu'à ce que nous commencions à l'exécuter à travers le processus d'acceptation initial du client
Pour cerner le problème, j'ai choisi l'une des valeurs oscillantes:
- J'ai vérifié l'application Touchscreen, elle oscillait
- J'ai vérifié la base de données, oscillant
- J'ai vérifié l'application de communication en oscillant
Ensuite, j'ai éclaté Wireshark et commencé à décoder manuellement les captures de paquets. Résultat:
- Pas oscillant mais les paquets ne semblaient pas corrects, il y avait trop de données.
J'ai parcouru chaque détail du code de communication cent fois sans trouver de défaut / erreur.
Enfin, j'ai commencé à envoyer des e-mails à l'autre développeur pour lui demander en détail comment sa fin fonctionnait pour voir s'il manquait quelque chose. Puis je l'ai trouvé.
Apparemment, lorsqu'il a envoyé des données, il n'a pas vidé le tableau de données avant la transmission.Il a donc essentiellement écrasé le dernier tampon utilisé avec les nouvelles valeurs écrasant l'ancienne, mais les anciennes valeurs non écrasées étaient toujours transmises.
Donc, si une valeur était à la position 80 du tableau de données et que la liste des valeurs demandées est passée à moins de 80 mais que la même valeur était contenue dans la nouvelle liste, les deux valeurs existeraient dans le tampon de données pour ce tampon spécifique à tout moment. temps donné.
La valeur lue dans la base de données dépend de la tranche de temps à laquelle l'interface utilisateur a demandé la valeur.
La solution était douloureusement simple. Lisez le nombre d'éléments entrants sur le tampon de données (il était en fait contenu dans le cadre du protocole de paquet) et ne lisez pas le tampon au-delà de ce nombre d'éléments.
Leçons apprises:
Ne tenez pas la puissance de calcul moderne pour acquise. Il fut un temps où les ordinateurs ne prenaient pas en charge Ethernet et où le vidage d'une baie pouvait être considéré comme coûteux. Si vous voulez vraiment voir jusqu'où nous en sommes, imaginez un système qui n'a pratiquement aucune forme d'allocation dynamique de mémoire. IE, le processus exécutif devait pré-allouer toute la mémoire pour tous les programmes dans l'ordre et aucun programme ne pouvait se développer au-delà de cette frontière. IE, allouer plus de mémoire à un programme sans recompiler tout le système pourrait provoquer un crash massif. Je me demande si les gens parleront un jour des jours de la pré-collecte des ordures.
Lors de la mise en réseau avec des protocoles personnalisés (ou de la gestion de la représentation des données binaires en général), assurez-vous de lire la spécification jusqu'à ce que vous compreniez chaque fonction de chaque valeur envoyée à travers le canal. Je veux dire, lisez-le jusqu'à ce que vos yeux vous fassent mal. Les gens manipulent des données en manipulant des bits ou des octets individuels ont des façons très intelligentes et efficaces de faire les choses. Manquer le moindre détail pourrait briser le système.
Le temps global pour réparer était de 2-3 jours avec la plupart de ce temps passé à travailler sur d'autres choses quand j'ai été frustré par cela.
SideNote: L'ordinateur hôte en question ne prend pas en charge Ethernet par défaut. La carte pour le conduire a été fabriquée sur mesure et mise à niveau et la pile de protocoles n'existait pratiquement pas. Le développeur avec lequel je travaillais était un enfer de programmeur, il a non seulement implémenté une version allégée d'UDP et une fausse pile Ethernet mimimale (le processeur n'était pas assez puissant pour gérer une pile Ethernet complète) sur le système pour ce projet mais il l'a fait en moins d'une semaine. Il avait également été l'un des chefs d'équipe du projet d'origine qui avait conçu et programmé le système d'exploitation en premier lieu. Disons simplement que tout ce qu'il a eu à partager sur les ordinateurs / la programmation / l'architecture, peu importe combien de temps ou combien je suis déjà nouveau, j'écouterais chaque mot.