c# - 为什么带有 UNC 路径的 .NET 的 File.Open 会产生过多的 SMB 调用?

标签 c# c++ .net file-io windows-networking

我有一段代码需要使用 UNC 路径从 NAS 服务器打开和读取大量小文本文件。此代码是最初用 C++ 编写但现在正在转换为 C# 的模块的一部分。 C# 版本要慢得多。我确定打开文件的调用几乎是所有性能差异的原因。使用 WireShark 我发现这是因为 System.IO.File.Open 调用比类似的 C++ 代码发出的 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
    

导致this代码:

1.a:获取完整路径:

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

GetFullPathName()调用 Win32Native.GetFullPathName 一到两次(取决于生成路径的长度)。

1.b。试图扩大短路径。您的路径包含 ~ 字符,因此它看起来像是 path expanding 的候选者:

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

因此,Win32Native.GetLongPathName() is called .

  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. Open 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 请求,但有些会。我尝试通过使用源代码逐步调试(here's 启用 .net 源代码调试的手册)并在每一步后检查日志来重现聊天请求。结果与您的第一个列表更相似。 如果您真的有兴趣找到真正的问题,则必须自己动手。

UPD 请注意,我检查了当前 (.net 4.5.2) 的行为。自 2.0 以来已多次更改(例如 FileIOPermission.Demand() 最初也被称为完全受信任的代码),因此取决于:)

关于c# - 为什么带有 UNC 路径的 .NET 的 File.Open 会产生过多的 SMB 调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27137267/

相关文章:

c# - 通过尝试从 soap 服务检索数据而未授权 HTTP 请求

c# - 现有 Umbraco 项目的开发

c++ - 指定初始化器

c# - 在 Selenium 中检查页面加载时间

c# - C# 中的乘法表

c++ - 在 C++ 中跨多个文件使用多个命名空间中的类

C++ malloc/realloc 奇怪的行为

c# - 使用 mongodb C# 驱动程序映射投影结果

c# - 使用 AllocConsole 和目标体系结构 x86 时没有控制台输出

.net - 使用 jquery 填充下拉菜单并设置从 .NET SelectList 中选择的内容