M>Никто не прокомментирует, почему для W2k3 сделан наворот с запуском потока?
Сей код писал немного неграмотный человек. Конкретно:
1. Он не знает, что проблема не в Windows Server 2003, на Windows XP может быть тоже самое, просто он ещё с этим не сталкивался.
2. Он не знает, что на определённых типах файловых хендлах имя запрашивать нельзя. Точнее можно, но приведёт к зависанию потока.
3. Он не знает, что хендлы типа "File" не всегда обозначают файл на диске. Это может быть именованный канал (с ними в определённых ситуациях бывают подобные зависания).
4. Он не понимает, что если в этом случае поток завис, то TerminateThread() уже не поможет, т.к. поток висит в режиме ядра. Это как в состоянии комы, — если он не выйдет оттуда сам, то считай мы его потеряли. Процесс при этом тоже станет неубиваемым.
Короче, если тебе нужны только объекты COM-портов, то правильным решением было бы такое:
1. Получить указатель на объект по хендлу, получим pFileObject.
2. Запросить имя объекта по адресу pFileObject->DeviceObject.
3. Если это имя типа "\Device\Serial0" — то это COM-порт.
Проблема в том, что для этого нужно писать драйвер, потому как я не знаю способа реализовать п.1 в режиме пользователя.
M>Единственно, не вызывается ли где-то внутрях опять же запрос имени объекта.
GetFileType() это аналог NtQueryVolumeInformationFile() с классом FileFsDeviceInformation, и ничего более. Имя не запрашивается.
M>PS x64, вы вроде занимаетесь разработкой низкоуровневых коммерческих продуктов, и ваше отношение к халтуре мне понятно
Ситуации бывают сильно разные и, возможно, в вашем продукте сойдёт и такое "половинчатое" решение. Я тоже часто бываю не прав.
M>но мне, как тяп-ляпщику, данная идея по душе.
Я почему-то сразу так и подумал.
M>Заодно хочу еще раз выразить вам свою благодарность, за то что долго помогали мне в брожении по дебрям низкоуровневых возможностей WinNT.
Оценочки, оценочки не забываем, и побольше, побольше!
Здравствуйте, Marty, Вы писали:
M>Насколько я понял, зависание может произойти, если мы запрашиваем имя пайпа. А нельзя ли как-то отфильтровать пайпы (они не нужны) до получения имени объекта
По поводу UniqueProcessId и HandleValue — да, тип USHORT здесь некорректен и ты запросто можешь получить ситуацию, при которой значения этих двух полей будут невалидны (в случае если оригинальное значение PID'а и хендла не влезают в 16 бит). Чтобы получить корректные значения, следует вызывать NtQuerySystemInformation() с классом SystemExtendedHandleInformation. При этом структуры будут следующие:
Здравствуйте, x64, Вы писали:
x64>4. Он не понимает, что если в этом случае поток завис, то TerminateThread() уже не поможет, т.к. поток висит в режиме ядра. Это как в состоянии комы, — если он не выйдет оттуда сам, то считай мы его потеряли. Процесс при этом тоже станет неубиваемым.
А как быть? Все изученные мной исходники используют именно такой подход. Тот же TaskManagerEx (кстати, код, фрагмент которого я привел, похоже писался по мотивам его исходников, и автор видимо хотел оптимизировать — не запускать для каждого объекта поток )
Насколько я понял, зависание может произойти, если мы запрашиваем имя пайпа. А нельзя ли как-то отфильтровать пайпы (они не нужны) до получения имени объекта
Здравствуйте, Marty, Вы писали:
M>А как быть? Все изученные мной исходники используют именно такой подход. Тот же TaskManagerEx (кстати, код, фрагмент которого я привел, похоже писался по мотивам его исходников, и автор видимо хотел оптимизировать — не запускать для каждого объекта поток ) M>Насколько я понял, зависание может произойти, если мы запрашиваем имя пайпа. А нельзя ли как-то отфильтровать пайпы (они не нужны) до получения имени объекта
Возникла мысль дернуть GetNamedPipeInfo — если она не вернет для хэндла ошибку, значит не стоит запрашивать имя объекта — можно зависнуть. Сейчас попробую проверить мысль.
Здравствуйте, Marty, Вы писали:
M>Возникла мысль дернуть GetNamedPipeInfo — если она не вернет для хэндла ошибку, значит не стоит запрашивать имя объекта — можно зависнуть. Сейчас попробую проверить мысль.
Писать драйвер. Это не сложно и не "из пушки по воробьям", в данном случае. Системные задачи требуют системных решений, а как ты хотел...
M>А нельзя ли как-то отфильтровать пайпы (они не нужны) до получения имени объекта
Нет, ибо с точки зрения приложения, пайп — это файл (объекта типа "File").
Здравствуйте, x64, Вы писали:
M>>А как быть?
x64>Писать драйвер. Это не сложно и не "из пушки по воробьям", в данном случае. Системные задачи требуют системных решений, а как ты хотел...
Задача была такова, чтобы прикрутить к библиотеке маленькую фичу по определению PID'а процесса, занявшего COM порт. И если под Линуксом это решается просто — чтением PID'а из лок файла, то под виндой возник гемор ;(
С драйвером проблема в том, что другие разработчики получают либу из репозитория, и чтобы собрать ее, им нужен ддк для сборки драйвера. Ну или если без ддк, то тогда мне придется повозится И вопрос сборки под мингв опять же встает.
В принципе, если ничего не придумаю, то придется реализовать вариант с созданием потока. У него довольно большой недостаток — медленно работает. И еще, не совсем понял, почему TerminateThread не работает? У меня есть прога, с которой я экспериментирую. Когда добавлял в нее обращения к объекту как к пайпу — она зависала. Открывал TaskManager(Ex) — процесс-потоки-завершить — поток завершается, и процесс исчезает. Вроде все хорошо.
M>>А нельзя ли как-то отфильтровать пайпы (они не нужны) до получения имени объекта
x64>Нет, ибо с точки зрения приложения, пайп — это файл (объекта типа "File").
;(
(С надеждой) Может, тогда \Device\SerialX можно как-то вычленить? ж)
Здравствуйте, x64, Вы писали:
x64>Это адрес объекта в пространстве адресов ядра, из приложения у тебя доступа к нему нет.
И нет никакого способа отобразить ядерную память в память моего процесса?
M>Задача была такова, чтобы прикрутить к библиотеке маленькую фичу по определению PID'а процесса, занявшего COM порт. И если под Линуксом это решается просто — чтением PID'а из лок файла, то под виндой возник гемор
Да, это вам не долбанный климакс, тут всё серьёзно.
M>С драйвером проблема в том, что другие разработчики получают либу из репозитория, и чтобы собрать ее, им нужен ддк для сборки драйвера. Ну или если без ддк, то тогда мне придется повозится И вопрос сборки под мингв опять же встает.
Нет, гемор не в винде, гемор у вас в головах (в особенности у лида).
M>В принципе, если ничего не придумаю, то придется реализовать вариант с созданием потока. У него довольно большой недостаток — медленно работает. И еще, не совсем понял, почему TerminateThread не работает? У меня есть прога, с которой я экспериментирую. Когда добавлял в нее обращения к объекту как к пайпу — она зависала. Открывал TaskManager(Ex) — процесс-потоки-завершить — поток завершается, и процесс исчезает. Вроде все хорошо.
В лучшем случае, когда поток находиться в alertable-ожидании, ты сможешь его прибить, но я сталкивался с неубиваемыми потоками и это поведение легко воспроизвести (достаточно, чтобы поток вошёл в режим тихого non-alertable ожидания в ядре).
M>(С надеждой) Может, тогда \Device\SerialX можно как-то вычленить?
Здравствуйте, x64, Вы писали:
M>>Задача была такова, чтобы прикрутить к библиотеке маленькую фичу по определению PID'а процесса, занявшего COM порт. И если под Линуксом это решается просто — чтением PID'а из лок файла, то под виндой возник гемор
x64>Да, это вам не долбанный климакс, тут всё серьёзно.
M>>С драйвером проблема в том, что другие разработчики получают либу из репозитория, и чтобы собрать ее, им нужен ддк для сборки драйвера. Ну или если без ддк, то тогда мне придется повозится И вопрос сборки под мингв опять же встает.
x64>Нет, гемор не в винде, гемор у вас в головах (в особенности у лида).
Ну может и так. Просто может быть до десятка ком портов на машине, и к каждому подключена железяка, и может работать (а может и не работать) софт, с железякой общающийся. И бывает, время тратится на разбирательство, кто занял порт, зачем, не результат ли это зависона или неосвобождения порта. За один раз минутка-другая погоды не делает, но когда это приходится делать достаточно часто, это вызывает проблемы.
В любом случае, огромное вам спасибо, за то что помогли разобратся и хотя бы оценить глубину проблемы, как минимум мне было интересно изучать этот вопрос, да и сложившееся обсуждение, думаю, сможет пригодится кому-то еще.
M>>Насколько я понял, зависание может произойти, если мы запрашиваем имя пайпа. А нельзя ли как-то отфильтровать пайпы (они не нужны) до получения имени объекта M>Если я правильно понял, то поможет GetFileType
Да, эта функция действительно делает то, что нужно (вызывает NtQueryVolumeInformationFile() с классом FileFsDeviceInformation, и далее можно проверить девайсу какого типа принадлежит указанный файловый хендл). Но тут есть две проблемы:
1. Это не панацея, ибо никто не даст гарантии, что запрос имени не повиснет на объекте другого типа. Допустим, этот вопрос можно исследовать, поставив кучу экспериментов и покопавшись во всех доступных исходниках, но стоит ли оно того?
2. Функция GetFileType() поможет в случае, если нужно проверить хендл, созданный в текущем процессе, а что будем делать если нужно проверить хендл в чужом процессе? Ну хорошо, можно воспользоваться DuplicateHandle(), но для этого придётся пооткрывать все процессы, включая системные. Во-первых, процесс ядра из режима пользователя открыть так просто никто не даст, во-вторых придётся включать привилегию отладки (это можно сделать только под админом). Ну и в конце концов, это всё чревато дырками в безопасности.
Это то, что навскидку, возможно, есть и другие проблемы. Ну для исследовательской разве что задачи — как раз самое оно. Без крайней нужды я бы не стал с этим всем связываться.
А мне идея нравится Надо будет проверить.
Единственно, не вызывается ли где-то внутрях опять же запрос имени объекта. Если так, то тот же вид, только в профиль.
x64>1. Это не панацея, ибо никто не даст гарантии, что запрос имени не повиснет на объекте другого типа. Допустим, этот вопрос можно исследовать, поставив кучу экспериментов и покопавшись во всех доступных исходниках, но стоит ли оно того?
Насчет повисона на объекте другого типа. Насколько я понял, виснет именно на пайпах, если их отбросить, шансы повиснуть сильно уменьшаются. Попробую сделать, а там посмотрю статистику.
x64>2. Функция GetFileType() поможет в случае, если нужно проверить хендл, созданный в текущем процессе, а что будем делать если нужно проверить хендл в чужом процессе? Ну хорошо, можно воспользоваться DuplicateHandle(), но для этого придётся пооткрывать все процессы, включая системные. Во-первых, процесс ядра из режима пользователя открыть так просто никто не даст, во-вторых придётся включать привилегию отладки (это можно сделать только под админом). Ну и в конце концов, это всё чревато дырками в безопасности.
Ну так способ без драйвера в любом случае без DuplicateHandle не обходится. Плюс для моей задачи можно в принципе проигнорить системные процессы.
x64>Это то, что навскидку, возможно, есть и другие проблемы. Ну для исследовательской разве что задачи — как раз самое оно. Без крайней нужды я бы не стал с этим всем связываться.
Проблемы возможно могут быть, но попробовать думаю стоит. Как я уже говорил, есть нужда ориентироваться в пачке ком портов и оперативно узнавать, кем занят конкретный порт, но эта фича как раз больше ориентирована на то, чтобы девелоперу не тратить время, а в продакшене можно будет и отрезать.
Попробую, завтра напишу, что вышло.
PS x64, вы вроде занимаетесь разработкой низкоуровневых коммерческих продуктов, и ваше отношение к халтуре мне понятно, но мне, как тяп-ляпщику, данная идея по душе. Заодно хочу еще раз выразить вам свою благодарность, за то что долго помогали мне в брожении по дебрям низкоуровневых возможностей WinNT.
Здравствуйте, x64, Вы писали:
x64>1. Это не панацея, ибо никто не даст гарантии, что запрос имени не повиснет на объекте другого типа. Допустим, этот вопрос можно исследовать, поставив кучу экспериментов и покопавшись во всех доступных исходниках, но стоит ли оно того?
Похоже, вопрос уже исследовал EliCZ в ImgList
Some programs get information about objects from handles
in processes (for example, they query names of objects).
Getting information about some file objects may require
acquiring file lock and when the lock cannot be acquired
thread waits. When you decide to kill the waiting thread
or the whole process you may not succeed, thread and process
still exist and are terminated after the file lock is acquired.
To avoid this "immortability" I've limited querying objects
to processes that could be open with debug privilege disabled
(this way my programs (see LocPInfo.zip\handles) didn't hang
on NtControlPipe* but did not display all information they could).
This was not a solution, moreover programs could still hang on file
objects in opened processes. Then I've disabled querying all file
objects (DumpOSD.zip\handles.exe) — still many many info lost.
Recently I've found a better solution — first test file object
by operation that can be interrupted and calling extra-thread
terminated. If the time is out — terminate the testing thread,
if not — query the file.
.....
struct {
ThreadStack;
hFile;
} TSI;
//get list of handles in system
//open process
//duplicate handle
bQueryObject = TRUE;
if(ObjectType == File) {
hThread = CreateThread(..TestFile, &tsi..)
if(WaitForSingleObject(hThread, MY_WAIT_TIME) == WAIT_TIMOUT) {
TerminateThread(hThread..);
VirtualFree(tsi.ThreadStack);
bQueryObject = FALSE;
}
}
if(bQueryObject) {
//....
}
// process next handle
.....
DWORD __stdcall TestFile(TSI *ptsi) {
VirtualQuery(&mbi, &mbi, sizeof(mbi)); //get stack base
ptsi->ThreadStack = mbi.AllocationBase;
GetFileType(ptsi->hFile); //other interruptable functions can be used
}
.....
Mark Russinovich's popular Process Explorer fights with this problem too
— when run with driver loaded it works well and when it hangs (for example,
when displaying security of file) it can recover from it. Former versions
simply stayed hung or didn't display file info.
— when run without driver loaded it detects when it hangs and creates other
thread. So procexp can have many threads and the waiting ones (and with them
the whole procexp process) cannot be terminated.
Мне тут не понятно одно: если проблема в TerminateThread, то зачем вообще использовать эту функцию, что бы "не было" утечек? Тогда можно не создавать тред, а взять его из пула, пусть ОС сама и разбирается со своими очередями
x64>2. Функция GetFileType() поможет в случае, если нужно проверить хендл, созданный в текущем процессе, а что будем делать если нужно проверить хендл в чужом процессе?
Тоже что и с NtQueryObject
x64>Ну для исследовательской разве что задачи — как раз самое оно. Без крайней нужды я бы не стал с этим всем связываться.
Возможно, в данном случае лучше исследовать запросы вроде IOCTL_SERIAL_GET_BAUD_RATE, или GetCommState.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Жаль только, что автор не разобрался в непосредственных причинах зависаний. Возможно, это помогло бы найти нормальный workaround (хотя я что-то сомневаюсь, что это возможно в общем случае). Я где-то читал об этом, да запамятовал уже, и ссылка потерялась...
GN>Возможно, в данном случае лучше исследовать запросы вроде IOCTL_SERIAL_GET_BAUD_RATE, или GetCommState.
Здравствуйте, x64, Вы писали:
M>>Единственно, не вызывается ли где-то внутрях опять же запрос имени объекта.
x64>GetFileType() это аналог NtQueryVolumeInformationFile() с классом FileFsDeviceInformation, и ничего более. Имя не запрашивается.
Здравствуйте, Marty, Вы писали:
M> Тем не менее, на нем тоже зависает ;(
А на GetCommState?
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth