Библиотека сайта rus-linux.net
| Цилюрик О.И. 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
| Предыдущий раздел: | Оглавление | Следующий раздел: |
| Данные потока | Расширенные операции ввода-вывода |
