Подтвердить что ты не робот

Что делает select (2), если вы закрываете (2) файловый дескриптор в отдельном потоке?

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

В стандарте POSIX не указаны какие-либо намеки, что происходит в этом случае, так что вы делаете UNDEFINED BEHAVIOR. Ожидайте, что результат будет очень различным между различными операционными системами и даже между версией той же ОС.

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

Ответ 4

Это немного запутывает то, что вы просите...

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

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