下面的代码位在SMB共享上打开一个文件并立即关闭它。出于某种原因,我看到CloseHandle呼叫和通过线路发送的SMB关闭请求之间存在延迟。
#include <Windows.h>
#include <stdio.h>
typedef NTSTATUS (__stdcall *NtQuerySecurityObjectPtr)(
_In_ HANDLE Handle,
_In_ SECURITY_INFORMATION SecurityInformation,
_Out_ PSECURITY_DESCRIPTOR SecurityDescriptor,
_In_ ULONG Length,
_Out_ PULONG LengthNeeded
);
void printTime() {
SYSTEMTIME time;
GetSystemTime(&time);
int required = GetTimeFormat(LOCALE_INVARIANT, 0, &time, nullptr, nullptr, 0);
LPTSTR buffer = (LPTSTR)GlobalAlloc(GPTR, required * sizeof(TCHAR));
GetTimeFormat(LOCALE_INVARIANT, 0, &time, nullptr, buffer, required);
wprintf(L"%s\n", buffer);
}
int main()
{
LPCTSTR file = L"\\\\192.168.13.163\\share\\file1";
HANDLE f = INVALID_HANDLE_VALUE;
f = CreateFile(
file,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
CREATE_ALWAYS,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
INVALID_HANDLE_VALUE
);
CloseHandle(f);
printTime();
return 0;
}
我使用调试器运行此代码,并在printTime调用之后立即设置断点。在此printTime输出的一次测试中输出12:18:11。 在Wireshark中,我看到相应的关闭请求在12:18:20发出。因此,函数调用和发送的消息之间有大约10秒的延迟。
我认为这可能是由于句柄被泄露到某处或某些其他进程保持文件句柄打开,但我不认为是这种情况。如果我在CloseHandle调用之前暂停程序,则在系统用户shell中执行的sysinternals句柄工具向我显示我的进程有文件句柄
C:\Users\pepijn\Desktop>Handle.exe file1
Handle v4.0
Copyright (C) 1997-2014 Mark Russinovich
Sysinternals - www.sysinternals.com
SmbClose.exe pid: 7020 type: File C8: \Device\Mup\192.168.13.163\DMTest_share\file1
在CloseHandle调用之后立即运行相同的命令导致没有找到句柄
C:\Users\pepijn\Desktop>Handle.exe file1
Handle v4.0
Copyright (C) 1997-2014 Mark Russinovich
Sysinternals - www.sysinternals.com
No matching handles found.
有谁知道造成这种延误的原因是什么?
我能找到的与此主题最相关的问题是FileStream.Close() is not closing the file handle instantly。接受的答案是,其他一些进程可能会持有该文件。如上所述,我认为这不是这种情况。我没有运行任何病毒扫描程序,我希望任何打开的句柄都显示在Handle的输出中,因为它以提升的权限运行。
答案 0 :(得分:2)
感谢第一个答案,我更了解到哪里看。据我所知,这是Windows SMB重定向器中的故意行为。它有一个打开的文件句柄缓存,每隔x秒收到一次。这样做是为了透明地优化经常打开/关闭/重新打开文件的应用程序。有许多注册表项应该影响这一点(请参阅https://support.microsoft.com/en-us/kb/102981,https://msdn.microsoft.com/en-us/library/windows/hardware/dn567661%28v=vs.85%29.aspx,https://social.msdn.microsoft.com/Forums/en-US/832d395b-6e6f-4658-8dbb-120138a4cd7c/smb2-registry-settings?forum=os_fileservices),但这些都不会导致CloseHandle上的SMB立即关闭请求。 我能够得到的最好结果是将CacheFileTimeout设置为0.这仍然会在3-5秒之间留下延迟(可能取决于清理计时器被触发的频率)。
答案 1 :(得分:1)
这显然是由于服务器在文件打开时授予批量Oplock(或相对租约)。您可以通过使用Wireshark检查创建响应来证明这一点。
建议的技巧之一是避免批量oplock被授予打开同一个文件,以便在原始CreateFile调用之前在另一个应用程序中读取。
答案 2 :(得分:0)
不完全是答案(抱歉),但我认为这也可能发生在本地文件中。我正在使用运行在Windows和Linux上的backup software,并且有一个测试套件,在此期间它会打开一个旧文件,读取其内容,写入新文件,关闭旧文件然后重命名它
在Unix上这很好,因为我们可以重命名现有文件,甚至是开放文件。在Windows上,如果我不先关闭文件句柄,这些测试将无法可靠地失败。但是,即使我这样做,测试也会在大约10%的时间内随机失败,例如this:
date
我知道在重命名文件之前,我使用TRACE: Trying to create testfiles/0_0\backup\01234567\refcount.rdb.rfw shared using dwShareMode FILE_SHARE_READ | FILE_SHARE_WRITE
[check fix] WARNING: Exception thrown: CommonException(OSFileError) (Failed to rename temporary refcount database file from testfiles/0_0\backup\01234567\refcount.rdb.rfwX to testfiles/0_0\backup\01234567\refcount.rdb.rfw: Access is denied. (5)) at C:\projects\boxbackup\lib\backupstore\BackupStoreRefCountDatabase.cpp:200
关闭了该文件。这些症状可以通过CloseHandle()
异步发生来完美解释,但到目前为止,我的经验和这个问题是我可能发生的唯一证据。这是在本地文件系统上,而不是SMB(我想,因为AppVeyor的内部对我来说有点黑盒子。)
修改:我在another StackOverflow question and answer中找到了确认和解释:
我相信Windows Internals涵盖了这一点。简短的说法是,即使你在文件句柄上调用了CloseHandle,内核仍然可能有一些需要几毫秒才能完成的杰出引用。