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