Epoll_wait возвращает события для дескриптора закрытого файла

Я работаю с многопоточным встроенным приложением, в котором epoll используется для ввода-вывода в одном из потоков. Я полагаюсь на особую функцию epoll, которая указывает, что закрытие файлового дескриптора автоматически удаляет его из набора epoll (вопрос/ответ 6 в man 7 epoll). В этом случае закрытие файлового дескриптора выполняется в том же потоке, в котором вызывается epoll_wait. В конечном итоге происходит то, что epoll_wait возвращает событие для дескриптора файла после его закрытия, и программа завершается сбоем, потому что она пытается получить доступ к ресурсам, которые были освобождены при закрытии дескриптора файла. Насколько мне известно, файловый дескриптор нигде не дублируется, хотя я не знаю, как это проверить. Я точно знаю, что нет вызовов fork(), dup(), dup2() или fcntl() с конкретной опцией дублирования. Этот конкретный дескриптор файла зарегистрирован с EPOLLOUT, EPOLLIN, EPOLLERR и EPOLLHUP. Он срабатывает по уровню. Есть ли какие-либо предостережения к этой функции, о которых кто-нибудь знает? Страница руководства неверна? Любая полезная информация, которая может помочь мне в дальнейшем отладке проблемы? Я знаю, что могу просто удалить дескриптор файла из набора, но я хотел бы знать, почему это происходит.


person duffsterlp    schedule 26.11.2013    source источник
comment
События, которые epoll_wait возвратил для этого файлового дескриптора, были EPOLLIN, EPOLLHUP и EPOLLERR.   -  person duffsterlp    schedule 26.11.2013
comment
Вы можете использовать strace, чтобы убедиться, что ваша программа делает то, что вы думаете. Можете ли вы воспроизвести это поведение в простой однопоточной тестовой программе?   -  person Craig M. Brandenburg    schedule 26.11.2013


Ответы (1)


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

Что я сделал в тесте:

  • создал tcp-сокет
  • привязал его к локальному хосту: 5555
  • настроить его на прослушивание
  • создал опрос
  • добавил туда сокет с hup, err и in
  • немного поспал, поэтому я мог при желании подключиться к с помощью nc
  • закрыл сокет
  • epoll_wait
  • epoll_ctl дел
  • убрано

Ожидание работает, даже если сокет был закрыт, независимо от того, подключился я к нему или нет.

Изменить: The epoll_ctl_del не удалось, если сокет был закрыт. И после прочтения текущих справочных страниц кажется, что они действительно в порядке. Страница epoll указывает на select(2) о закрытии отслеживаемого сокета, и эта страница говорит, что поведение не указано.

person Tommi Kyntola    schedule 05.12.2013
comment
Для записи, что я делаю на одном из своих серверов, когда соединение должно быть закрыто, я удаляю его из epoll явно (и фактически помечаю epoll для повторного ожидания, если он только что вышел из epoll_wait, который запускается в другом поток), а затем закройте и уничтожьте связанные ресурсы. - person Tommi Kyntola; 05.12.2013
comment
Тот факт, что epoll_ctl del сработал в вашем примере, вызывает большие подозрения. Розетка либо не закрыта, либо дублирована. Можем ли мы увидеть код? - person Jean-Bernard Jansen; 17.10.2016
comment
Нашел код. Конечно, epoll_ctl_del действительно терпит неудачу. Я сделал флаг doclose в своей тестовой программе, чтобы проверить значения ожидания epoll с закрытием сокета и без него, и я, должно быть, взял del, работающий из запуска без закрытия, когда он, конечно, работает. - person Tommi Kyntola; 27.10.2016
comment
Это правильно. Вот пример программы с выводом strace, чтобы доказать это: stackoverflow.com/a/51543273/432 - person andrewrk; 26.07.2018
comment
Я думаю, что в момент события, если в epoll_wait() нет потока, это событие добавляется в список готовности (см. idndx.com/2014/09/22/the-implementation-of-epoll-3, где говорится о rdllink). Закрытие fd не удаляет событие из списка готовых (?), а только из списка интересов (чтобы новые события не приводили к их добавлению в этот список готовых). Если затем вызвать epoll_wait(), он ищет в этом готовом списке события, которые произошли, когда этот поток не был в epoll_wait(). - person Carlo Wood; 10.07.2019