为什么这个代码被认为是re​​etrant以及当操作系统中断线程时究竟发生了什么?

bri*_*maa 7 c++ winapi multithreading reentrancy

以下是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;
}
Run Code Online (Sandbox Code Playgroud)

对我来说,这段代码不可重入,因为循环计数器的索引是在本地定义的.如果操作系统在循环中中断此线程,并且另一个线程调用此函数,则索引将丢失.我错过了什么?为什么这段代码被认为是可重入的?

操作系统在中断线程时是否将局部变量(如索引)的副本保存到线程的堆栈中,然后在处理继续时重新建立变量?

它似乎使这个函数可重入索引必须是接口的一部分作为调用者提供的存储.

Ton*_*roy 6

不可重入,因为循环计数器的索引是在本地定义的.如果操作系统在循环中中断此线程,并且另一个线程调用此函数,则索引将丢失.我错过了什么?为什么这段代码被认为是可重入的?

当发生中断时,CPU本身将至少保存当前指令指针(可能是标志寄存器和一些段和堆栈寄存器,但它依赖于CPU),然后例如(对于x86)基于函数指针表调用代码特定的内存地址.可以期望这些中断处理程序保存(例如,推送到堆栈)他们想要使用的其他寄存器,然后在返回之前恢复它们.

每个线程都有自己的堆栈,所以这一切都挂起来了.

操作系统在中断线程时是否将局部变量(如索引)的副本保存到线程的堆栈中,然后在处理继续时重新建立变量?

通常......要么保存到堆栈,要么某些CPU(例如Sparc)有注册窗口 - 相同的CPU操作码在中断处理程序运行时寻址不同的寄存器,然后上下文切换回程序正在使用的寄存器.


使用非堆栈数据可以阻止函数重入,例如函数体内的静态变量或某个全局变量/缓冲区.