Поиск открытых дескрипторов в программе Windows

В Windows (MinGW) моя программа наследует нежелательные дескрипторы вызывающего процесса.

Процессу не нужно открывать эти файлы, но поскольку он живет дольше жизни родителя, у меня возникают обычные проблемы с открытыми файлами.

В Linux я решаю проблему следующим образом:

// Close all file descriptors
// It's hard to figure out how many are open, but the first 1000 should do
int fd;
for (fd = 0; fd < 1000; fd++)
  close (fd);

Похоже, это не работает в Windows.

Как я могу определить, какие дескрипторы файлов были унаследованы? Как я могу тогда закрыть их?

Проект написан на C (без C++) с использованием API совместимости MinGW и Unix для Windows.


person ams    schedule 12.09.2013    source источник
comment
Что вы имеете в виду под «Это не работает в Windows»?   -  person sara    schedule 12.09.2013
comment
Я имею в виду, он работает без ошибок, но файлы остаются открытыми.   -  person ams    schedule 12.09.2013
comment
Этот вопрос о перечислении дескрипторов процессов может быть отправной точкой.   -  person Mark Wilkins    schedule 12.09.2013
comment
он живет дольше жизни родителя ... кто живет дольше жизни какого родителя?   -  person Praetorian    schedule 12.09.2013
comment
@sara: это в основном то, что у меня есть выше.   -  person ams    schedule 12.09.2013
comment
@Praetorian: это процесс. Родитель — это другой процесс.   -  person ams    schedule 12.09.2013
comment
@MarkWilkins: я думаю, что смогу что-то позаимствовать из этой кучи кода C++.   -  person ams    schedule 12.09.2013
comment
@ams: Как узнать, что он работает без ошибок, не проверяя результат close()?   -  person alk    schedule 12.09.2013
comment
Почему вы не знаете, какие файловые дескрипторы использует/использует ваша программа?   -  person alk    schedule 12.09.2013
comment
@alk: закрывает 1000 дескрипторов, даже не проверяя их существование. Конечно, многие вызовы вернут отказ. Под без ошибок я подразумеваю, что программа не дала сбой.   -  person ams    schedule 12.09.2013
comment
@alk: прочитайте вопрос внимательнее: дескрипторы наследуются от родительского процесса против моей воли.   -  person ams    schedule 12.09.2013
comment
Так почему бы просто не сделать for(int i=0, i< 0xffffffff; ++i) CloseHandle(i); на окнах?! :-/   -  person alk    schedule 12.09.2013
comment
Значит, у вас нет доступа к коду родительского процесса? Как этот процесс наследует созданные дескрипторы?   -  person alk    schedule 12.09.2013
comment
@alk, слепо закрывать дескриптор окна, как это, нехорошо.   -  person josh poley    schedule 12.09.2013
comment
@alk: родительский процесс не помечает дескрипторы флагом NOINHERIT, поэтому просто происходит наследование. Это нормальная практика для Unix-программ, и MinGW делает то же самое. Проблема в том, что трюк Unix не исправляет это в MinGW.   -  person ams    schedule 12.09.2013
comment
Если у вас есть контроль над родительским процессом, то пусть он передаст FALSE для флага bInheritHandles для CreateProcess.   -  person josh poley    schedule 12.09.2013
comment
@joshpoley это неподходящее место для решения проблемы, поскольку мой процесс не единственный, который он может вызвать. Даже если бы это было так, он использует _spawnl, поэтому нет способа остановить наследование. Он не может использовать CreateProcess, потому что смешивание API приводит к плохим вещам (хотя иногда это работает).   -  person ams    schedule 12.09.2013
comment
Если вам приходится иметь дело с HANDLE, разве вы уже не смешивали API?   -  person alk    schedule 12.09.2013
comment
@alk, если дескриптор унаследован, то, по-видимому, проблем с состоянием во время выполнения не существует?   -  person ams    schedule 12.09.2013
comment
@joshpoley: я прекрасно об этом знаю. Это была просто беспомощная попытка сделать иронический комментарий...   -  person alk    schedule 13.09.2013


Ответы (2)


Теперь я немного изучил это и нашел решение реальной проблемы, но не так, как хотел.

Я думал, что смогу найти и очистить любые нежелательные открытые файлы, но это оказалось сложно. Я нашел несколько разных руководств (здесь и здесь), но они полагаются на недокументированные API. Я не смог заставить эту технику работать — возможно, я делал это неправильно, или, возможно, API изменился в Windows Server 2012 — но в любом случае я не уверен, что хочу туда идти; для Sysinternals нормально отслеживать эти вещи и поддерживать работу Process Explorer, но я не хочу, чтобы это бремя обслуживания ложилось на мой проект.

Теперь у меня есть два варианта:

  1. Поместите некоторый специальный код в родительский (вызывающий) процесс, чтобы он вызывал CreateProcess с отключенным наследованием, когда это уместно (в настоящее время он использует _spawnlp, потому что он совместим с каналами и дескрипторами файлов в стиле Unix, и вы не можете использовать CreateProcess с этими очень надежно).

  2. Пусть процесс немедленно вызовет сам себя с помощью CreateProcess, а затем завершит работу (или подождет неопределенное время), чтобы уничтожить любые нежелательные дескрипторы.

Первый кажется более эффективным. Второй более гибкий (позволяет процессу выбирать самому).

Думаю, я выберу первый вариант, потому что для моих текущих потребностей он кажется наименее худшим.

person ams    schedule 16.09.2013

Сначала немного справочной информации - почему цикл не работает под Windows:

В Linux дескрипторы имеют номера 0...n. Дескрипторы, переданные в «close()» и подобные функции, напрямую передаются в операционную систему:

void close(int handle)
{
    syscall(SYS_CLOSE,handle);
}

Однако в Windows операционная система использует свои собственные дескрипторы, похожие на адреса указателей, а библиотека C использует своего рода таблицу перевода:

void close(int handle)
{
    CloseHandle(table[handle]);
    table[handle]=NULL;
}

Если дескрипторы остаются открытыми, библиотека C не знает (не STDIN, STDOUT, STDERR), что этих дескрипторов не будет в таблице.

Теперь о самом вопросе:

В отличие от Linux, Windows смешивает разные типы дескрипторов (дескрипторы файлов, дескрипторы памяти, дескрипторы процессов и т. д.). Если бы вы могли получить список всех дескрипторов, вам пришлось бы относиться к ним по-другому.

Другой момент заключается в том, что библиотеки Windows используют некоторые дескрипторы внутри. Если вы просто закроете все дескрипторы, унаследованные родительским процессом, вы рискуете сбоем вашей программы, поскольку библиотеки Windows могут зависеть от некоторых из этих дескрипторов.

Таким образом, родительское приложение, безусловно, должно гарантировать, что никакие дескрипторы не останутся открытыми. По умолчанию дескрипторы не наследуются в Windows. Однако оболочки библиотеки C (например, «fopen()») будут устанавливать флаг «наследовать дескриптор», поэтому дескрипторы будут унаследованы.

person Martin Rosenau    schedule 13.09.2013
comment
Этот ответ не касается вопроса. - person ams; 13.09.2013