При открытии файла, находящегося на общем ресурсе win2k8, сначала происходит сбой
Суть проблемы: я получаю "(0x80070002) Системе не удается найти указанный файл" в течение примерно 8-9 секунд, прежде чем она сможет открыть его успешно.
В двух словах, у нас есть два ком-компонента. Компонент A вызывает Компонент B и запрашивает имя файла UNC для записи - возвращенное имя файла еще не существует, но путь делает - он затем выполняет свою работу, создает и заполняет файл, и сообщает Компоненту B, что это сделано, делая еще один вызов. В этот момент Компонент B вызовет MoveFile, чтобы переименовать файл с его "официальным" именем.
Этот код работал (буквально) лет. На win2k3 работает нормально. Он отлично работает, когда работает на win2k8 и указывает на общий ресурс на сервере win2k3. Но если вы запустите его на win2k8 и укажете на общий ресурс на (другом) сервере win2k8, произойдет сбой. Он также работает нормально, если общий ресурс фактически находится на той же машине win2k8, на которой выполняется код.
Оба компонента A и компонент B существуют в своих собственных службах Windows, работающих под учетной записью администратора домена. Общие ресурсы настроены на "Все / Полный контроль" во всех средах тестирования, так же как и базовые папки, на которые указывает общий ресурс. Все машины находятся в одном домене.
Во время отладки я понял, что файл действительно существует к тому времени, когда я проверяю его вручную - после нескольких итераций мне пришло в голову, что файл не "появляется", пока не проходит некоторая задержка - поэтому я поместил цикл ниже в компоненте B, как показано ниже:
void Demo()
{
int nCounter = 0;
while (true)
{
CFileStatus fs;
if (CFile::GetStatus(tempname, fs)) break;
SleepEx(100, FALSE);
nCounter++;
}
}
На самом деле этот код завершает работу, и nCounter обычно находится между 80 и 90 итерациями, когда он указывает, что файл "появляется" примерно через 8-9 секунд. Как только этот цикл завершается, код может успешно переименовать файл, и вся дальнейшая обработка работает.
Я помещаю CFile::GetStatus в компонент A непосредственно перед его вызовом в компонент B, и это указывает на успех - он может увидеть файл и получить его истинный размер, но вызов компонента b, выполненный сразу после этого, не может увидеть файл, пока не будет указано выше задержка проходит. Я проверил, что имена путей точно такие же, даже если вызовы в конечном итоге будут успешными после паузы от 8 до 9 секунд...
Когда происходит что-то подобное, я всегда предполагаю, что в моем коде есть ошибка, пока не доказано обратное, но, учитывая, что этот код выполнялся должным образом в течение очень долгого времени и (кроме добавленного моего диагностического цикла) не изменился, и он работает во всех средах кроме win2k8 - > win2k8 share, я предполагаю, что здесь есть какая-то проблема с ОС, которую я не понимаю.
Любое понимание будет полезно - спасибо
1 ответ
Проблема оказывается в изменениях кэширования протокола SMB2 - SMB2 используется только тогда, когда обе стороны согласны, поэтому это проблема только для win2k8->win2k8 (Vista & Windows 7 также реализуют SMB2)
В SMB2 клиент имеет локальный кеш, который обновляется (по умолчанию) каждые 10 секунд, это означает, что другие машины могут обновлять общий ресурс сервера, и он не будет виден, пока кеш не станет недействительным. Я думаю, но не подтвердил с Microsoft на данный момент, что есть также некоторая проблема в слое WindowsOnWindows в win2k8 при запуске 32-битных приложений - может показаться, что они также могут получить свой собственный кеш, что может объяснить, почему мои два взаимодействующих приложения на той же машине не видел того же взгляда на долю сервера.
Существует (по крайней мере) пара обходных путей для решения этой проблемы, предоставленных Microsoft
- Перепишите свой код, чтобы использовать небуферизованный доступ к файлам
- вызовите FlushFileBuffers, когда вы закончите писать (но перед тем, как закрыть файл)
- Отключить SMB2 на одной или обеих машинах
Запустите "regedit" на Windows Server 2008
на базе компьютера. Разверните и найдите поддерево следующим образом.
HKLM \ System \ CurrentControlSet \ Services \ LanmanServer \ Parameters Добавить новый ключ REG_DWORD с именем "Smb2" (без кавычек)
Имя значения: Smb2 Тип значения:
REG_DWORD 0 = отключено 1 = включено
Установите значение 0, чтобы отключить SMB 2.0, или 1, чтобы снова включить SMB 2.0. Перезагрузите сервер.
- Отключите кеширование SMB2 на клиентском компьютере
Настройте указанные ниже ключи с желаемым значением времени ожидания для обновления локального кэша. Установка этих ключей на ноль отключит соответствующий кеш.
HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ LanmanWorkstation \ Параметры:
FileInfoCacheLifetime FileNotFoundCacheLifetime DirectoryCacheLifetime Все значения являются DWORD, указанными в секундах