Литмир - Электронная Библиотека
Содержание  
A
A

# nice -n-19 p6s -n10000 -q

2854963 -> 2863156 : cycles - 57194634; on signal - 2859

2863156 -> 2854963 : cycles - 57199831; on signal - 2859

# nice -n-19 p6s -n1000000 -q

2871347 -> 2879540 : cycles - 571543013; on signal - 2857

2879540 -> 2871347 : cycles - 571550847; on signal - 2857

# nice -n-19 p6s -n1000000 -q

2887731 -> 2895924 : cycles - 5715903548; on signal - 2857

2895924 -> 2887731 : cycles - 5715908318; on signal - 2857

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

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

Модель реакций на сигналы многопоточных приложений не проработана до конца в рамках POSIX и находится на стадии предварительных предложений. Тем не менее в системах с развитой многопоточностью (а QNX — именно такая система) эта сторона вопроса не может игнорироваться, и не только потому, что потоки в комбинации с сигналами могут создавать мощные конструктивные элементы программ, а еще и потому, что непроизвольные разблокирующие или завершающие операции, инициируемые сигналами, могут породить очень серьезные проблемы в случае многопоточности (мы еще будем возвращаться к этим вопросам по тексту). А раз так, то в этих случаях система должна обязательно предлагать некоторую модель функционирования (удачную или не очень).

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

The original POSIX specification defined signal operation on processes only. In a multi-threaded process, the following rules are followed:

*The signal actions are maintained at the process level. If a thread ignores or catches a signal, it affects all threads within the process.

*The signal mask is maintained at the thread level. If a thread blocks a signal, it affects only that thread.

*An un-ignored signal targeted at a thread will be delivered to that thread alone.

*An un-ignored signal targeted at a process is delivered to the first thread that doesn't have the signal blocked. If all threads have the signal blocked, the signal will be queued on the process until any thread ignores or unblocks the signal. If ignored, the signal on the process will be removed. If unblocked, the signal will be moved from the process to the thread that unblocked it.

Все достаточно однозначно: обработчики сигналов определяются на уровне процесса, а вот сигнальные маски, определяющие, реагировать ли на данный сигнал, - на уровне каждого из потоков.

Для манипулирования сигнальными масками на уровне потоков нам придется использовать функцию

SignalProcmask()
[34](естественно, из состава native API, поскольку эта модель не декларирована POSIX):

#include <sys/neutrino.h>

int SignalProcmask(pid_t pid, int tid, int how, const sigset_t* set,

 sigset_t* oldset);

Видна прямая аналогия с рассматривавшейся ранее функцией

sigprocmask()
. Да это и неудивительно, поскольку
sigprocmask()
является POSIX-«оберткой» к
SignalProcmask()
. Только рассматриваемый вызов имеет два «лишних» начальных параметра: PID и TID потока, к маске которого применяется действие. Если
pid
— 0, то предполагается текущий процесс, если tid = 0, то
pid
игнорируется и предполагается текущий поток, вызывающий функцию.

Остальные параметры соответствуют параметрам

sigprocmask()
(дополнительно появляется возможное значение
SIG_PENDING
для
how
).

Рассмотрим, как это работает на примере простейшего кода ( файл s6.cc):

Сигналы, обрабатываемые в потоках

#include <stdio.h>

#include <iostream.h>

#include <signal.h>

#include <unistd.h>

#include <pthread.h>

#include <time.h>

#include <sys/neutrino.h>

static void handler(int signo, siginfo_t* info, void* context) {

 cout << "SIG = " << signo << ";

 TID = " << pthread_self() << endl;

}

sigset_t sig;

void* threadfunc(void* data) {

 SignalProcmask(0, 0, SIG_UNBLOCK, &sig, NULL);

 while (true) pause();

}

int main() {

 sigemptyset(&sig);

 sigaddset(&sig, SIGRTMIN);

 sigprocmask(SIG_BLOCK, &sig, NULL);

 cout << "Process " << getpid() << ", waiting for signal " <<

  SIGRTMIN << 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: ");

 const int thrnum = 3;

 for (int i = 0; i < thrnum; i++)

  pthread_create(NULL, NULL, threadfunc, NULL);

 pause();

 exit(EXIT_SUCCESS);

}

Для анализа этого и последующих фрагментов нам будет недостаточно команды

kill
, поэтому сделаем простейший «передатчик» плотной (в смысле минимального интервала следования) последовательности повторяющихся сигналов ( файл k6.cc). Выполнение этого тестера, например по команде:

вернуться

34

Функция

SignalProcmask()
имеет свой реентерабельный (безопасный в потоках) эквивалент:
SignalProcmask_r()
.

49
{"b":"155449","o":1}