这是代码

  struct val {
  int x;
  int y;
  };

  struct memory {
  struct val *sharedmem;
  };

  struct val va;
  struct memory mem;

  void *myThreadFun(void *vargp)
  {
  va.x = 1;
  va.y = 2;

  mem.sharedmem = &va;
  sleep(1);
  printf("Printing GeeksQuiz from Thread0 x = %d y = %d sharedmem = %d\n", va.x, va.y, mem.sharedmem->x);
  return NULL;
  }

  int main()
  {
   pthread_t tid;
   printf("Before Thread \n");
   asm volatile ("" ::: "eax", "esi");
   pthread_create(&tid, NULL, myThreadFun, NULL);

   asm volatile ("mfence" ::: "memory");
   printf("sharedmem = %d\n", mem.sharedmem->x); <----- **Here is the fault**
   pthread_join(tid, NULL);
   printf("After Thread \n");
   exit(0);
  }


查看dmesg时,它显示40073f作为故障地址,即

  mov eax,DWORD PTR [rax]


我尝试将一些通用寄存器添加到受破坏的寄存器中,但是仍然没有成功。在gdb下运行时,该代码运行良好。

最佳答案

为了进一步说明我的意见:

在编写单线程应用程序时,可以确定代码的较早部分将(有效)始终在代码的较后部分之前执行。

但是,当您拥有多个线程时,事情就会变得棘手。您怎么知道那时将发生什么顺序?线程会快速启动吗?还是慢慢地?主线程会继续处理吗?还是操作系统会安排其他线程接下来运行,以允许“新”线程继续运行?

在编写多线程应用程序时,通常会在2个(或更多)线程之间进行这种“竞争”,以查看谁先到达代码中的某个特定点,这是一个常见问题。众所周知,这些类型的错误很难跟踪。

如果您不采取任何措施来确保事件的顺序,那么您只是“希望”事件以正确的顺序发生。考虑到线程如何安排的自然变化,将会有一些回旋余地。除非您使用某些操作系统的同步功能来协调线程之间。

在这种情况下,我们正在查看的事件是设置mem.sharedmem变量。这是在myThreadFun线程中完成的。现在,myThreadFun线程能够在main线程尝试访问该变量之前对其进行设置吗?好吧,很难确定。可能。可能不。

在调试器下运行此命令会改变两个线程的运行速度吗?你敢打赌。怎么样?很难预测。

但是,如果在尝试访问该变量之前将代码更改为join线程,则可以确定该线程已完成工作并且可以使用该变量。 join是我所指的那些同步方法之一。

查看您的代码,也许您假设mfencememory Clobbers将为您执行这种类型的同步?抱歉,但这不是那么容易。

学习如何在多个线程之间安全地同步工作是编程的一个艰巨而艰巨的领域。重要,有用但具有挑战性。



编辑1:

我还要指出的一点是lfencesfencemfence是非常底层的指令。人们(尤其是多线程新手)会发现,将atomic functions(根据需要自动使用适当的汇编程序指令)与操作系统synchronization objects一起使用将使他们的生活变得更加轻松。

关于原子的教程和讨论比mfence还要多。它还倾向于更便携。

关于c - c中奇怪的分割错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43421667/

10-10 19:58