Инструмент сборки порождает демона, который не закрывает свой стандартный вывод. Как предотвратить остановку конвейера оболочки?

Инструмент сборки порождает демона, который не закрывает свой стандартный вывод. Как предотвратить остановку конвейера оболочки?

Я использую (с закрытым исходным кодом) специфичный для поставщика инструмент сборки (Microsemi Designer, инструмент компоновки ПЛИС). Я вызываю его из скрипта оболочки (сильно упрощенно):

...  # Setup

/opt/.../designer SCRIPT:my_script.tcl |tee build.log

...  # Postprocessing

Проблема в следующем: даже после designerзавершения процесса он teeпродолжает работать, и, таким образом, выполнение скрипта не продолжается.

Насколько я понимаю, это происходит потому, что designerпорождает долгоживущий демон, windu_scmd50, и этот демон не закрывает свой стандартный вывод (который он наследует от designer). Так же как teeи ожидание EOF на его стандартном вводе (подключенном к каналу), который так и не приходит.

Доказательства того, что это является причиной проблемы:

  • psпоказывает, что процесс создания тройника все еще выполняется, а процесс проектирования — нет.
  • Когда я завершаю windu_scmd50процесс, teeон немедленно завершается, а скрипт продолжается (но я не могу сделать это в продакшене).
  • Если я не передаю вывод в designer( teeили куда-либо еще; то же самое происходит и с catin вместо tee), скрипт не останавливается.
  • Если я запускаю designerиз Jenkins (без перенаправления вывода в файл), Jenkins уведомляет меня о том, что «Процесс допустил утечку файловых дескрипторов», ссылаясь на эта страница помощи(судя по всему, Дженкинс конкретно рассматривает эту ситуацию).

Так:Как я могу гарантировать, что teeпроцесс завершится в тот момент (или вскоре после этого) designer?

Вот некоторые подходы, которые я рассмотрел:

  • Можно ли перенаправить stdout запущенного процесса?
  • Есть ли инструмент, который я могу использовать вместо трубы-оболочки и который определит завершение процесса designer?

Некоторые подходы, которые невозможны:

  • Я не могу игнорировать вывод процесса designer; мне нужно записать его в файл журнала.
  • Я не могу завершить windu_scmd50процесс, так как он может использоваться другими процессами на машине.
  • Обходные пути, описанные настраница помощи Jenkinsпохоже, не применяются, поскольку они также заглушат вывод из designer.
  • Я не могу изменить установку инструмента, чтобы заменить windu_scmd50 исполняемый файл оболочкой.

Еще несколько заметок:

  • Было бы неплохо регистрировать вывод из windu_scmd50процесса, но это не обязательно. Однако мне нужно регистрировать вывод из процесса дизайнера.
  • designerНа практике между и есть еще один фильтр, teeкоторый добавляет временные метки ( designer ... |ts -s |tee build.log)
  • Использование bashспецифических функций допустимо; совместимость с shне является обязательным требованием.

Вот минимальный пример, демонстрирующий проблему ( test.sh):

#!/bin/bash
echo "Start"
sleep 10 &    # Background process that does not close stdout
echo "End"

Вызов этого скрипта напрямую ( ./test.sh) печатает "Start" и "End" и немедленно завершается. Передача вывода другому процессу ( ./test.sh |cat) печатает "Start" и "End" немедленно, но затем делает паузу в 10 секунд перед завершением.

решение1

Довольно простой обходной путь — ввести сообщение в поток после designerзавершения процесса. catПредшествующий фильтр типа - teeдолжен выйти, когда встретит сообщение.

Это будет что-то вроде:


{ /opt/.../designer SCRIPT:my_script.tcl; echo message; } \
| sed -n '/^message$/q;p' | tee build.log

Примечания:

  • Если designer(или что-либо, что наследует его stdout) выводит сообщение, то sedпреждевременно завершит работу. Выберите sTr1ng_Unlik3ly t0-Be enCOUnTered_in vvhat designer PRinT5. Символ ASCII EOT (восьмеричное 004) может быть хорошим выбором:
    { …; printf '\004\n'; } | sed -n "/^$(printf '\004')\$/q;p" | …

  • Есть пределы тому, что sedможно выдержать. Смотритеэтот ответ оgrep, это похоже на sed.

  • ^message$соответствует полной строке, содержащей messageтолько. Это предполагает, что вывод (если есть) процесса designerзаканчивается символом новой строки (т.е. последняя строка завершена; см.линияпротивнеполная строка); только тогда messageбудет в своей собственной строке. Если designerгенерирует неполную строку наверняка, то вы хотите sed -n '/message$/{s/message$//;p;q};p'. Если это может быть любым способом, то вы хотите sed -n '/^message$/q;/message$/{s/message$//;p;q};p'. Обратите внимание, что неполная строка из designerстанет полной строкой.

  • Немолчаливые потомки designerмогут вмешиваться:

    • Они могут вызвать messageпоявление средней строки (даже если они пытаются вывести полные строки). Для нашего sedэто будет как будто designerсгенерирована неполная строка.
    • Если выбранное вами сообщение очень длинное, то оно может появиться в конвейере вместе с другими данными (см.этот ответиPIPE_BUFздесь). В таком случае мы sedего вообще не обнаружим.

Связанный контент