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

时间:2014-04-04 06:11:11

标签: c++ multithreading winapi 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;
}

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

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

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

2 个答案:

答案 0 :(得分:6)

  

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

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

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

  

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

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


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

答案 1 :(得分:4)

非静态局部变量通常分配在堆栈或寄存器中。每个线程都有自己的堆栈和寄存器副本(或者至少操作系统通过保存和恢复内容来创建错觉)。

因此,非静态局部变量是线程安全的,它们不会忘记"上下文切换时的值。