如果我为SIGABRT设置和信号处理程序,同时我有一个等待sigwait()的线程来接收SIGABRT(我通过pthread_sigmask在其他线程中阻塞了SIGABRT)。
那么首先处理哪一个?信号处理程序或sigwait()?
[我正面临一些问题,即sigwait()永远被阻止。我正在调试它]
main()
{
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigprocmask(SIG_BLOCK, &signal_set, NULL);
// Dont deliver SIGABORT while running this thread and it's kids.
pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
pthread_create(&tAbortWaitThread, NULL, WaitForAbortThread, NULL);
..
Create all other threads
...
}
static void* WaitForAbortThread(void* v)
{
sigset_t signal_set;
int stat;
int sig;
sigfillset( &signal_set);
pthread_sigmask( SIG_BLOCK, &signal_set, NULL ); // Dont want any signals
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT); // Add only SIGABRT
// This thread while executing , will handle the SIGABORT signal via signal handler.
pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
stat= sigwait( &signal_set, &sig ); // lets wait for signal handled in CatchAbort().
while (stat == -1)
{
stat= sigwait( &signal_set, &sig );
}
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
// Abort signal handler executed via sigaction().
static void CatchAbort(int i, siginfo_t* info, void* v)
{
sleep(20); // Dont return , hold on till the other threads are down.
}
在sigwait(),我会知道已收到SIGABRT。我将告诉其他线程。然后将保持中止信号处理程序,以便不终止进程。
我想知道sigwait()和信号处理程序的交互。
答案 0 :(得分:4)
来自sigwait()
文档:
sigwait()函数暂停执行调用线程,直到 信号集中指定的信号之一变为待定状态。
待处理信号表示等待传递给线程/进程之一的阻塞信号。因此,您需要不来解锁信号,就像使用pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)
电话一样。
这应该有效:
static void* WaitForAbortThread(void* v){
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigwait( &signal_set, &sig );
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
答案 1 :(得分:2)
我从此< link>
获得了一些信息它说:
为了允许线程等待异步生成的信号,线程库提供了sigwait子例程。 sigwait子例程阻塞调用线程,直到其中一个等待信号被发送到进程或线程。 使用sigwait子例程,等待信号上不得安装信号处理程序。
我将删除sigaction()处理程序并仅尝试sigwait()。
答案 2 :(得分:1)
从您发布的代码段中,您似乎错误地使用了sigwait()
。 AFAIU,您需要WaitForAbortThread
,如下所示:
sigemptyset( &signal_set); // change it from sigfillset()
for (;;) {
stat = sigwait(&signal_set, &sig);
if (sig == SIGABRT) {
printf("here's sigbart.. do whatever you want.\n");
pthread_kill(tid, signal); // thread id and signal
}
}
我认为不需要pthread_sigmask()
。既然你只想处理SIGABRT,首先将init signal_set设为空,然后简单地添加SIGABRT
,然后跳转到无限循环,sigwait
将等待你正在寻找的特定信号,你检查如果它是SIGABRT则发出信号,如果是 - 做你想做的任何事情。注意使用pthread_kill()
,使用它将任何信号发送到通过tid指定的其他线程和您要发送的信号,确保您知道要发送信号的其他线程的tid。希望这会有所帮助!
答案 3 :(得分:1)
我知道这个问题大约有一年了,但我经常使用一种模式,它使用pthreads和信号来解决这个问题。它有点长,但会处理我所知道的任何问题。
我最近与一个用SWIG包装的库结合使用,并在Python中调用。一个恼人的问题是我的IRQ线程使用sigwait等待SIGINT从未收到SIGINT信号。当从Matlab调用时,相同的库工作得很好,它没有捕获SIGINT信号。
解决方案是安装信号处理程序
#define _NTHREADS 8
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
#include <linux/unistd.h>
#include <sys/signal.h>
#include <sys/syscall.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h> // strerror
#define CallErr(fun, arg) { if ((fun arg)<0) \
FailErr(#fun) }
#define CallErrExit(fun, arg, ret) { if ((fun arg)<0) \
FailErrExit(#fun,ret) }
#define FailErrExit(msg,ret) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr); \
return ret; }
#define FailErr(msg) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr);}
typedef struct thread_arg {
int cpu_id;
int thread_id;
} thread_arg_t;
static jmp_buf jmp_env;
static struct sigaction act;
static struct sigaction oact;
size_t exitnow = 0;
pthread_mutex_t exit_mutex;
pthread_attr_t attr;
pthread_t pids[_NTHREADS];
pid_t tids[_NTHREADS+1];
static volatile int status[_NTHREADS]; // 0: suspended, 1: interrupted, 2: success
sigset_t mask;
static pid_t gettid( void );
static void *thread_function(void *arg);
static void signalHandler(int);
int main() {
cpu_set_t cpuset;
int nproc;
int i;
thread_arg_t thread_args[_NTHREADS];
int id;
CPU_ZERO( &cpuset );
CallErr(sched_getaffinity,
(gettid(), sizeof( cpu_set_t ), &cpuset));
nproc = CPU_COUNT(&cpuset);
for (i=0 ; i < _NTHREADS ; i++) {
thread_args[i].cpu_id = i % nproc;
thread_args[i].thread_id = i;
status[i] = 0;
}
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_mutex_init(&exit_mutex, NULL);
// We pray for no locks on buffers and setbuf will work, if not we
// need to use filelock() on on FILE* access, tricky
setbuf(stdout, NULL);
setbuf(stderr, NULL);
act.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT;
act.sa_handler = signalHandler;
sigemptyset(&act.sa_mask);
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
if (setjmp(jmp_env)) {
if (gettid()==tids[0]) {
// Main Thread
printf("main thread: waiting for clients to terminate\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
if (status[i] == 1)
printf("thread %d: terminated\n",i+1);
}
// On linux this can be done immediate after creation
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
else {
// Should never happen
printf("worker thread received signal");
}
return -1;
}
// Install handler
CallErr(sigaction, (SIGINT, &act, &oact));
// Block SIGINT
CallErr(pthread_sigmask, (SIG_BLOCK, &mask, NULL));
tids[0] = gettid();
srand ( time(NULL) );
for (i = 0; i < _NTHREADS; i++) {
// Inherits main threads signal handler, they are blocking
CallErr(pthread_create,
(&pids[i], &attr, thread_function,
(void *)&thread_args[i]));
}
if (pthread_sigmask(SIG_UNBLOCK, &mask, NULL)) {
fprintf(stderr, "main thread: can't block SIGINT");
}
printf("Infinite loop started - CTRL-C to exit\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
//printf("%d\n",status[i]);
if (status[i] == 2)
printf("thread %d: finished succesfully\n",i+1);
}
// Clean up and exit
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
static void signalHandler(int sig) {
int i;
pthread_t id;
id = pthread_self();
for (i = 0; i < _NTHREADS; i++)
if (pids[i] == id) {
// Exits if worker thread
printf("Worker thread caught signal");
break;
}
if (sig==2) {
sigaction(SIGINT, &oact, &act);
}
pthread_mutex_lock(&exit_mutex);
if (!exitnow)
exitnow = 1;
pthread_mutex_unlock(&exit_mutex);
longjmp(jmp_env, 1);
}
void *thread_function(void *arg) {
cpu_set_t set;
thread_arg_t* threadarg;
int thread_id;
threadarg = (thread_arg_t*) arg;
thread_id = threadarg->thread_id+1;
tids[thread_id] = gettid();
CPU_ZERO( &set );
CPU_SET( threadarg->cpu_id, &set );
CallErrExit(sched_setaffinity, (gettid(), sizeof(cpu_set_t), &set ),
NULL);
int k = 8;
// While loop waiting for exit condition
while (k>0) {
sleep(rand() % 3);
pthread_mutex_lock(&exit_mutex);
if (exitnow) {
status[threadarg->thread_id] = 1;
pthread_mutex_unlock(&exit_mutex);
pthread_exit(NULL);
}
pthread_mutex_unlock(&exit_mutex);
k--;
}
status[threadarg->thread_id] = 2;
pthread_exit(NULL);
}
static pid_t gettid( void ) {
pid_t pid;
CallErr(pid = syscall, (__NR_gettid));
return pid;
}
答案 4 :(得分:0)
我进行了服务器测试,结果是:
对于所有测试用例,我通过在主线程中调用sigaction
来注册信号处理程序。
主线程阻塞目标信号,线程A通过调用pthread_sigmask
解除阻塞目标信号,线程A睡眠,发送目标信号。
结果:信号处理程序在线程A中执行。
主线程阻塞目标信号,线程A通过调用pthread_sigmask
解除阻塞目标信号,线程A调用sigwait
,发送目标信号。
结果:sigwait
被执行。
主线程不阻止目标信号,线程A不阻止目标信号,线程A调用sigwait
,发送目标信号。
结果:选择主线程,并在主线程中执行注册的信号处理程序。
如您所见,组合1和2很容易理解和得出结论。
是:
如果信号被线程阻塞,那么sigaction
注册的进程范围的信号处理程序将无法捕获甚至不知道。
如果没有阻塞信号,并且在调用sigwait
之前发送了信号,则进程范围的信号处理程序将获胜。这就是为什么 APUE 这些书要求我们在调用sigwait
之前阻塞目标信号的原因。在这里,我在线程A中使用sleep
来模拟较长的“窗口时间”。
如果没有阻塞信号,并且在sigwait
已经等待时发送,则sigwait
获胜。
但是您应该注意到,对于测试用例1和2,主线程被设计为阻塞目标信号。
最后对于测试用例3,当主线程未阻塞目标信号,并且线程A中的sigwait
也正在等待时,信号处理程序将在主线程中执行。
我相信测试用例3的行为是 APUE 所谈论的:
来自 APUE §12.8:
如果捕获到信号(进程已建立信号 例如,使用
sigaction
处理程序),并且线程正在等待 调用sigwait
时使用相同的信号,直到 实施以决定传递信号的方式。的 实现可以允许sigwait
返回或调用 信号处理程序,但不能同时使用。
首先,如果要完成一个线程<->一个信号模型,则应:
pthread_sigmask
阻止主线程中的所有信号(在主线程中创建的后续线程继承了信号掩码)sigwait(target_signal)
。测试代码
#define _POSIX_C_SOURCE 200809L
#include <signal.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
FILE* file;
void* threadA(void* argv){
fprintf(file, "%ld\n", pthread_self());
sigset_t m;
sigemptyset(&m);
sigaddset(&m, SIGUSR1);
int signo;
int err;
// sigset_t q;
// sigemptyset(&q);
// pthread_sigmask(SIG_SETMASK, &q, NULL);
// sleep(50);
fprintf(file, "1\n");
err = sigwait(&m, &signo);
if (err != 0){
fprintf(file, "sigwait error\n");
exit(1);
}
switch (signo)
{
case SIGUSR1:
fprintf(file, "SIGUSR1 received\n");
break;
default:
fprintf(file, "?\n");
break;
}
fprintf(file, "2\n");
}
void hello(int signo){
fprintf(file, "%ld\n", pthread_self());
fprintf(file, "hello\n");
}
int main(){
file = fopen("daemon", "wb");
setbuf(file, NULL);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = hello;
sigaction(SIGUSR1, &sa, NULL);
sigset_t n;
sigemptyset(&n);
sigaddset(&n, SIGUSR1);
// pthread_sigmask(SIG_BLOCK, &n, NULL);
pthread_t pid;
int err;
err = pthread_create(&pid, NULL, threadA, NULL);
if(err != 0){
fprintf(file, "create thread error\n");
exit(1);
}
pause();
fprintf(file, "after pause\n");
fclose(file);
return 0;
}
运行./a.out &
(在后台运行),然后使用kill -SIGUSR1 pid
进行测试。请勿使用raise
。 raise
,sleep
,pause
是线程级的。