簡體   English   中英

sigwait()和信號處理程序

[英]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來注冊信號處理程序。

  1. 主線程阻塞目標信號,線程A通過調用pthread_sigmask解鎖目標信號,線程A休眠,發送目標信號。

    結果:信號處理程序在線程A中執行。

  2. 主線程塊目標信號,線程A通過調用pthread_sigmask解除阻塞目標信號,線程A調用sigwait ,發送目標信號。

    結果: sigwait被執行。

  3. 主線程不阻塞目標信號,線程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返回或調用信號處理程序,但不能同時返回兩者。


最重要的是,如果你想完成一個線程< - >一個信號模型,你應該:

  1. 使用pthread_sigmask阻塞主線程中的所有信號(在主線程中創建的后續線程繼承信號掩碼)
  2. 創建線程並使用目標信號調用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 raisesleeppause都是線程范圍的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM