Запускаемый уровнем epoll_wait() при выводе gdb MI через канал не уведомляет о существовании строки (gdb)\n

В приложении я создаю gdb и подключаю его stdout (и другие) к каналам. Затем я epoll_wait в этом канале (и других), чтобы получать уведомления о получении ответа от gdb.

Каждый раз, когда epoll_wait просыпается с положительным возвращаемым значением (есть fd для чтения), я читаю одну строку из канала stdout gdb (если это fd с событием) и возвращаюсь к epoll_wait .

Все это работает нормально, за исключением того, что иногда последняя строка ответа gdb (которая неизменно "(gdb)\n") не читается, а epoll_wait возвращает 0 навсегда. Если я подожду несколько секунд, а затем прочитаю из канала stdout gdb, несмотря на то, что epoll_wait возвращает 0, я могу получить строку "(gdb)\n".

В чем дело? Эти данные явно находятся в конвейере и готовы к чтению, но epoll, запускаемый уровнем, не генерирует для них событие.

Некоторые примечания:

  • Канал, подключенный к stdout gdb, создается с помощью O_NONBLOCK.
  • epoll_create1 вызывается с EPOLL_CLOEXEC (и никак иначе), т.е. срабатывает по уровню.
  • Я использую GNU getline() для чтения строки
  • После каждого вызова getline() я clearerr() выполняю fd конвейера (я делаю это, потому что в тестовом приложении я заметил, достигнут ли конец EOF (потому что другой конец конвейера не закончил запись всей строки до того, как я ее прочитал), функции на основе stdio застревают, думая, что EOF достигнут. Я могу справиться с чтением строки по частям, так что это нормально. Я также пытался удалить вызов clearerr() безрезультатно)
  • Если я добавлю задержку в одну секунду после каждой прочитанной строки и перед повторным epoll_waiting, epoll_wait немедленно вернет stdout fd для каждой строки исходного сообщения версии+лицензии, но не для последней "(gdb)\n" строки.

person Shahbaz    schedule 21.05.2020    source источник


Ответы (1)


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

  • Вы звоните getline
  • getline звонит read
  • В канале доступны две строки, и read возвращает их обе.
  • getline дает вам первую строку и буферизует вторую
  • Вы звоните epoll_wait
  • epoll_wait блоков, так как ядро ​​​​видит, что в канале не осталось данных
  • epoll_wait истекает через несколько секунд
  • Вы снова звоните getline
  • getline дает вам вторую строку, которую он буферизировал ранее

Основная проблема заключается в том, что с точки зрения ядра нет никакой разницы между данными в буфере stdin пользовательского пространства и данными, которые вы уже прочитали и обработали. Чтобы обойти эту проблему, никогда не передавайте FD каким-либо функциям, которые заключают его в FILE *, и выполняйте все чтения самостоятельно с помощью системного вызова read напрямую.

person Joseph Sible-Reinstate Monica    schedule 22.05.2020
comment
Ах, это имеет смысл. Я до сих пор не могу объяснить, почему, если я добавляю задержку в 1 с между epoll_wait с, я все равно получаю событие для каждой строки вывода gdb (изначально он выводит около 20 строк), но, возможно, это связано с тем, что последняя строка такая короткая. Вы правы, я должен воздержаться от чтения FILE *. Спасибо - person Shahbaz; 22.05.2020