Linux 多线程应用:信号产生,信号处理


1 问题讨论背景

笔者有一种应用场景,A 线程从 socket 接收指令,根据指令在 B 线程进行相应工作。采用信号机制,设置 SIGUSR1 的信号处理函数 sighandler,A 线程解析指令后发射信号 SIGUSR1,B 线程执行sighandler. 因对 POSIX 信号机制不熟,过程中产生了一些误区,总结如下。

// A 线程
...
while(1)
{
    recv(sock_fd, buf, BUF_SIZE, 0);
    ...
    // 发射信号 SIGUSR1;
    raise(SIGUSR1);
    // pthread_kill(thread, SIGUSR1);
    // kill(pid, SIGUSR1);
}
...

// B 线程
...
void sighandler(int);

// 设置信号处理函数
signal(SIGUSR1, sighandler);
...
  • 尽量不要使用 raise 发射信号
    在单线程应用中,raise(sig)等效于kill(getpid(), sig),在多线程应用中等效于pthread_kill(pthread_self(), sig),如果信号有信号处理函数要调用,raise() 只有在信号处理函数返回后才能返回。于是,在上面的应用中,由于笔者在 sighandler 中做了复杂且耗时的操作(在信号的应用中,这应该是避免的),raise() 长时间阻塞,阻止了 socket 接收消息。

  • 信号处理函数位于哪个线程
    pthread_kill(pthread_self(), sig)发射信号,sighandler 也将在当前线程 A 执行,而不是线程 B 执行,即便信号处理函数是在线程 B 设置的,这就是raise(sig)被阻塞的原因,因为 sighandler 在当前线程执行。

  • 合理的操作
    pthread_kill(thread_B, SIGUSR1),这样 sighandler 将在线程 B 执行,pthread_kill() 立即返回,不会阻塞 socket 线程 A。

如前所述,以上提到的问题是由于在 sighandler 里做了复杂操作导致的,多线程信号编程中,信号处理函数应当尽量只做简单操作,关于具体的技术细节,可参考
https://www.ibm.com/developerworks/cn/linux/l-cn-signalsec/index.html
该文简单介绍了 Linux 线程与信号机制,编写安全的异步信号处理函数,在指定的线程中以同步的方式处理异步信号。