我有一个使用sem_wait
的程序。 Posix specification说:
sem_wait()
功能可通过信号传送中断。
此外,在有关错误的部分中说:
[[EINTR]-信号中断了此功能。
但是,在我的程序中,发送信号并不会解除阻止呼叫(并按规范所示返回-1
。
下面是一个最小的示例。发送信号后,该程序会挂起并且sem_wait
永不解锁。
#include <semaphore.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
sem_t sem;
void sighandler(int sig) {
printf("Inside sighandler\n");
}
void *thread_listen(void *arg) {
signal(SIGUSR1, &sighandler);
printf("sem_wait = %d\n", sem_wait(&sem));
return NULL;
}
int main(void) {
pthread_t thread;
sem_init(&sem, 0, 0);
pthread_create(&thread, NULL, &thread_listen, NULL);
sleep(1);
raise(SIGUSR1);
pthread_join(thread, NULL);
return 0;
}
程序输出Inside sighandler
然后挂起。
关于此还有另一个问题here,但实际上并没有提供任何清晰性。
我是否误解了规格说明?仅供参考,我的计算机使用Ubuntu GLIBC 2.31-0ubuntu9。
此程序有三个错误,只有两个是可修复的。
正如David Schwartz的回答所指出的,在多线程程序中,raise
向调用raise
的线程发送信号。
要获取发送到所需线程的信号,在此测试程序中
,将raise(SIGUSR1)
更改为pthread_kill(thread, SIGUSR1)
。但是,如果要让特定线程在发送到整个过程时处理SIGUSR1
,则需要做的是在所有线程中使用pthread_sigmask
阻塞SIGUSR1
except应该处理的那个。为了使该信号可靠地工作,应从一开始就阻塞除同步CPU异常(SIGABRT
,SIGBUS
,SIGFPE
,SIGILL
,SIGSEGV
,SIGSYS
和SIGTRAP
)之外的所有信号main
中的SIGHUP
,SIGINT
,[C0 ],SIGPWR
,SIGQUIT
,SIGTERM
,SIGTSTP
,SIGXCPU
)。在使用glibc的系统上,SIGXFSZ
安装一个信号处理程序,该信号处理程序not
signal
并将sigaction
设置为没有包含sa_flags
的值。例如,注意:信号处理程序是全局进程的,因此,在哪个线程上调用SA_RESTART
注意:保证
struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_handler = sighandler; sa.sa_flags = 0; sigaction(SIGUSR1, &sa, 0);
与memset(&sa, 0, sizeof sa)
具有相同的效果。
sigemptyset(&sa.sa_mask)
都没有关系。在几乎所有情况下,多线程程序都应在创建任何线程之前执行其在sigaction
中的所有sigaction
调用,只是为了确保信号处理程序在任何信号发生之前都处于活动状态。之前
,线程有机会调用main
。如果发生这种情况,将调用信号处理程序并返回,然后将调用sem_wait
,它将永远阻塞。在此测试程序中,可以通过增加sem_wait
中sleep
的长度来使这种情况不太可能发生,但是没有办法使其变为不可能main
,sigsuspend
和sigsuspend
。这些是only
系统调用,可以避免这种竞争情况。对于必须处理信号的多线程程序的最佳实践是让一个线程devoted进行信号处理。如上所述,主程序会阻塞除同步CPU异常以外的所有信号;然后,它创建信号处理线程,该线程循环调用sigwaitinfo
以获取适当的信号集,并使用管道或条件变量或任何真正的信号将消息分发到其余线程。除了sigwaitinfo
之外,此线程绝不能阻止任何系统调用。 (您仍然需要为所有信号设置信号处理程序,但实际上它们永远不会被调用,因此它们可以成为无功能的函数;它们存在的唯一原因是由于默认操作而导致内核无法杀死整个进程等于pselect
或其他。)在该测试程序的假设情况下,信号处理线程将通过调用pselect
来响应sigwaitinfo
,这将唤醒侦听器线程,或者将导致侦听器线程不会在[ C0]放在首位。
在多线程程序中,sigwaitinfo
向调用SIGUSR1
的线程发送信号。您需要使用SIGUSR1
或sem_post(&sem)
。
在使用glibc的系统上,signal
安装一个信号处理程序,该信号处理程序执行not中断阻止系统调用。要获得能够执行此操作的信号处理程序,您需要使用sigaction
并将sa_flags
设置为没有包含SA_RESTART
的值。
((大卫·施瓦茨的答案也是正确的。)