una rutas ruta relativas relativa informatica escribe ejemplo como archivo acceso absolutas absoluta c# c++ .net file-io windows-networking

c# - rutas - ¿Por qué File.Open de.NET con una ruta UNC hace llamadas SMB excesivas?



rutas absolutas y relativas linux (2)

Tengo un bloque de código que necesita abrir y leer una gran cantidad de pequeños archivos de texto de un servidor NAS usando rutas UNC. Este código es parte de un módulo que originalmente se escribió en C ++ pero ahora se está convirtiendo en C #. La versión de C # es significativamente más lenta. Decidí que la llamada para abrir el archivo explica casi toda la diferencia de rendimiento. Usando WireShark encontré que esto se debe a que la llamada System.IO.File.Open hace muchas más solicitudes de red SMB que código C ++ similar.

El código C ++ hace esta llamada:

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

Esto da como resultado la siguiente secuencia de solicitudes 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

El código C # hace esta llamada:

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

Esto da como resultado la siguiente secuencia de solicitudes 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

¿Por qué System.IO.File.Open realiza todas estas solicitudes SMB adicionales? ¿Hay alguna manera de cambiar este código para evitar todas estas solicitudes adicionales?


En resumen, File.Open llama a new FileStream() y new FileStream() hace muchas llamadas:

  1. NormalisePath .

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

conduce a this código:

1.a: obtener la ruta completa:

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

GetFullPathName() llama a Win32Native.GetFullPathName una o dos veces (dependiendo de la longitud de la ruta resultante).

1.b. Intentando expandir el camino corto. Su ruta contiene ~ char, por lo que parece un candidato para una ruta en expansión :

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

como resultado, se llama a Win32Native.GetLongPathName ().

  1. FileIoPermission.Demand() (para no solo de confianza):

    // 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. Abra fileStream (floppy strike back;)):

    // 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()

No todos ellos conducirían a la solicitud smb, pero algunos lo harán. Intenté reproducir las solicitudes de conversación mediante la depuración con el código fuente paso a paso ( here''s manual para habilitar la depuración del origen de .NET) y verificar el registro después de cada paso. Las resistencias son más similares a la primera lista de tu. Si está realmente interesado en encontrar el verdadero problema, tendrá que hacerlo usted mismo.

UPD Tenga en cuenta que he comprobado el comportamiento actual (.net 4.5.2). Fue cambiado varias veces desde 2.0 (por ejemplo, FileIOPermission.Demand() originalmente fue llamado para código de plena confianza también), por lo que depende :)


Realmente no tengo una respuesta específica de por qué la implementación de .NET es tan parlanchina, pero este comportamiento se debería a la implementación de System.IO.FileStream como todo lo que File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); lo que está haciendo es pasar los parámetros al constructor FileStream .

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

Cambiar el comportamiento de FileStream significaría que básicamente tendría que volver a implementar la clase FileStream, lo que requerirá un gran esfuerzo.

Su otra alternativa más simple sería crear un contenedor nativo que llame al código C ++ que usted proporcionó. Luego, llame al contenedor nativo desde su código C # .