可能的堆栈损坏。

3

关于我之前提到的有关GDB无法确定SIGSEGV的位置的问题,

我的线程代码如下:

void *runner(void *unused)
{
 do
 {
 sem_wait(&x);
  ...

  if(/*condition 1 check*/)
  {
   sem_post(&x);
   sleep(5);
   sem_wait(&x);
   if(/*repeat condition 1 check; after atleast 5 seconds*/)
   {
    printf("LEAVING...\n");
    sem_post(&x); 
    // putting exit(0); here resolves the dilemma
    return(NULL);  
   }
  }
 sem_post(&x);
 }while(1);

}

主要代码:

sem_t x;    

int main(void)
{   
    sem_init(&x,0,1);
        ...
    pthread_t thrId;
    pthread_create(&thrId,NULL,runner,NULL);
        ...
    pthread_join(thrId,NULL);
    return(0);
}

编辑:在运行线程代码中加入exit(0)会使故障消失。


造成堆栈损坏的原因是什么?

GDB 输出:(0xb7fe2b70 是运行线程 ID)

LEAVING...
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7fe2b70 (LWP 2604)]
0x00000011 in ?? ()

Valgrind输出:

==3076== Thread 2:
==3076== Jump to the invalid address stated on the next line
==3076==    at 0x11: ???
==3076==    by 0xA26CCD: clone (clone.S:133)
==3076==  Address 0x11 is not stack'd, malloc'd or (recently) free'd
==3076== 
==3076== 
==3076== Process terminating with default action of signal 11 (SIGSEGV)
==3076==  Bad permissions for mapped region at address 0x11
==3076==    at 0x11: ???
==3076==    by 0xA26CCD: clone (clone.S:133)
==3076==  Address 0x11 is not stack'd, malloc'd or (recently) free'd

1
你需要发布更多的代码,所有有趣的部分都没有了。 - EboMike
Valgrind报告的是唯一的错误吗?如果它报告了其他错误,请从第一个开始。 - nos
在运行线程代码中使用exit(0)根本不是一个可接受的解决方案。 - BatchyX
3个回答

7
写一个新的源文件,其中包含一个 main 函数,该函数执行与此处发布的 main 相同的操作,不使用 pthread_create 调用函数。看看是否可以独立于使用线程重现问题。从事情的样子来看,在单线程环境中,您的信号量仍然应该正常工作。

如果仍然失败,则调试将更容易。

由于您说调用 exit 而不是返回没有产生错误,这表明您已经损坏了在启动 runner 时堆栈上的返回地址。通过调用 exit,您不依赖于此内存区域来到达退出函数(如果您返回 pthread_exit 将被 pthread 库代码调用已调用 runner)。我认为 valgrind 输出不是 100% 准确的 -- 不是由于 valgrind 的任何错误,而是因为触发错误的位置以及触发的错误类型使得很难确定谁调用了什么。

您可能感兴趣的一些 gcc 标志:

-fstack-protector-all -Wstack-protector

在这里,如果没有使用-f选项,则警告选项无法起作用。

您也可以尝试:

-fno-omit-frame-pointer

2

您的代码中缺少重要的部分,但栈溢出的最常见原因有:

  • 将指向栈上元素的指针存储起来,并在对象已经超出作用域后继续使用它。
  • 缓冲区溢出,例如在栈上有一个char buffer[20],并在边界外写入(使用sprintf是实现这一点的好方法)。
  • 错误转换,即在栈上有一个基类A,将其转换为派生类并使用它。

他的堆栈似乎没有被破坏。 - BatchyX

1
使用valgrind或等效的内存检查工具来找出问题所在。停止猜测。如果您不知道代码是否存在问题,请勿发布不完整的代码。错误可能在此函数之外。例如,也许信号量没有初始化。
从valgrind输出中,我可以建议您的pthread_create()行必须包含一个无效的函数指针。因此,pthread跳转到该虚假地址并崩溃。显然没有堆栈...

信号量已经初始化,Valgrind 输出已发布。 - user191776
只有在valgrind没有抱怨的情况下,我才会相信你。认真点,别瞎猜了。 - BatchyX
我已经放置了主要代码。我不知道还能发布什么,否则就要在这里倾倒整个源文件了。 - user191776
@Kedar:那就这么做吧。如果代码非常大,请将其发布在pastebin.com上,并发布链接。 - Adam Rosenfield
让gdb运行程序直到pthread_create行,然后使用“dissass $eip,$eip + 40”显示调用pthread_create的main函数的汇编代码。此外,也许可以使用-Wall -Wextra编译您的代码。 - BatchyX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接