多个线程访问相同的寄存器值 ARM 程序集
Multiple Threads Accessing Same Register Value ARM Assembly
我正在使用一些 ARM 代码对需要访问同一寄存器的多个线程进行试验。我在 asm 调用中使用 C。但是,我把运行存成总线错误。这是我的意思的一个例子:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int someVar = 0;
void setup(){
__asm__("LDR R7, =someVar\n\t"); // load someVar into R7
}
void loadAction(){
__asm__("LDREX R1, [R7]\n\t");
}
int main(){
setup();
loadAction();
}
这完全没问题。
然而,当我引入线程时,像这样的总线错误结果:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int someVar = 0;
void setup(){
__asm__("LDR R7, =someVar\n\t"); // load someVar into R7
}
void *loadAction(void *threadArg){
__asm__("LDREX R1, [R7]\n\t");
}
int main(){
pthread_t tid;
setup();
int i;
for (i = 0; i < 1; i++){
pthread_create(&tid, NULL, loadAction, (void *)&tid);
}
pthread_exit(NULL);
return 0;
}
我对这个问题的最佳猜测是 R7 中的值无效,因为不能保证在子例程调用中保留寄存器。也许在第一个例子中,我只是运气好,放在 setup() 中的 R7 中的值恰好保留了下来,但线程代码导致 R7 中的值被破坏。
如果是这样的话,有什么方法可以保留R7吗?我可以保存并存储到堆栈,但多个线程将同时访问它。是否有某种编译标志我可以通过 gcc 传递以确保在 setup() 中加载的 R7 的值在 loadAction() 中被访问?
谢谢
每个线程都有自己的寄存器值。这实际上是使线程成为线程的原因。如果两个线程共享它们的寄存器值(尤其是 PC 和 SP),它们就是同一个线程。
是的,寄存器通常不会在子例程调用中保留。编译器使用它们来存储 您的代码使用的每个值 - 它们不是一些您只能通过内联汇编代码访问的特殊不寻常的东西。根据程序中使用的 calling convention,编译器可能有义务保存它决定使用的某些寄存器的旧值,并在子例程 returns.[=18= 之前将它们恢复回来]
根据链接的维基百科页面,在 32 位 ARM 上,r7 是 编译器必须保存和恢复的寄存器之一。
在这种情况下,编译器尚未决定在 setup
函数中使用 r7(因为其中没有实际代码被编译);如果 setup
确实有一堆 C 代码并且编译器决定使用 r7,那么它会在开始时保存旧值,在末尾加载旧值,在中间使用寄存器,然后你的加载到 r7 会覆盖编译器认为存储在那里的任何值,从而破坏 C 代码。到 loadAction
运行 在同一个线程上时,旧值将被放回 r7.
在C语言中有一种方法可以保存一个寄存器,它被称为变量。
而不是这个:
// wrong code
void setup(){
__asm__("LDR R7, =someVar\n\t"); // load someVar into R7
}
void *loadAction(void *threadArg){
__asm__("LDREX R1, [R7]\n\t");
}
如果这样写:
int *pSomeVar;
void setup(){
pSomeVar = &someVar; // load someVar into pSomeVar
}
void *loadAction(void *threadArg){
int value = *pSomeVar;
}
然后编译器将尽一切努力确保值从 setup
变为 loadAction
。
我正在使用一些 ARM 代码对需要访问同一寄存器的多个线程进行试验。我在 asm 调用中使用 C。但是,我把运行存成总线错误。这是我的意思的一个例子:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int someVar = 0;
void setup(){
__asm__("LDR R7, =someVar\n\t"); // load someVar into R7
}
void loadAction(){
__asm__("LDREX R1, [R7]\n\t");
}
int main(){
setup();
loadAction();
}
这完全没问题。
然而,当我引入线程时,像这样的总线错误结果:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int someVar = 0;
void setup(){
__asm__("LDR R7, =someVar\n\t"); // load someVar into R7
}
void *loadAction(void *threadArg){
__asm__("LDREX R1, [R7]\n\t");
}
int main(){
pthread_t tid;
setup();
int i;
for (i = 0; i < 1; i++){
pthread_create(&tid, NULL, loadAction, (void *)&tid);
}
pthread_exit(NULL);
return 0;
}
我对这个问题的最佳猜测是 R7 中的值无效,因为不能保证在子例程调用中保留寄存器。也许在第一个例子中,我只是运气好,放在 setup() 中的 R7 中的值恰好保留了下来,但线程代码导致 R7 中的值被破坏。
如果是这样的话,有什么方法可以保留R7吗?我可以保存并存储到堆栈,但多个线程将同时访问它。是否有某种编译标志我可以通过 gcc 传递以确保在 setup() 中加载的 R7 的值在 loadAction() 中被访问?
谢谢
每个线程都有自己的寄存器值。这实际上是使线程成为线程的原因。如果两个线程共享它们的寄存器值(尤其是 PC 和 SP),它们就是同一个线程。
是的,寄存器通常不会在子例程调用中保留。编译器使用它们来存储 您的代码使用的每个值 - 它们不是一些您只能通过内联汇编代码访问的特殊不寻常的东西。根据程序中使用的 calling convention,编译器可能有义务保存它决定使用的某些寄存器的旧值,并在子例程 returns.[=18= 之前将它们恢复回来]
根据链接的维基百科页面,在 32 位 ARM 上,r7 是 编译器必须保存和恢复的寄存器之一。
在这种情况下,编译器尚未决定在 setup
函数中使用 r7(因为其中没有实际代码被编译);如果 setup
确实有一堆 C 代码并且编译器决定使用 r7,那么它会在开始时保存旧值,在末尾加载旧值,在中间使用寄存器,然后你的加载到 r7 会覆盖编译器认为存储在那里的任何值,从而破坏 C 代码。到 loadAction
运行 在同一个线程上时,旧值将被放回 r7.
在C语言中有一种方法可以保存一个寄存器,它被称为变量。
而不是这个:
// wrong code
void setup(){
__asm__("LDR R7, =someVar\n\t"); // load someVar into R7
}
void *loadAction(void *threadArg){
__asm__("LDREX R1, [R7]\n\t");
}
如果这样写:
int *pSomeVar;
void setup(){
pSomeVar = &someVar; // load someVar into pSomeVar
}
void *loadAction(void *threadArg){
int value = *pSomeVar;
}
然后编译器将尽一切努力确保值从 setup
变为 loadAction
。