簡體   English   中英

為什么.NET的File.Open使用UNC路徑進行過多的SMB調用?

[英]Why is .NET's File.Open with a UNC path making excessive SMB calls?

我有一段代碼需要使用UNC路徑打開和讀取來自NAS服務器的許多小文本文件。 該代碼是最初用C ++編寫的模塊的一部分,但現在已轉換為C#。 C#版本明顯較慢。 我確定打開文件的調用幾乎解決了所有性能差異。 使用WireShark,我發現這是因為與類似的C ++代碼相比,System.IO.File.Open調用發出的SMB網絡請求要多得多。

C ++代碼進行此調用:

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

這將導致以下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

C#代碼進行此調用:

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

這將導致以下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

為什么System.IO.File.Open發出所有這些額外的SMB請求? 有什么方法可以更改此代碼以避免所有這些額外請求?

簡而言之,File.Open調用new FileStream()new FileStream() 進行很多調用:

  1. NormalisePath

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

導致代碼:

1.a:獲取完整路徑:

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

GetFullPathName()調用Win32Native.GetFullPathName一兩次(取決於結果路徑的長度)。

1.b. 試圖擴大短路。 你的路徑包含~字符,所以它看起來像一個候選人擴張路徑

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

作為結果,Win32Native.GetLongPathName() 被調用

  1. FileIoPermission.Demand() (僅適用於非信任):

     // 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. 打開fileStream (軟盤回擊;)):

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

並非所有這些都會導致smb請求,但是有些會。 我嘗試通過逐步進行源調試( 這是啟用.net源調試手冊)並在每個步驟之后檢查日志來重現聊天請求。 結果與您的第一個清單更相似。 如果您真的有興趣找到真正的問題,則必須自己解決。

UPD請注意,我已經檢查了當前(.net 4.5.2)行為。 自2.0以來,它已多次更改(例如,最初也為完全信任的代碼調用了FileIOPermission.Demand() ),因此取決於:)

對於.NET實現為何如此健談,我並沒有確切的答案,但這是由於System.IO.FileStream作為File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); 正在將參數傳遞給FileStream構造函數

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

更改FileStream的行為將意味着您基本上必須重新實現FileStream類,這將需要大量的精力。

您另一個更簡單的選擇是創建一個本地包裝程序,該包裝程序調用您提供的C ++代碼。 然后從您的C#代碼中調用本地包裝器

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM