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 线程与信号机制,编写安全的异步信号处理函数,在指定的线程中以同步的方式处理异步信号。