Почему эта программа C генерирует SIGPIPE позже, чем ожидалось?

Эта программа генерирует SIGPIPE после того, как она была отправлена ​​на "head -n 1" после случайного времени. Я понимаю, что, поскольку мы кормим больше "head -n 1" после первой строки, мы ожидаем, что он сгенерирует SIGPIPE, но вместо этого он сделает это случайным числом (обычно > 20 и 200) до exitting. Любая идея почему?

#include <stdio.h>
#include <stdlib.h>

main()
{
  int i;
  char *s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n";

  i = 0;
  while (1) {
    fputs(s, stdout);
    fflush(stdout);
    fprintf(stderr, "Iteration %d done\n", i);
    i++;
  }
}

Это не домашнее задание, только что-то в моем профессоре отмечает, что я не понимаю.

Ответы

Ответ 1

Это капризы планирования.

Ваш продюсер - позвоните ему alphabeta - может работать некоторое время до того, как head сможет читать и выходить (таким образом, разбивая трубку).

То, что "некоторое количество времени", конечно, является переменной.

Иногда alphabeta запускается 20 раз, прежде чем head может читать stdin и выйти. Иногда 200 раз. В моей системе иногда бывает 300 или 1000 или 2000 раз. В самом деле, это теоретически может быть связано с пропускной способностью трубы, соединяющей производителя и потребителя.

Для демонстрации допустим некоторую задержку, поэтому мы можем быть разумно уверены, что head застревает в read(), прежде чем alphabeta выдает одну строку вывода:

so$ { sleep 5; ./alphabeta; } | head -n 1
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Iteration 0 done

(NB это не гарантирует, что alphabeta будет повторять только один раз в приведенном выше примере. Однако в ненагруженной системе это будет более или менее, всегда будет иметь место: head будет готов, а его чтение/выключение произойдет более или менее немедленно.)

Следите за тем, что происходит, когда мы искусственно задерживаем head:

so$ ./alphabeta | { sleep 2; head -n 1; }
Iteration 0 done
...
Iteration 2415 done    # <--- My system *pauses* here as pipe capacity is reached ...
Iteration 2416 done    # <--- ... then it resumes as head completes its first read()
...
Iteration 2717 done    # <--- pipe capacity reached again; head didn't drain the pipe 
ABCDEFGHIJKLMNOPQRSTUVWXYZ

В стороне, @R.. совершенно прав в своих замечаниях о том, что SIGPIPE является синхронным. В вашем случае первая вызванная fflush запись в разбитую трубу (после выхода head) будет синхронно генерировать сигнал. Это документальное поведение.

Ответ 2

Я думаю, что это просто потому, что сигналы асинхронны.

Обновление: как отмечали другие (точнее, многие, в том числе SIGPIPE), нет. Это был непродуманный ответ:)

Ответ 3

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

Ответ 4

Команда head использует stdio для чтения stdin, и, таким образом, первый getc не возвращается до заполнения буфера или EOF, в зависимости от того, что произойдет раньше.