где
nd
— дескриптор сетевого узла QNET, на котором будут разыскиваться
pid
и
tid
. Для посылки сигнала локальному процессу (потоку) можно для
nd
указать 0, но лучше — определенную системой константу
ND_LOCAL_NODE
.
Дескриптор узла в сети QNET — понятие относительное; он может быть получен, например, вызовом
netmgr_strtond()
. Но и здесь не все так просто:
• Дескриптор, соответствующий, скажем, узлу «host», полученный на узле «А», может иметь значение N, но дескриптор того же узла, полученный на узле «В», будет иметь уже значение M, то есть дескриптор узла — это «дескриптор сетевого узла X, как он видится с сетевого узла Y».
• Тот же дескриптор узла «host» может быть определен как имеющий значение N, но уже через несколько секунд он может «сменить» свое значение на M, то есть значения, полученные
netmgr_strtond()
, должны использоваться немедленно...
Эти и другие сложности относятся к особенностям программного использования QNET и требуют отдельного обстоятельного обсуждения. Однако они не являются предметом нашего текущего рассмотрения.
pid
— PID процесса, которому направляется сигнал,
pid
может иметь и отрицательное значение, при этом положительное значение (
-pid
) идентифицирует группу процессов EGID, и сигнал будет отправлен всем процессам группы. При нулевом значении
pid
сигнал будет отправлен всем процессам группы процесса отправителя.
tid
— 0 или TID потока, которому направляется сигнал. При указании
tid
сигнал будет доставляться только указанному потоку, а при
tid
= 0 — всем потокам процесса. Дальнейшая судьба сигнала в обоих случаях зависит от маскирования сигнала в потоке, как мы рассматривали ранее.
signo
— номер сигнала (с ним неоднократно встречались выше).
code
и
value
— код и значение, ассоциированные с сигналом (их мы тоже встречали при рассмотрении модели сигналов реального времени).
Как и обычно, внешнее различие (для программиста) основной формы
SignalKill()
и формы, безопасной в многопоточной среде,
SignalKill_r()
состоит в том, что:
•
SignalKill()
возвращает -1 в случае ошибки, а код ошибки заносится в
errno
; любой другой возврат является индикатором успешного выполнения;
•
SignalKill_r()
возвращает
EOK
в случае успеха, а в случае ошибки возвращается отрицательный код ошибки (тот же, который основная форма заносит в
errno
, но со знаком минус).
Возможны следующие коды ошибок, возвращаемые этими вызовами:
EINVAL
— недопустимое числовое значение
signo
;
ESRCH
— несуществующий адресат (
pid
или
tid
);
EPERM
— процесс не имеет достаточных прав для посылки сигнала;
EAGAIN
— недостаточно ресурсов ядра для выполнения запроса.
Для того чтобы получить работающий пример использования этой возможности, возьмите любой из приводившихся выше примеров, разнесите процессы по сетевым узлам и определите «целеуказание» в процессе-отправителе.
Простейшим примером и демонстрацией удаленной реакции в сети может быть следующая последовательность действий:
• Производим запуск задачи на удаленномузле, например:
# on -f
• После чего, выполнив ряд операций в запущенной программе, прекращаем ее работу по [Ctrl+C] с локального терминала.
Интересно оценить далеко идущие последствия этого «маленького» расширения стандартной POSIX-схемы работы с сигналами:
• На технике «сетевых сигналов» может быть построена целая система уведомлений сетевых составляющих компонент единой программной прикладной системы.
• Именно «уведомлений» (но не синхронизации с наследованием приоритетов, влияющей на общую систему диспетчеризации составляющих частей и т.п.): посылка сигнала является неблокирующей операцией (не требует ответа), а прием сигнала не сопровождается наследованием (или любым изменением) приоритетов.
• Такое «сигнальное» взаимодействие, записанное в формальной POSIX-семантике (но, по сути, осуществляющее механизмы, далеко выходящие за POSIX), может оказаться гораздо проще в записи и понимании, чем при использовании низкоуровневых механизмов обмена сообщениями (пульсами).
4. Примитивы синхронизации