Что делает select (2), если вы закрываете (2) файловый дескриптор в отдельном потоке? - PullRequest
24 голосов
/ 13 февраля 2009

Какова функция select(2), когда дескриптор файла, который он просматривает для чтения, закрыт другим потоком?

После некоторого беглого тестирования он сразу возвращается. Я подозреваю, что результат либо в том, что (а) он все еще продолжает ждать данные, но если вы действительно попытаетесь прочитать их, вы получите EBADF (возможно - есть потенциальная гонка), или (б) что он притворяется, будто дескриптор файла никогда не передавался. Если последний случай равен true, передача одного fd без таймаута привела бы к тупику, если он был закрыт.

Ответы [ 4 ]

21 голосов
/ 19 февраля 2009

Из какого-то дополнительного исследования выясняется, что оба dwc и оба правы.

ответ обоих обоих на вопрос сводится к: это неопределенное поведение. Это не значит, что это непредсказуемо, но разные ОС делают это по-разному. Может показаться, что системы, такие как Solaris и HP-UX, возвращаются с select(2) в этом случае, но Linux не основывает это сообщение в списке рассылки linux-kernel с 2001 года.

Аргумент в списке рассылки linux-kernel заключается в том, что на него нельзя полагаться (и он не работает). В случае Linux, вызов close(2) в дескрипторе файла эффективно уменьшает счетчик ссылок на него. Поскольку есть вызов select(2) также со ссылкой на него, fd останется открытым и ожидает ввода, пока не вернется select(2). Это в основном ответ DWC . Вы получите событие в дескрипторе файла, а затем оно будет закрыто. Попытка чтения из него приведет к EBADF, при условии, что fd не был переработан. (Обеспокоенность, которую МаркР высказал в в своем ответе , хотя я думаю, что в большинстве случаев этого можно избежать при правильной синхронизации.)

Так что спасибо всем за помощь.

6 голосов
/ 13 февраля 2009

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

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

Как только вы закрываете файл, его номер становится доступным для повторного использования и может быть повторно использован при следующем вызове open (), socket () и т. Д., Даже если другой поток. Поэтому вам действительно нужно избегать подобных вещей.

5 голосов
/ 13 февраля 2009

Системный вызов select - это способ подождать, пока дескрипторы файлов не изменят состояние, в то время как программам больше нечего делать. Основное использование предназначено для серверных приложений, которые открывают несколько файловых дескрипторов, а затем ждут, что с ними что-то будет делать (принимают новые соединения, читают запросы или отправляют ответы). Эти файловые дескрипторы будут открыты в неблокирующем режиме io, так что процесс сервера не будет зависать в системном вызове в любое время.

Это дополнительно означает, что нет необходимости в отдельных потоках, потому что вся работа, которая может быть выполнена в потоке, может быть выполнена до вызова select. И если работа занимает больше времени, чем ее можно прервать, выберите вызов с тайм-аутом = {0,0}, дескрипторы файлов будут обработаны, а затем работа возобновится.

Теперь вы закрываете дескриптор файла в другом потоке. Почему у вас вообще есть этот дополнительный поток, и почему он должен закрывать файловый дескриптор?

Стандарт POSIX не дает никаких подсказок, что происходит в этом случае, поэтому то, что вы делаете, - НЕОПРЕДЕЛЕННОЕ ПОВЕДЕНИЕ. Ожидайте, что результат будет очень разным в разных операционных системах и даже в разных версиях одной и той же ОС.

С уважением, Бодо

2 голосов
/ 13 февраля 2009

Это немного сбивает с толку, что вы спрашиваете ...

Функция Select () должна вернуться после «интересного» изменения. Если метод close () просто уменьшил счетчик ссылок, а файл все еще был открыт для записи куда-либо, то нет никаких причин для вызова select ().

Если другой поток действительно закрыл () для единственного открытого дескриптора, тогда он становится более интересным, но мне нужно увидеть простую версию кода, чтобы увидеть, действительно ли что-то не так.

...