Часто системные вызовы, такие как write(2)
, read(2)
, close(2)
и т. д., терпят неудачу из-за того, что они прерываются сигналом со значением errno
EINTR
(скажем, размер окна терминала изменился и был получен SIGWINCH
), что является временной ошибкой и должно быть повторено, и код часто использует оболочки вокруг этих системных вызовов, которые повторяют попытку EINTR
(и часто EAGAIN
или ENOBUFS
).
Но можно застрять в теоретической ситуации, когда код просто продолжает бесконечно зацикливаться EINTR
либо из-за получения непрерывных сигналов, либо из-за того, что системный вызов был перехвачен специальной реализацией этого системного вызова, которая просто возвращает EINTR
.
В таких случаях в библиотечном коде сколько раз имеет смысл повторить системный вызов?
Отсутствие повторной попытки не остановит поток сигналов. /// Не думайте об этом как о повторной попытке, думайте об этом как о возобновлении ожидания, которое выполнял ваш блокирующий вызов. Это то, что вы бы сделали, если бы не получили сигнал.
Да, это «возможно», но не «вероятно». Скажем, если бы вы создали интервальный таймер (см., например, man 2 timer_create
) с интервалом повторения в пару наносекунд, вы, скорее всего, могли бы столкнуться с чем-то подобным, что вы описываете. Хотя это скорее результат ошибки проектирования программы, чем то, чего вы обычно ожидаете.
@ikegami Достаточно справедливо, но будет ли вероятность того, что библиотечный код выйдет из строя, скажем, после 40-100 итераций или будет продолжать цикл бесконечно?
would it be apt for library code to fail after say 40-100 iterations
было бы кстати. Просто делайте это, не работайте проактивно: если у вас возникнет проблема с 40 итерациями, вы сделаете 100. Нет особых причин беспокоиться об этом.
Опять же, он не терпит неудачу, он ждет. Что бы read
делал без сигнала? Жди вечность. Так почему же вы думаете, что нужно делать что-то другое, если поступает сигнал??? Цель EINTR — дать обработчикам сигналов возможность работать, когда в противном случае вызов был бы заблокирован.
Сколько раз имеет смысл повторять попытки для оболочек системных вызовов, которые повторяют попытку EINTR?
От нуля до бесконечности.
Стандартная библиотека Glibc используется на миллиардах устройств по всему миру. Вызов printf("Hello world\n");
приведет к вызову функции _IO_new_file_write
, которая выглядит следующим образом:
ssize_t
_IO_new_file_write (FILE *f, const void *data, ssize_t n)
{
ssize_t to_do = n;
while (to_do > 0)
{
ssize_t count = (__builtin_expect (f->_flags2
& _IO_FLAGS2_NOTCANCEL, 0)
? __write_nocancel (f->_fileno, data, to_do)
: __write (f->_fileno, data, to_do));
if (count < 0)
{
f->_flags |= _IO_ERR_SEEN;
break;
}
to_do -= count;
data = (void *) ((char *) data + count);
}
n -= to_do;
if (f->_offset >= 0)
f->_offset += n;
return n;
}
Как вы можете, while (to_do > 0)
функция будет выполнять цикл бесконечно много раз, пока данные не будут записаны, игнорируя любой EINTR
сигнал и даже не проверяя его.
Поскольку это программное обеспечение используется буквально почти на каждом Linux-устройстве по всему миру, можно с уверенностью сказать, что бесконечное количество циклов вполне допустимо.
Теперь вы, возможно, работаете с нестандартной реализацией записи. Например, на встроенном устройстве программист может реализовать свою собственную реализацию write
, например _write_r
при использовании стандартной библиотеки Newlib C. Если такой программист устанавливает errno = EINTR
и бесконечно возвращает 0 из своей write
функции, я бы сказал, что это его дело. Но если вы чувствуете, что хотите обнаружить такие ситуации, вперед. Я не чувствую необходимости это делать.
Контракт функции write
заключается в том, что когда количество записанных байтов не равно тому, сколько байт вы хотели записать, вы должны повторить вызов со смещенными данными и счетчиком. Ничего не поделаешь.
Re «Как вы можете, while (to_do > 0)
функция будет выполнять цикл бесконечно много раз, пока данные не будут записаны, игнорируя любой сигнал EINTR
и даже не проверяя его.», Разве она не завершается через break
при получении EINTR
или любой другой ошибки?
Я никогда не видел, чтобы кто-нибудь вводил ограничение повторных попыток для чего-то подобного. Нет оснований полагать, что прерывания будут повторяться. Это не похоже на ошибку доступа к удаленному ресурсу, где есть большая вероятность, что проблема окажется постоянной.