Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Linux-инструменты для Windows-программистов
Назад Библиотеки API POSIX Вперед

Сигналы в потоках

Сигналы не могут направляться отдельным потокам процесса — сигналы направляются процессу в целом, как оболочке, обрамляющей несколько потоков. Точно так же, для каждого сигнала может быть переопределена функция-обработчик, но это переопределение действует глобально в рамках процесса.

Тем не менее, каждый из потоков (в том числе и главный поток процесса main() {...}) могут независимо определить (в терминах модели надёжной обработки сигналов, сигнальных наборов) собственную маску реакции на сигналы. Таким образом оказывается возможным: а). распределить потоки, ответственные за обработку каждого сигнала, б). динамически изменять потоки, в которых (в контексте которых) обрабатывается реакция на сигнал и в). создавать обработчики сигналов в виде отдельных потоков, специально для того предназначенных.

Ниже показан многопоточный пример (3 потока сверх главного), в котором направляемая извне (из другой консоли) последовательность повторяемого сигнала поочерёдно обрабатывается каждым из дочерних потоков по 1-му разу, после чего реакция на сигнал блокируется:

s6.cc :

#include <iostream> 
#include <iomanip> 
#include <stdio.h> 
#include <signal.h> 
#include <unistd.h> 
#include <pthread.h> 
#include <time.h> 
using namespace std; 
static void handler( int signo, siginfo_t* info, void* context ) { 
   cout << "sig=" << signo << "; tid=" << pthread_self() << endl; 
}; 
sigset_t sig;
void* threadfunc ( void* data ) { 
   sigprocmask( SIG_UNBLOCK, &sig, NULL ); 
   while( true ) { 
      pause();
      sigprocmask( SIG_BLOCK, &sig, NULL ); 
   } 
   return NULL; 
}; 
int main() { 
   const int thrnum = 3; 
   sigemptyset( &sig ); 
   sigaddset( &sig, SIGRTMIN ); 
   sigprocmask( SIG_BLOCK, &sig, NULL ); 
   cout << "main + " << thrnum << " threads : waiting fot signal " << SIGRTMIN 
        << "; pid=" << getpid() << "; tid(main)=" << pthread_self() << endl; 
   struct sigaction act; 
   act.sa_mask = sig; 
   act.sa_sigaction = handler; 
   act.sa_flags = SA_SIGINFO; 
    if( sigaction( SIGRTMIN, &act, NULL ) < 0 ) perror( "set signal handler: " ); 
   pthread_t pthr; 
   for( int i = 0; i < thrnum; i++ ) 
      pthread_create( &pthr, NULL, threadfunc, NULL ); 
   pause(); 
}; 

Вот как происходит выполнение этого процесса:

$ ./s6 

main + 3 threads : waiting fot signal 34; pid=7455; tid(main)=3078510288 
sig=34; tid=3078503280 
sig=34; tid=3068013424 
sig=34; tid=3057523568 
^C 

$ kill -34 7455 
$ kill -34 7455 
$ kill -34 7455 
$ kill -34 7455 
$ kill -34 7455 

Хорошо видно, что:

- главный поток процесса не реагирует на получаемые извне сигналы, его реакция изначально заблокирована;

- tid потока, который принимает каждый последующий сигнал, отличается от предыдущего;

- после 3-х реакций, обслуженных в каждом из 3-х потоков, реагирование на этот сигнал прекращается (блокируется).

Пользуясь гибкостью API расширения реального времени POSIX 1003.b, можно построить реакцию на получаемые сигналы в отдельных обрабатывающих потоках, вообще без обработчиков сигналов (со своими ограничениями на операции в контексте сигнального обработчика). В следующем примере процесс приостанавливается (или мог бы выполнять другую полезную работу) до тех пор, пока поток обработчика сигналов не сообщит о завершении.

sigthr.c :

#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <signal.h> 
#include <pthread.h> 
int quitflag = 0; 
sigset_t mask; 
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t  wait = PTHREAD_COND_INITIALIZER; 
void* threadfunc ( void* data ) { 
   int signo;
   while( 1 ) { 
      if( sigwait( &mask, &signo ) != 0 ) 
         perror( "sigwait:" ), exit( EXIT_FAILURE ); 
      switch( signo ) { 
         case SIGINT: 
            printf( "  ... signal SIGINT\n" ); 
            break;
         case SIGQUIT: 
            printf( "  ... signal SIGQUIT\n" ); 
            pthread_mutex_lock( &lock ); 
            quitflag = 1; 
            pthread_mutex_unlock( &lock ); 
            pthread_cond_signal( &wait ); 
            return NULL; 
         default:
            printf( "undefined signal %d\n", signo ), exit( EXIT_FAILURE ); 
      } 
   }; 
}; 
int main() { 
   printf( "process started with PID=%d\n", getpid() ); 
   sigemptyset( &mask ); 
   sigaddset( &mask, SIGINT ); 
   sigaddset( &mask, SIGQUIT ); 
   sigset_t oldmask; 
   if( sigprocmask( SIG_BLOCK, &mask, &oldmask ) < 0 ) 
      perror( "signals block:" ), exit( EXIT_FAILURE ); 
   pthread_t tid; 
   if( pthread_create( &tid, NULL, threadfunc, NULL ) != 0 ) 
      perror( "thread create:" ), exit( EXIT_FAILURE ); ; 
   pthread_mutex_lock( &lock ); 
   while( 0 == quitflag ) 
      pthread_cond_wait( &wait, &lock ); 
   pthread_mutex_unlock( &lock ); 
   /* SIGQUIT был перехвачен, но к этому моменту снова заблокирован */ 
   if( sigprocmask( SIG_SETMASK, &oldmask, NULL ) < 0 ) 
      perror( "signals set:" ), exit( EXIT_FAILURE ); 
   return EXIT_SUCCESS; 
}; 

Примечание: Изменения флага quitflag производится под защитой мьютекса lock, чтобы главный поток не мог пропустить изменение значения флага.

Выполнение задачи:

$ ./sigthr

^C  ... signal SIGINT 
^C  ... signal SIGINT 
^C  ... signal SIGINT 
^\  ... signal SIGQUIT

Предыдущий раздел: Оглавление Следующий раздел:
Данные потока   Расширенные операции ввода-вывода