Если задуматься над вопросом, что в наши дни является ключевой компонентой в области передовых компьютерных технологий, то без сомнения можно было бы ответить, что такой технологией в наши дни является технология "клиент-сервер". Действительно, эта универсальная модель служит основой построения любых сколь угодно сложных систем, в том числе и сетевых. Пользуясь известным выражением, можно было бы сказать, что технология "клиент-сервер" является ключевым звеном в цепи, ухватившись за которое можно вытащить всю цепь. Разработчики СУБД, коммуникационных систем, систем электронной почты, банковских систем и т.д. во всем мире используют эту технологию. В этом смысле ОС UNIX является для разработчиков системного и прикладного программного обеспечения идеальным средством, потому что в своей основе наиболее полно отвечает требованиям технологии "клиент-сервер", далеко опережая при этом своих основных конкурентов. Для построения моделей типа "клиент-сервер" в UNIX существуют великолепные возможности:
сигналы; семафоры; программные каналы; именованные программные каналы; очереди сообщений; сегменты разделяемой памяти; специальные команды (write, cu, mail); средства межмашинного взаимодействия (uucp, tcp/ip, nfs, rfs).
В первоначальных версиях UNIX в момент ее зарождения этих средств либо вообще не существовало, либо они существовали в зачаточном состоянии. Не этим ли объясняется некий скептицизм в отношении UNIX со стороны программистов старшего поколения даже и в наши дни, что уж совсем необъяснимо, особенно после того, как System V, начиная с версии 3.2, явилась перед нами во всем богатстве своего могущественного арсенала. Рассмотрим эти средства более подробно.
Если рассматривать процесс как некую виртуальную машину, то в такой системе должна существовать система прерываний, отвечающая стандартным требованиям:
обработка исключительных ситуаций; средства обработки внешних и внутренних прерываний; средства управления системой прерываний (маскирование и демаскирование).
Всем этим требованиям в UNIX отвечает механика сигналов, которая может не только воспринимать и обрабатывать сигналы, но и порождать их и посылать на другие машины (процессы). Сигналы могут быть синхронными, когда инициатор сигнала - сам процесс, и асинхронными, когда инициатор возникновения сигнала - интерактивный пользователь за терминалом. Источником асинхронных сигналов может быть также ядро, когда оно контролирует определенные состояния аппаратуры рассматриваемые как ошибочные. Для удобства использования каждый сигнал закодирован и цифровое значение сигнала с использованием мнемонической формы записи хранится в файле signal.h
Нижеследующий перечень сигналов представлен в порядке возрастания значений сигналов (целое число в скобках).
ИМЯ СОДЕРЖАНИЕ РЕАКЦИЯ SIGHUP(1) Вырабатывается при отключении связи с Закончить процесс, терминалом. Рассылается всем процессам принявший сигнал. TGID SIGINT(2) При нажатии определенной клавиши. Закончить процесс Всем процессам TGID SIGQUIT(3) При нажатии клавиши quit. Всем Здесь и далее процессам TGID закончить процесс SIGILL(4) При возникновении аппаратных некон- тролируемых состояний. Текущему PID SIGTRAP(5) При трассировке. Текущему PID SIGIOT(6) При неисправности в аппаратуре. Текущему PID SIGTEMT(7) При выполнении команды EMT. Текущему PID SIGFPE(8) При обработке чисел с плавающей точкой. Текущему PID SIGKILL(9) При выдаче системного вызова kill. Адресуемому PID SIGBUS(10) При ошибке в косвенной адресации. Текущему PID SIGSEGV(11) При выходе за пределы сегмента. Текущему PID SIGSYS(12) При неверном системном вызове. Текущему PID SIGPIPE(13) При записи в Pipe, когда нет читающего процесса. Текущему PID записи в канал SIGALARM(14) При окончании временной установки. Посылается к PID установки SIGTERM(15) При выполнении команды kill в команд- ном режиме. К адресуемому PID SIGUSR1(16) При системном вызове kill. SIGUSR2(17) К адресуемому PID SIGCLD(18) При окончании "процесса-сына". Сброс сигнала К "процессу-отцу" SIGPWR(19) При уменьшении напряжения в сети. В зависимости от В зависимости от реализации реализации. SIGPOOL(22) При регистрации события в Stream- Закончить процесс. устройстве. К процессу, работающему с потоком. ---------------------------------------------------------------------------
Средством посылки и восприятия сигналов в ОС UNIX служат два системных вызова kill и signal. Системный вызов kill посылает выбранному процессу сигнал с определенным номером:
Системный вызов signal воспринимает и идентифицирует сигнал:
Возможности системного вызова signal позволяют обрабатывать сигналы следующими способами:
Используя технику сигналов, можно решать самые разнообразные задачи, в том числе можно даже предотвратить такую неприятную особенность, как появление в системе "процессов-зомби".
Семафоры наряду с программными каналами, очередями сообщений и разделяемой памятью входят в так называемую группу IPC - средства межпроцессных коммуникаций (InterProcess Communication).
Семафоры - это достаточно мощное средство синхронизации процессов.
Зачем нужны семафоры?
В любой операционной системе имеются критические ресурсы, с каждым из которых в текущий момент может работать только один процесс, который захватывает ресурс и по истечении короткого интервала времени освобождает его для других. Так вот, семафоры по определенной схеме управляют доступом к критическим ресурсам. Сами по себе они представляют целые числа или массивы целых чисел, с которыми допустимы только две атомарные (неделимые) операции.
Всегда семафоры - это средства задержки процессов, которые тормозятся или "засыпают" на светофорах и "пробуждаются" и приходят в движение при изменении состояния семафоров. Таким образом, дескриптор семафора является информационной управляющей структурой ipc-perm и хранится в файле ipc.h.
Процесс может создать семафор с помощью системного вызова semget.
В результате успешного выполнения semget возвращается semid - идентификатор созданного или уже имеющегося семафора. После создания семафора любой процесс, который знает его идентификатор semid, может на него воздействовать с помощью системного вызова semctl, который осуществляет настройку и проверку состояния множественного семафора.
Семафор является также средством регистрации некоего события (событий) в системе. О наступлении таких событий сообщают процессы, используя системный вызов semop, что приводит к переустановке текущих значений семафора. Процесс, выполняющий системный вызов semop, должен использовать поле sem_op в структуре sembuf, в котором задается требуемая операция:
struct sembuf { ushort sem_num; /* номер элемента семафора */ short sem_op; /* конкретная операция над элементом семафора */ short sem_flg; /* флаги */ }
На семафорах допустимо использовать специальные флаги, например IPC_NOWAIT, которые информируют ядро о нежелании процесса переходить в состояние ожидания, несмотря на "красный" свет семафора. Это продиктовано стремлением избежать опасности блокировки всех процессов, висящих на семафоре, в случае, если по той или иной причине процесс, захвативший ресурс, вдруг аварийно завершился или досрочно вышел по получении сигнала kill. Поскольку этот сигнал невозможно перехватить, то убиваемый процесс не в силах ничего предпринять по освобождению семафора и ресурса.
В этом случае необходимо использовать флаг SEM_UNDO.
Программные каналы - это средство обмена потока информации между процесами. Это синхронизации процессов, потому что параллельные процессы могут всегда корректно взаимодействовать между собой. Попытка записи в канал, который никто не читает, приведет к приостановке "процесса-писателя" до появления "процесса-читателя", и наоборот, попытка чтения из канала, в который никто не пишет, приведет к приостановке "процесса-читателя" до появления "процесса-писателя".
Вообще, pipe - это роскошная штука! Вспомните хотя бы директиву RCVD из доброй и старой RSX11M на PDP-11, которая принимала всего-то, кажется, 17 слов из другой задачи, и сравните это с возможностью pipes в UNIX, которые существовали в ней всегда, с момента ее зарождения. Однако в новых версиях System V появилось много новых замечательных особенностей.
Во-первых, снята проблема взаимодействия (как по сигналам, так и по программным каналам) только "процессов-родственников", что принципиально ограничивало общность средств взаимодействия процессов.
Во-вторых, в добавление к pipes появились новые средства в виде очередей сообщений и разделяемых областей памяти.
По сути pipe - это некоторый специальный файл, в который можно писать информацию или читать из него. Это укладывается в общую концепцию ввода/вывода в UNIX. Выборка и помещение информации в такой особый файл должна происходить по дисциплине FIFO (First In/First Out).
При вводе/выводе всегда используется потоковая модель данных. Это означает, что передаваемые через pipe данные никак не интерпретируются. Длина передаваемых сообщений не контролируется. Сразу несколько "процессов-клиентов" могут писать в канал, а один процесс-сервер может читать из него и обмениваться с "клиентами" сообщениями. При этом сообщения никак не отделяются друг от друга и сервер сам должен определять, кто из "клиентов" и какое сообщение прислал ему.
Очереди сообщений также являются средством обмена информацией между процессами, но в отличие от программных каналов допускают более гибкую организацию взаимодействий процессов.
То есть здесь совсем не обязательно существование парных процессов "читателя" и "писателя". Просто будет существовать некая очередь сообщений, которая используется как хранилище сообщений для любых процессов, которые к ней обращаются, - и "читатели" и "писатели".
При формировании очереди сообщений не используется потоковая модель данных. Каждое сообщение имеет строго определенную структуру: тип и данные. В сообщении допустимы любые данные. Длина данных выбирается пользователем и может быть произвольной в пределах памяти, отведенной под размещение очереди в момент ее создания.
Ядро никак не интерпретирует содержания сообщений, а только лишь обеспечивает размещение и выборку в соответствии с указанным типом. Шаблон сообщения хранится в файле sys/msg.h.
struct msgbuf { long mtype; /*тип сообщения*/ char mtext[1] /*данные*/ }Для работы с очередями в UNIX System V существуют следующие системные вызовы:
Очереди имеют свои преимущества по сравнению, скажем, с программными каналами. Например, чтобы осуществлять полнодуплексную связь между двумя процессами, очевидно, что нужно создать два канала: один для записи, а другой для чтения. В случае использования очередей можно обойтись одной очередью, если различать сообщения по типу: от клиента к серверу и наоборот. Кроме того, имеется возможность извлекать сообщения из очереди не только по дисциплине FIFO, но и в произвольном порядке. Эта особенность открывает перспективу построения систем с приоритетным обслуживанием клиентов.