【问题标题】:Signal handling in pthreadspthread 中的信号处理
【发布时间】:2011-07-14 00:25:13
【问题描述】:

我创建了一个 pthread,并在其中安装了一个信号处理程序,就像我们在 main( ) 函数中所做的那样。线程的信号处理程序是一个单独的函数。令人惊讶的是,它不起作用,即线程的信号处理程序无法捕获信号。

代码如下:

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 printf("Caught signal: %d\n",sig);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 printf("This is from thread function\n");
 signal(SIGSEGV,sig_func); // Register signal handler inside thread
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data *ptr;

 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 printf("Name:%s\n",ptr->name);
 printf("Age:%d\n",ptr->age);
}

输出:

分段错误(表示信号未被处理程序捕获)

【问题讨论】:

  • 首先并继续@sarnold 所说的,您使用了错误的 API。不要使用signal()。从手册页(阅读):“未指定多线程进程中 signal() 的效果。”在 man 2 sigaction 开始阅读文档。
  • @rlibby:那么我应该使用“struct sigaction”还是“sigevent 结构”来捕捉信号,你的意思是?

标签: c pthreads signals


【解决方案1】:

你的代码有几个问题:

  • ptr 未初始化,所以所有ptr-&gt; 部分都会导致程序崩溃
  • 您正在立即调用pthread_kill(),很可能在安装信号处理程序之前,并且在线程中(具有未指定的行为)
  • 您从信号处理程序中调用 printf(),但不能保证其正常工作(请参阅 man 7 signal 以获取安全函数列表)

这会更好,尽管您仍然需要适当的线程同步,并且如其他地方所述,您应该使用sigaction()

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 write(1, "Caught signal 11\n", 17);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 fprintf(stderr, "This is from thread function\n");
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data d;
 data *ptr = &d;

 signal(SIGSEGV,sig_func); // Register signal handler before going multithread
 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 sleep(1); // Leave time for initialisation
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 fprintf(stderr, "Name:%s\n",ptr->name);
 fprintf(stderr, "Age:%d\n",ptr->age);
}

编辑:在主线程中安装 sighandler

【讨论】:

  • 非常感谢,它成功了。那么你认为只有指针初始化是一个问题吗?但是,如果我不提供任何信号,则代码可以正常工作。
  • 也可以在 main 中注册信号处理程序,而不是线程函数。为什么会这样?
  • @kingsmasher:两个主要问题是指针初始化以及您在子线程有时间设置信号处理程序之前调用了pthread_kill()。如果没有这两个修复程序,您的程序就无法按预期运行。其他问题是可移植性问题,这些问题会导致各种 Linux 或 Unix 版本出现意外行为,因此也应该解决。在那之前,请考虑它只是偶然的。
  • 另外值得注意的是:signal() 的手册页说“signal() 在多线程进程中的影响是未指定的。”它还建议使用sigaction()
  • ptr 没有初始化,所以所有ptr-&gt; 部分都会使程序崩溃 绝对错误,它确实取决于很多事情但它肯定会导致未定义的行为。
【解决方案2】:

没有人提到的代码的一个问题是,虽然信号阻塞(和传递,如果你使用pthread_killraise)是每个线程的,但信号处理程序是每个进程的。这意味着它们对于线程间通信来说是一种非常糟糕的机制,尤其是当您的代码将被用作库代码时,因为库更改调用者的信号处理程序是非常糟糕的行为。

另请注意,与其他线程信号发送方法(如条件变量或障碍)相比,使用信号处理程序进行线程间通信的性能次优,因为至少有一个额外的用户-内核-用户转换(当信号处理程序返回时) .

【讨论】:

  • 感谢您的仔细调查和您的 cmets。你说“虽然信号阻塞(和传递,如果你使用 pthread_kill 或 raise)是每个线程的,信号处理程序是每个进程的。”如果我们在线程内注册信号处理程序,你不认为它是有参考的只有那个线程?在主函数和线程内部添加信号处理程序(准确地说是注册信号处理程序)有什么区别?我知道如果我们在 main() 中注册,它也会处理线程,因为线程共享 main() 代码。
  • 那么如果我们在线程函数中注册处理程序,它是否只特定于该线程?
  • @kingsmahser1:不,没有任何迹象表明signal 的存储是或应该是线程本地的。您应该假设注册信号处理程序具有相同的效果,无论当前线程是什么。
  • 正如 Sam 所说,信号处理程序是进程全局。您不能安装每个线程的信号处理程序。您可以尝试聪明一点并安装一个信号处理程序,该处理程序以pthread_getspecific 作为信号的线程特定处理程序读取函数指针,但pthread_getspecific 不是异步信号安全的。据我所知,没有异步信号安全的方法来确定您所在的线程,因此向特定线程传递信号的唯一要点是生成EINTR(如果SA_RESTART 从@987654328 中省略@flags) 或停止该线程的进度...
【解决方案3】:

我认为问题的核心是信号作为一个整体传递给进程,而不是单个线程。通常,指定一个线程来处理所有信号;所有其他线程(包括主线程)都需要block the signals using pthread_sigmask()

您可以将掩码设置为阻止所有信号,启动信号处理线程,取消掩码您希望处理的信号,然后返回主线程,启动您需要的所有其他线程。它们将从主线程继承“阻塞所有信号”掩码。

顺便说一句,是时候离开signal(3) 并切换到sigaction(2),它具有可靠的语义和更好的标准化。 (因此更便携。)

【讨论】:

  • @sarnold:你说:“相信问题的核心是信号作为一个整体传递给进程,而不是单个线程”但是如果我使用 pthread_kill(thread_id,signal_no) 信号我认为是交付给他的特定线程。
  • @Kingsmasher1,我不确定这是不是真的 :) 但@Sam Hocevar 的回答看起来很棒。我很想删除我的答案,但它可能还有用。
  • @sarnold:信号作为一个整体传递给进程,但是根据pthread_kill()文档,它保证了如果信号不是SIGSTOP,@ 987654327@或SIGTERM
  • @Sam Hocevar,所有声称来自 POSIX 联机帮助页的文档都很棒,但我不相信他们会报告 Linux 线程 API 的现实。 :( 如果你有来自 NPTL 的人的文档也说同样的话,那真的会让我很开心。:)
  • NPTL 声称符合 POSIX,并且在这方面做得相当好。我没有发现任何明显的不一致问题,除了可能与内核坚持拥有单独的每个线程真实/有效/保存/fs uid/gid 和组并强制用户空间同步更改它们有关的一些事情。 (尽管 POSIX 声明组是进程而不是线程的属性,NPTL 不会同步对补充组的更改。)
猜你喜欢
  • 1970-01-01
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-23
相关资源
最近更新 更多