Вот фрагмент кода, который IBM повторяет:
/* reentrant function (a better solution) */
char *strtoupper_r(char *in_str, char *out_str)
{
int index;
for (index = 0; in_str[index]; index++)
out_str[index] = toupper(in_str[index]);
out_str[index] = 0
return out_str;
}
Для меня этот код не реентерабельный, потому что индекс для счетчика цикла определяется локально. Если ОС прервет этот поток внутри цикла, а другой поток вызовет эту функцию, индекс будет потерян. Что мне не хватает? Почему этот код считается реентерабельным?
Сохраняет ли ОС копию локальных переменных, таких как index, в стек потока, когда она прерывает поток, а затем восстанавливает переменные, когда обработка продолжается?
Кажется, чтобы сделать эту функцию, входящий в состав Index индекс должен был бы быть частью интерфейса в качестве хранилища, предоставляемого вызывающей стороной.
не реентерабельный, потому что индекс для счетчика цикла определяется локально. Если ОС прервет этот поток внутри цикла, а другой поток вызовет эту функцию, индекс будет потерян. Что мне не хватает? Почему этот код считается реентерабельным?
Сам ЦП сохранит, по крайней мере, текущий указатель команды (вероятно, регистр флагов и несколько регистров сегментов и стека, но это зависит от ЦП), когда происходит прерывание, то, например, (для x86) вызывать код на основе таблицы указателей функций по определенному адресу памяти. Можно ожидать, что эти обработчики прерываний сохранят (например, push-to-stack) другие регистры, которые они хотят использовать, а затем восстановят их перед возвратом.
Каждый поток имеет свой собственный стек, так что все это висит вместе.
Сохраняет ли ОС копию локальных переменных, таких как index, в стек потока, когда она прерывает поток, а затем восстанавливает переменные, когда обработка продолжается?
Часто … либо сохраняются в стек, либо у некоторых ЦП (например, Sparcs) есть окна регистров — одни и те же коды операций ЦП обращаются к разным регистрам во время работы обработчика прерываний, а затем контекст переключается обратно на регистры, которые использовала программа.
Именно использование не-стековых данных предотвращает повторное поступление функции, например, статическая переменная в теле функции или некоторая глобальная переменная / буфер.
Нестатические локальные переменные обычно размещаются в стеке или регистрах. Каждый поток имеет свою собственную копию стека и регистров (или, по крайней мере, ОС создает эту иллюзию, сохраняя и восстанавливая содержимое).
Поэтому нестатические локальные переменные являются потокобезопасными, они не «забывают» свое значение при переключении контекста.