Suppression d’une poignée d’un port d’achèvement d’E / S et d’autres questions sur IOCP

La fonction CreateIoCompletionPort permet la création d’un nouveau port d’achèvement d’E / S et l’enregistrement des CreateIoCompletionPort de fichiers sur un port d’achèvement d’E / S existant.

Ensuite, je peux utiliser n’importe quelle fonction, comme une recv sur un socket ou un fichier ReadFile sur un fichier avec une structure OVERLAPPED pour démarrer une opération asynchrone.

Je dois vérifier si l’appel de fonction est revenu de manière synchrone bien qu’il ait été appelé avec une structure OVERLAPPED et dans ce cas, le gérer directement. Dans l’autre cas, lorsque ERROR_IO_PENDING est renvoyé, je peux utiliser la fonction GetQueuedCompletionStatus pour être averti lorsque l’opération se termine.

La question qui se pose sont:

  • Comment puis-je supprimer une poignée du port d’achèvement d’E / S? Par exemple, lorsque j’ajoute des sockets à l’IOCP, comment puis-je supprimer ceux qui sont fermés? Dois-je simplement réenregistrer un autre socket avec la même clé de complétion?

  • En outre, existe-t-il un moyen de passer les appels TOUJOURS sur le port d’achèvement des E / S et de ne pas retourner de manière synchrone?

  • Et enfin, est-il possible, par exemple, de recv manière asynchrone mais d’ send synchrone? Par exemple, lorsqu’un simple service d’écho est implémenté: puis-je attendre avec une recv asynchrone pour de nouvelles données mais send la réponse de manière synchrone afin de réduire la complexité du code? Dans mon cas, je ne recevrais pas une seconde fois avant le traitement de la première demande.

  • Que se passe-t-il si un ReadFile asynchrone a été demandé, mais avant qu’il ne soit terminé, un WriteFile dans le même fichier doit être traité. Le ReadFile sera-t-il annulé avec un message d’erreur et je dois redémarrer le processus de lecture dès que l’écriture est terminée? Ou dois-je annuler le ReadFile manuellement avant d’écrire? Cette question se pose en combinaison avec un appareil de communication; Ainsi, l’écriture et la lecture ne devraient pas poser de problèmes si elles se produisent simultanément.

Comment puis-je supprimer une poignée du port d’achèvement d’E / S?

Dans mon expérience, vous ne pouvez pas dissocier un handle d’un port d’achèvement. Toutefois, vous pouvez désactiver la notification de port d’achèvement en définissant le bit de hEvent champ hEvent de votre structure OVERLAPPED : Consultez la documentation de GetQueuedCompletionStatus .

Par exemple, lorsque j’ajoute des sockets à l’IOCP, comment puis-je supprimer ceux qui sont fermés? Dois-je simplement réenregistrer un autre socket avec la même clé de complétion?

Il n’est pas nécessaire de dissocier explicitement un handle d’un port d’achèvement d’E / S; fermer la poignée est suffisant. Vous pouvez associer plusieurs descripteurs avec la même clé de complétion; La meilleure façon de déterminer quelle requête est associée à l’achèvement des E / S est d’utiliser la structure OVERLAPPED . En fait, vous pouvez même étendre OVERLAPPED pour stocker des données supplémentaires.

En outre, existe-t-il un moyen de passer les appels TOUJOURS sur le port d’achèvement des E / S et de ne pas retourner de manière synchrone?

C’est le comportement par défaut, même lorsque ReadFile / WriteFile renvoie TRUE . Vous devez explicitement appeler SetFileCompletionNotificationModes pour indiquer à Windows de ne pas mettre en queue un paquet d’achèvement lorsque TRUE et ERROR_SUCCESS sont renvoyés.

est-il possible par exemple de recv manière asynchrone mais d’ send synchrone?

Pas en utilisant recv et send ; Vous devez utiliser des fonctions qui acceptent les structures OVERLAPPED , telles que WSARecv , WSASend ou, en variante, ReadFile et WriteFile . Il peut être plus pratique d’utiliser cette dernière si votre code est destiné à gérer plusieurs types de descripteurs d’E / S, tels que les sockets et les canaux nommés. Ces fonctions fournissent un mode synchrone, donc si vous les utilisez, vous pouvez combiner des appels asynchrones et synchrones.

Que se passe-t-il si un ReadFile asynchrone a été demandé, mais avant qu’il ne soit terminé, un WriteFile dans le même fichier doit être traité?

Il n’y a pas d’annulation implicite. Tant que vous utilisez des structures OVERLAPPED séparées pour chaque lecture / écriture sur un périphérique full-duplex, je ne vois aucune raison pour laquelle vous ne pouvez pas effectuer des opérations d’E / S simultanées.

Comme je l’ai déjà souligné, la croyance répandue selon laquelle il est impossible de supprimer les poignées des ports d’achèvement est erronée, probablement à cause de l’absence de toute indication sur la façon de procéder à partir de presque toute la documentation. En fait, c’est assez facile:

Appelez NtSetInformationFile avec la valeur d’énumérateur FileReplaceCompletionInformation pour FileInformationClass et un pointeur vers une structure FILE_COMPLETION_INFORMATION pour le paramètre FileInformation . Dans cette structure, définissez le membre Port sur NULL (ou nullptr , en C ++) pour dissocier le fichier du port auquel il est actuellement connecté (je suppose que si ce fichier n’est associé à aucun port, rien ne se produirait) ou définissez Port sur un HANDLE valide à un autre port d’achèvement pour associer le fichier à celui-ci à la place.

D’abord quelques corrections importantes.

Si l’opération d’E / S superposées se termine immédiatement ( ReadFile ou une fonction d’E / S similaire renvoie un succès) – la finalisation des E / S est déjà planifiée pour l’IOCP.

En outre, en fonction de vos questions, je pense que vous confondez les poignées de fichier / socket et les opérations d’E / S spécifiques émises sur ces dernières.

Maintenant, en ce qui concerne vos questions:

  1. Avant tout, il n’y a pas de moyen conventionnel pour supprimer un fichier / handle de socket de l’IOCP (en général, vous n’avez simplement pas à le faire). Vous parlez de retirer les poignées fermées de l’IOCP, ce qui est absolument incorrect. Vous ne pouvez pas supprimer un handle fermé, car il ne référence plus un object kernel valide!

Une question plus correcte devrait être comment le fichier / socket doit être correctement fermé. La réponse est: fermez simplement votre poignée. Toutes les opérations d’E / S en suspens (émises sur cette poignée) reviendront bientôt avec un code d’erreur (avortement). Ensuite, dans votre routine d’achèvement (celle qui appelle GetQueuedCompletionStatus dans une boucle), vous GetQueuedCompletionStatus effectuer le nettoyage nécessaire pour chaque E / S.

  1. Comme je l’ai déjà dit, toutes les entrées / sorties arrivent à IOCP dans des cas synchrones et asynchrones. La seule situation où il ne parvient pas à IOCP est quand une E / S se termine de manière synchrone avec une erreur . Quoi qu’il en soit, si vous souhaitez un traitement unifié – dans ce cas, vous pouvez PostQueuedCompletionStatus données de complétion artificielles à IOCP (utilisez PostQueuedCompletionStatus ).

  2. Vous devez utiliser WSASend et WSARecv (pas recv et send ) pour les E / S superposées. Néanmoins, même le socket a été ouvert avec l’indicateur WSA_FLAG_OVERLAPPED – vous êtes autorisé à appeler les fonctions d’E / S sans spécifier la structure OVERLAPPED . Dans ce cas, ces fonctions fonctionnent de manière synchrone. Vous pouvez donc choisir des modes synchrones / asynchrones pour chaque appel de fonction.

  3. Il n’y a pas de problème à mélanger les requêtes de lecture / écriture superposées. Le seul point délicat est ce qui se passe si vous essayez de lire les données à partir de la position du fichier où vous écrivez actuellement. Le résultat peut dépendre de choses subtiles, telles que l’ordre d’achèvement des E / S par le matériel, certains parameters de synchronisation du PC, etc. Une telle situation doit être évitée.

Comment puis-je supprimer une poignée du port d’achèvement d’E / S? Par exemple, lorsque j’ajoute des sockets à l’IOCP, comment puis-je supprimer ceux qui sont fermés? Dois-je simplement réenregistrer un autre socket avec la même clé de complétion?

Vous l’avez mal compris. Vous définissez le port d’achèvement d’E / S à utiliser par un object fichier – lorsque l’object fichier est supprimé, vous n’avez rien à craindre. La raison pour laquelle vous vous trompez est la manière dont Win32 expose la fonctionnalité de l’API native sous-jacente ( CreateIoCompletionPort fait deux choses très différentes en une seule fonction).

En outre, existe-t-il un moyen de passer les appels TOUJOURS sur le port d’achèvement des E / S et de ne pas retourner de manière synchrone?

C’est comme ça que ça a toujours été. À partir de Windows Vista uniquement, vous pouvez personnaliser la gestion des notifications d’achèvement.

Que se passe-t-il si un ReadFile asynchrone a été demandé, mais avant qu’il ne soit terminé, un WriteFile dans le même fichier doit être traité. Le fichier de lecture sera-t-il annulé avec un message d’erreur et je dois redémarrer le processus de lecture dès que l’écriture est terminée?

Les opérations d’E / S sous Windows sont insortingnsèquement asynchrones et les requêtes sont toujours en attente. Vous ne le pensez peut-être pas, car vous devez spécifier FILE_FLAG_OVERLAPPED dans CreateFile pour activer les E / S asynchrones. Cependant, au niveau de la couche native, les E / S synchrones sont vraiment un complément, ce qui permet au kernel de garder une trace de la position du fichier et d’attendre que les E / S se terminent avant de retourner.