我不知道POSIX是否会涉及到这个话题,但我没有进行全面的搜索。
通过使用gcc / nptl系统进行简短的实验,显示出来就像我所怀疑的那样,NPTL中没有此类保护 - 取消处理程序确实会从信号处理程序上下文中调用。
下面的程序(对于hackiness等方面的道歉)将显示以下输出:
Signal handler called
Sent cancellation
Cleanup called
In sighandler
这段代码表明:
- 信号处理程序被调用
- 另一个线程接着调用了
pthread_cancel()
- 取消处理程序随后被调用,但信号处理程序没有完成
以下是程序代码:
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
pthread_t mainthread;
int in_sighandler = 0;
void
cleanup (void *arg)
{
write(1, "Cleanup called\n", strlen("Cleanup called\n"));
if (in_sighandler) {
write(1, "In sighandler\n", strlen("In sighandler\n"));
} else {
write(1, "Not in sighandler\n", strlen("In sighandler\n"));
}
}
void
sighandler (int sig, siginfo_t *siginfo, void *arg)
{
in_sighandler = 1;
write(1,"Signal handler called\n", strlen("Signal handler called\n"));
usleep(3000000);
write(1, "Signal handler exit\n", strlen("Signal handler exit\n"));
in_sighandler = 0;
}
void *
thread (void *arg)
{
sleep(1);
pthread_kill(mainthread, SIGUSR1);
usleep(500000);
pthread_cancel(mainthread);
printf("Sent cancellation\n");
return (NULL);
}
int
main (int argc, char **argv)
{
int rc;
struct sigaction sa;
pthread_t threadid;
mainthread = pthread_self();
sa.sa_sigaction = &sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
rc = sigaction(SIGUSR1, &sa, NULL);
assert(rc == 0);
rc = pthread_create(&threadid, NULL, &thread, NULL);
assert(rc == 0);
pthread_cleanup_push(&cleanup, NULL);
while (1) {
sleep(60);
}
pthread_cleanup_pop(0);
return (0);
}
pthread_sigmask
阻塞信号不允许阻塞取消操作。如果取消操作是通过信号实现的,则必须对应用程序透明,并且在实践中(至少在 glibc/nptl 上),pthread_sigmask
库函数(以及sigprocmask
函数在线程化应用程序中根本不需要安全)会默默地拒绝掩码SIGCANCEL
和SIGSETXID
(两者都由实现内部使用)。 - R.. GitHub STOP HELPING ICEpthread_sigmask
来阻止所有信号,在调用pthread_create
之前,并且必须在那时和返回给调用者之间的某个时间恢复原始信号掩码。如果使用取消,还应该在调用可能不支持取消的外部库代码时显式禁用取消。 - R.. GitHub STOP HELPING ICE