Функция называется реентерабельной, если ее можно безопасно (и без побочных эффектов) вызвать в процессе выполнения.

Некоторые однопоточные приложения могут работать с нереентерабельными функциями. Потому что большинство приложений вообще не обрабатывают сигналы и не имеют рекурсивных функций обратного вызова. Будет сложно отладить код, если вы загрузите его в ядро ​​​​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