[英]sigwait() and signal handler
如果我為SIGABRT設置和信號處理程序,同時我有一個線程在SIGBRT上等待sigwait()(我通過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()和信號處理程序的交互。
來自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;
}
我從這個< link >獲得了一些信息
它說 :
為了允許線程等待異步生成的信號,線程庫提供了sigwait子例程。 sigwait子例程阻塞調用線程,直到其中一個等待信號被發送到進程或線程。 使用sigwait子例程,不得在等待信號上安裝信號處理程序。
我將刪除sigaction()處理程序並嘗試只使用sigwait()。
從您發布的代碼段開始,您似乎使用了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_set設置為空,然后簡單地添加SIGABRT
,然后跳轉到無限循環, sigwait
將等待你正在尋找的特定信號,你檢查信號是否為SIGABRT,如果是 -做你想做的。 注意使用pthread_kill()
,使用它將任何信號發送到通過tid指定的其他線程和你想發送的信號,確保你知道你想要發送信號的其他線程的tid。 希望這會有所幫助!
我知道這個問題大約有一年了,但我經常使用一種模式,它使用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;
}
我進行了幾項測試,結果和結果如下:
對於所有測試用例,我通過在主線程中調用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
都是線程范圍的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.