Question Pourquoi le fichier .NET est-il ouvert avec un chemin UNC rendant les appels SMB excessifs?


J'ai un bloc de code qui doit ouvrir et lire beaucoup de petits fichiers texte à partir d'un serveur NAS en utilisant des chemins UNC. Ce code fait partie d'un module qui a été écrit à l'origine en C ++ mais qui est maintenant converti en C #. La version C # est nettement plus lente. J'ai déterminé que l'appel à ouvrir le fichier représente presque toute la différence de performance. En utilisant WireShark, j'ai trouvé que cela était dû au fait que l'appel System.IO.File.Open fait beaucoup plus de requêtes réseau SMB qu'un code C ++ similaire.

Le code C ++ effectue cet appel:

FILE *f = _wfsopen(fileName, L"r", _SH_DENYWR);

Cela se traduit par la séquence suivante de requêtes SMB:

NT Create AndX Request, FID: 0x0004, Path: \\a\\i\\a\\q\\~141106162638847.nmd
NT Create AndX Response, FID: 0x0004
Trans2 Request, QUERY_FILE_INFO, FID: 0x0004, Query File Basic Info
Trans2 Response, FID: 0x0004, QUERY_FILE_INFO
Read AndX Request, FID: 0x0004, 1327 bytes at offset 0
Read AndX Response, FID: 0x0004, 1327 bytes
Close Request, FID: 0x0004
Close Response, FID: 0x0004
NT Create AndX Request, FID: 0x0005, Path: \\a\\i\\a\\q\\~141106162638847.nmd
NT Create AndX Response, FID: 0x0005

Le code C # effectue cet appel:

FileStream f = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);

Cela se traduit par la séquence suivante de requêtes SMB:

Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: 
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: 
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a
Trans2 Response, FIND_FIRST2, Files: a
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i
Trans2 Response, FIND_FIRST2, Files: i
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i\\a
Trans2 Response, FIND_FIRST2, Files: a
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i\\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i\\a\\q
Trans2 Response, FIND_FIRST2, Files: q
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: 
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: 
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a
Trans2 Response, FIND_FIRST2, Files: a
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i
Trans2 Response, FIND_FIRST2, Files: i
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i\\a
Trans2 Response, FIND_FIRST2, Files: a
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i\\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i\\a\\q
Trans2 Response, FIND_FIRST2, Files: q
Close Request, FID: 0x000f
Close Response
NT Create AndX Request, FID: 0x0018, Path: \\a\\i\\a\\q\\~141106162638847.nmd
NT Create AndX Response, FID: 0x0018
Trans2 Request, QUERY_FILE_INFO, FID: 0x0018, Query File Basic Info
Trans2 Response, FID: 0x0018, QUERY_FILE_INFO
Read AndX Request, FID: 0x0018, 1327 bytes at offset 0
Read AndX Response, FID: 0x0018, 1327 bytes
Close Request, FID: 0x0018
Close Response, FID: 0x0018
NT Create AndX Request, FID: 0x0019, Path: \\a\\i\\a\\q\\~141106162638847.nmd
NT Create AndX Response, FID: 0x0019

Pourquoi System.IO.File.Open crée-t-il toutes ces requêtes SMB supplémentaires? Est-il possible de changer ce code pour éviter toutes ces requêtes supplémentaires?


35
2017-11-25 21:42


origine


Réponses:


En bref, Appels File.Open new FileStream() et new FileStream() Est-ce que beaucoup d'appels:

  1. NormalisePath.

    String filePath = Path.NormalizePath(path, true, maxPath); // fullCheck: true
    

mène à ce code:

1.a: obtenir le chemin complet:

    if (fullCheck) { ... 
        result = newBuffer.GetFullPathName();

GetFullPathName () appels Win32Native.GetFullPathName une ou deux fois (en fonction de la longueur du chemin résultant).

1.b. Essayer d'étendre le chemin court. Votre chemin contient ~ char, il ressemble à un candidat pour un chemin en expansion:

    if (mightBeShortFileName) {
        bool r = newBuffer.TryExpandShortFileName();

en conséquence, Win32Native.GetLongPathName () est appelé.

  1. FileIoPermission.Demand () (pour les non-fiables seulement):

    // All demands in full trust domains are no-ops, so skip 
    if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) {
        ...
        new FileIOPermission(secAccess, control, new String[] { filePath }, false, false).Demand();
    
  2. Ouvrir le fichierStream (disquette retourne;)):

     // Don't pop up a dialog for reading from an emtpy floppy drive
    int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS);
    try {
        ...
        _handle = Win32Native.SafeCreateFile(tempPath, fAccess, share, secAttrs, mode, flagsAndAttributes, IntPtr.Zero);
    
  3. Win32Native.GetFileType ()

Tous ne mèneraient pas à une requête smb, mais certains le feront. J'ai essayé de reproduire les requêtes bavardes en déboguant avec la source pas à pas (ici c'est manuel pour activer le débogage de la source .net) et vérifier le journal après chaque étape. Les résultats sont plus proches de ceux de votre première liste. Si vous êtes vraiment intéressé à trouver le vrai problème, vous devrez le faire vous-même.

UPD Notez que j'ai vérifié le comportement actuel (.net 4.5.2). Il a été modifié plusieurs fois depuis 2.0 (par ex. FileIOPermission.Demand() à l'origine était appelé pour le code de confiance complet), donc ça dépend :)


12
2017-12-03 06:58



Je n'ai pas vraiment de réponse précise à la raison pour laquelle l'implémentation .NET est si bavarde, mais ce comportement serait dû à la mise en œuvre de System.IO.FileStream comme tout cela File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); est en train de passer les paramètres à la Constructeur FileStream.

public static FileStream Open(string path, FileMode mode, FileAccess access, FileShare share)
{
    return new FileStream(path, mode, access, share);
}

Changer le comportement de FileStream signifierait que vous devriez fondamentalement ré-implémenter la classe FileStream qui nécessitera beaucoup d'efforts.

Votre autre alternative plus simple serait de créer un wrapper natif qui appelle le code C ++ que vous avez donné. Puis appelez le wrapper natif à partir de votre code C #.


7
2017-11-25 23:35