Функция называется реентерабельной, если ее можно безопасно (и без побочных эффектов) вызвать в процессе выполнения.
Некоторые однопоточные приложения могут работать с нереентерабельными функциями. Потому что большинство приложений вообще не обрабатывают сигналы и не имеют рекурсивных функций обратного вызова. Будет сложно отладить код, если вы загрузите его в ядро Linux в виде модуля или во встроенной системе.
В этом коде функция calculate
не является реентерабельной. Потому что функция calculate
может быть вызвана сразу после выполнения расчета (строка номер 7) и устанавливает значение переменной sum
. Однако в этом коде это не представляется возможным. Таким образом, даже с нереентерабельной функцией этот код все еще жизнеспособен.
Запустим:
Давайте обработаем сигнал Linux, SIGINT в коде:
Каждый раз, когда в этот код отправляется сигнал SIGINT (Ctrl + C), будет вызываться функция signal_handler
. Обратите внимание, что функция signal_handler
создает переменную с именем sum_internal
.
Запустим:
Кажется, он работает нормально.
Я изменю функцию signal_handler
. Функция signal_handler
изменяет глобальную переменную sum
:
Давайте проверим это:
Поскольку переменная sum
является глобальной, при отправке сигнала SIGINT функция signal_handler
изменяет ее. Мы представляем вызов функции с интенсивным вводом-выводом в функции calculate
.
Во-первых, функция calculate
вызывается функцией main
(3 + 5). Кстати, я отправил программе сигнал SIGINT. Глобальная переменная sum
была изменена. Поэтому мы видим на мгновение следующую строку:
Result from main: 110
Проблема в том, что функция calculate
не является реентерабельной. Мы можем исправить код следующим образом:
Вроде нормально:
Иногда невозможно изменить реентерабельные функции. Потому что они являются частью glibc или частью сторонней библиотеки. Вы можете заблокировать сигнал при выполнении реентерабельной функции и разблокировать сигнал, когда закончите с реентерабельной функцией:
Запустим:
Нам удалось решить проблему, так как это был простой код. Представьте, что компилятор предупреждает вас, когда вы пытаетесь изменить глобальную переменную.
Попробуем написать такой же нереентерабельный код на Rust:
Rust заставит вас использовать глобальные переменные в блоке unsafe
. Вы можете понять, что здесь что-то подозрительно.
Почему функции malloc() и printf() не являются реентерабельными?
https://stackoverflow.com/questions/3941271/why-are-malloc-and-printf-said-as-non-reentrant
Какие функции можно безопасно использовать?
https://manpages.ubuntu.com/manpages/bionic/man7/signal-safety.7.html
P.S.: strtok
раньше была нереентерабельной функцией. Поэтому в glibc был добавлен strtok_r
. Однако на момент этой фиксации strtok является оболочкой для strtok_r