#c #unix #exec #fork #signals
#c #unix #exec #fork #сигналы
Вопрос:
Это некоторый псевдокод того, чего я пытаюсь достичь:
fork
в дочернем dup2 дескриптор вывода в файл, затем выполняется другая программа
в родительском, убейте дочерний элемент через определенный промежуток времени
Проблема в том, что после того, как я уничтожаю дочерний файл, выходной файл пуст, даже если дочерний файл записал в него на каком-то этапе. Что я делаю не так? Я не хочу ждать дочернего элемента, но я также не хочу, чтобы он отменял то, что уже записано в файл.
Комментарии:
1. Это
kill
делает именно то, что вы хотите, чтобы это делалось (отправляетSIGINT
вchildpid
). В чем именно ваша проблема?2. О, хорошо. Я дублирую файловый дескриптор в файл для записи в мой дочерний процесс, а затем выполняю его в своем дочернем процессе. Это работает, но моя проблема в том, что как только я убиваю дочерний файл, выходной файл становится пустым. Если я не уничтожу его, то у него будет правильный вывод из моего дочернего процесса, но если я это сделаю, то у него ничего не будет (хотя он уже записал бы материал в файл, прежде чем я его уничтожу). Нужно ли мне сказать дочернему устройству закрыть выходной поток, прежде чем я его убью или что-то в этом роде?
3. пожалуйста, отредактируйте свой вопрос (и его название), чтобы точно объяснить, что происходит и чего вы хотите добиться. Не ждать, пока ребенок закончит правильно, не имеет смысла, если вам нужно, чтобы он закончил что-то делать.
4. Я не хочу ждать его завершения, но я также не хочу, чтобы он отменял то, что он уже сделал (записал в файл), поэтому я не понимаю, что он делает. Я отредактирую вопрос.
Ответ №1:
Если ваш дочерний элемент умирает в результате сигнала, он не сбрасывает никаких буферов, вот почему вы не видите никакого вывода. Для этого есть решения
- Обработайте сигнал и очистите буферы
- Используйте небуферизованный ввод-вывод (следует избегать этого)
- Синхронизируйте дочерний элемент и родительский элемент (используя канал) (чтобы родитель знал, что убивать дочерний элемент безопасно).
Конечно, третий вариант довольно глупый: дочерний элемент, сигнализирующий родителю, может быть заменен простым завершением.
Редактировать
В свете отредактированного вопроса и комментариев
stdio
(printf, fwrite и т.д.) Из соображений эффективности ввода-вывода Использует некоторые буферы. То есть, когда вы выполняете простую запись, низкоуровневая операция выполняется не сразу. Данные копируются в некоторый буфер, а позже, когда библиотека сочтет это необходимым (полные буферы или что-то еще), буферы будут очищены — материал будет записан.
Теперь, когда программа вызывает exit(3)
(обычное завершение процесса), происходит то, что stdio
буферы сбрасываются. Если программа завершает работу в результате сигнала, stdio
буферы не очищаются, и память просто запрашивается операционной системой.
Если у вас нет контроля над вашими программами exec
(скажем, вы оболочка), вы не можете быть уверены, что они сделают свое дело, если вы их убьете. То есть это не ваша ответственность. Однако, если у вас есть контроль над ними (вы что-то сказали о канале), вы можете просто передать по каналу некоторую последовательность, которая вызовет дочерние элементы exit(3)
.
Комментарии:
1. Спасибо! Что из этого я хочу сделать, если я не хочу изменять исполняемую программу?
2. @Sam Ну, поскольку вы не хотите изменять программу, которую вы
exec
, трудно сказать. Почему вы не хотите, чтобы он просто завершался нормально? Если он не должен завершаться нормально, как вы узнаете, когда пришло время его отключить?3. Мне нужно, чтобы пользователь мог отправлять ему сигналы для его отключения, но я не хочу, чтобы он стирал то, что он уже сделал. Честно говоря, я действительно не понимаю, почему он стирает то, что уже сделано. Можете ли вы подробнее объяснить очистку буферов? Зачем это необходимо? Могу ли я сделать это в родительском?
4. Почему вы говорите, что дочерний элемент «стирает то, что он уже сделал»? Если на самом деле вы видите данные в файле, которые усекаются при уничтожении дочернего элемента, это было бы замечательно. Откуда вы знаете, что дочерний элемент вообще что-либо сделал, прежде чем вы его отключите?
5. @Sam: Вместо
sleep(1)
правильного способа было бы сначала заблокироватьSIGCHLD
с помощьюsigprogmask
, затем отправитьSIGINT
, использоватьsigtimedwait
для ожидания (с таймаутом в 1 секунду)SIGCHLD
, использоватьwaitpid
withWNOHANG
, чтобы увидеть, завершен ли он, и, если нет, отправитьSIGKILL
. Таким образом, вы избегаете внесения ненужных задержек в свою программу — задержек, которые пользователи наверняка ненавидят!