Межпроцессовые коммуникации linux


Содержание

Межпроцессные коммуникации

Если задуматься над вопросом, что в наши дни является ключевой компонентой в области передовых компьютерных технологий, то без сомнения можно было бы ответить, что такой технологией в наши дни является технология «клиент-сервер». Действительно, эта универсальная модель служит основой построения любых сколь угодно сложных систем, в том числе и сетевых. Пользуясь известным выражением, можно было бы сказать, что технология «клиент-сервер» является ключевым звеном в цепи, ухватившись за которое можно вытащить всю цепь. Разработчики СУБД, коммуникационных систем, систем электронной почты, банковских систем и т.д. во всем мире используют эту технологию. В этом смысле ОС UNIX является для разработчиков системного и прикладного программного обеспечения идеальным средством, потому что в своей основе наиболее полно отвечает требованиям технологии «клиент-сервер», далеко опережая при этом своих основных конкурентов. Для построения моделей типа «клиент-сервер» в UNIX существуют великолепные возможности:

В первоначальных версиях UNIX в момент ее зарождения этих средств либо вообще не существовало, либо они существовали в зачаточном состоянии. Не этим ли объясняется некий скептицизм в отношении UNIX со стороны программистов старшего поколения даже и в наши дни, что уж совсем необъяснимо, особенно после того, как System V, начиная с версии 3.2, явилась перед нами во всем богатстве своего могущественного арсенала. Рассмотрим эти средства более подробно.

Сигналы

Если рассматривать процесс как некую виртуальную машину, то в такой системе должна существовать система прерываний, отвечающая стандартным требованиям:

Всем этим требованиям в UNIX отвечает механика сигналов, которая может не только воспринимать и обрабатывать сигналы, но и порождать их и посылать на другие машины (процессы). Сигналы могут быть синхронными, когда инициатор сигнала — сам процесс, и асинхронными, когда инициатор возникновения сигнала — интерактивный пользователь за терминалом. Источником асинхронных сигналов может быть также ядро, когда оно контролирует определенные состояния аппаратуры рассматриваемые как ошибочные. Для удобства использования каждый сигнал закодирован и цифровое значение сигнала с использованием мнемонической формы записи хранится в файле signal.h

Нижеследующий перечень сигналов представлен в порядке возрастания значений сигналов (целое число в скобках).

Средством посылки и восприятия сигналов в ОС UNIX служат два системных вызова kill и signal. Системный вызов kill посылает выбранному процессу сигнал с определенным номером:

int kill (int pid, int sig);

Системный вызов signal воспринимает и идентифицирует сигнал:

int (* signal (int sig, void (* func (int))) (int));

Возможности системного вызова signal позволяют обрабатывать сигналы следующими способами:

  1. Реакция по умолчанию
  2. Замаскировать сигнал (т.е. проигнорировать его). Для указания указания возможности маскирования второму аргументу присваивается специальное значение SIG_IGN.
  3. Перехватить посланный процессу сигнал и написать собственную программу обработки прерывания по асинхронно посланному сигналу.

Используя технику сигналов, можно решать самые разнообразные задачи, в том числе можно даже предотвратить такую неприятную особенность, как появление в системе «процессов-зомби».

Семафоры

Семафоры наряду с программными каналами, очередями сообщений и разделяемой памятью входят в так называемую группу IPC — средства межпроцессных коммуникаций (InterProcess Communication).

Семафоры — это достаточно мощное средство синхронизации процессов.

Зачем нужны семафоры?

В любой операционной системе имеются критические ресурсы, с каждым из которых в текущий момент может работать только один процесс, который захватывает ресурс и по истечении короткого интервала времени освобождает его для других. Так вот, семафоры по определенной схеме управляют доступом к критическим ресурсам. Сами по себе они представляют целые числа или массивы целых чисел, с которыми допустимы только две атомарные (неделимые) операции.

Всегда семафоры — это средства задержки процессов, которые тормозятся или «засыпают» на светофорах и «пробуждаются» и приходят в движение при изменении состояния семафоров. Таким образом, дескриптор семафора является информационной управляющей структурой ipc-perm и хранится в файле ipc.h.

Процесс может создать семафор с помощью системного вызова semget.

В результате успешного выполнения semget возвращается semid — идентификатор созданного или уже имеющегося семафора. После создания семафора любой процесс, который знает его идентификатор semid, может на него воздействовать с помощью системного вызова semctl, который осуществляет настройку и проверку состояния множественного семафора.

Семафор является также средством регистрации некоего события (событий) в системе. О наступлении таких событий сообщают процессы, используя системный вызов semop, что приводит к переустановке текущих значений семафора. Процесс, выполняющий системный вызов semop, должен использовать поле sem_op в структуре sembuf, в котором задается требуемая операция:

Различают три типа операций:
  1. sem_op > 0 — освобождение процессом, выполнившим semop, ресурса, контролируемого данным элементом семафора. Текущее значение элемента семафора semval инкрементируется на величину sem_op.
  2. sem_op = 0 — процесс, исполняющий такой вызов semop, будет переведен в ожидание до тех пор, пока не освободится ресурс, т. е. semval не станет равным нулю.
  3. semop < 0 - процесс, вызвавший выполнение этой операции, ждет когда sem_op станет больше или равно semval. В данном случае происходит декремент текущего значения семафора на абсолютную величину sem_op. Эта операция используется при захвате ресурса.

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

В этом случае необходимо использовать флаг SEM_UNDO.

Программные каналы (Pipes)

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

Вообще, pipe — это роскошная штука! Вспомните хотя бы директиву RCVD из доброй и старой RSX11M на PDP-11, которая принимала всего-то, кажется, 17 слов из другой задачи, и сравните это с возможностью pipes в UNIX, которые существовали в ней всегда, с момента ее зарождения. Однако в новых версиях System V появилось много новых замечательных особенностей.

Во-первых, снята проблема взаимодействия (как по сигналам, так и по программным каналам) только «процессов-родственников», что принципиально ограничивало общность средств взаимодействия процессов.

Во-вторых, в добавление к pipes появились новые средства в виде очередей сообщений и разделяемых областей памяти.

По сути pipe — это некоторый специальный файл, в который можно писать информацию или читать из него. Это укладывается в общую концепцию ввода/вывода в UNIX. Выборка и помещение информации в такой особый файл должна происходить по дисциплине FIFO (First In/First Out).

При вводе/выводе всегда используется потоковая модель данных. Это означает, что передаваемые через pipe данные никак не интерпретируются. Длина передаваемых сообщений не контролируется. Сразу несколько «процессов-клиентов» могут писать в канал, а один процесс-сервер может читать из него и обмениваться с «клиентами» сообщениями. При этом сообщения никак не отделяются друг от друга и сервер сам должен определять, кто из «клиентов» и какое сообщение прислал ему.

Очереди сообщений

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

То есть здесь совсем не обязательно существование парных процессов «читателя» и «писателя». Просто будет существовать некая очередь сообщений, которая используется как хранилище сообщений для любых процессов, которые к ней обращаются, — и «читатели» и «писатели».

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

Ядро никак не интерпретирует содержания сообщений, а только лишь обеспечивает размещение и выборку в соответствии с указанным типом. Шаблон сообщения хранится в файле sys/msg.h.

Для работы с очередями в UNIX System V существуют следующие системные вызовы:

  • msgget — создание очереди;
  • msgctl — установка параметров;
  • msgsnd — посылка сообщений в очередь;
  • msgrcv — получение сообщений из очереди;

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

Межпроцессорное взаимодействие

Читайте также:

  1. N Взаимодействие кислорода с металлами переменной валентности
  2. Белок-белковое взаимодействие
  3. Биологические процессы в океане и их взаимодействие с гидрологическими условиями
  4. Взаимодействие a и b — частиц с веществом
  5. Взаимодействие HTML-страницы с WEB сервером
  6. Взаимодействие административного права с другим отраслями права.
  7. Взаимодействие в процессе программированного обучения
  8. Взаимодействие водорослей и животных
  9. Взаимодействие гос. и рыночного регулир-я экономики.
  10. Взаимодействие грибов с высшими растениями
  11. Взаимодействие грибов с животными.
  12. Взаимодействие двигательных навыков.

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

1.Сигнальные средства обмена информацией – взаимодействие процессов осуществляется путем передачи сигнала посредством специального системного вызова. Степень воздействия на поведение процесса, получившего информацию, минимальна. Процесс может посылать сигналы (табл. 3.4.) только членам своей группы, состоящей из родителя и предков или всей группе за один системный вызов. Сигналы используются и для других целей. Например, если процесс выполняет вычисления с плавающей точкой и непреднамеренно делит число на 0, он получает сигнал SIGFPE (Floating-Point Exception SIGnal – сигнал исключения при выполнении операции с плавающей точкой). Большинство операционных систем кроме сигналов, требуемых стандартом POSIX, используют дополнительные сигналы.

Сигналы, требуемые стандартом POSIX

№п/п Сигнал Причина
SIGABRT Посылается, чтобы прервать процесс и создать дамп памяти (memory dump) – снимок оперативной памяти
SIGALRM Истекло время будильника
SIGFPE Произошла ошибка при выполнении операции с плавающей точкой (например, деление на 0)
SIGHUP Модем повесил трубку на телефонной линии, использовавшейся процессом
SIGILL Пользователь нажал клавишу DEL, чтобы прервать процесс
SIGQUIT Пользователь нажал клавишу, требуя прекращения работы процесса с созданием дампа памяти
SIGKILL Посылается, чтобы уничтожить процесс (не может игнорироваться или перехватываться)
SIGPIPE Процесс пишет в канал, из которого никто не читает
SIGSEGV Процесс обратился к неверному адресу памяти
SIGTERM Вежливая просьба процессу завершить свою работу
SIGUSR1 Может быть определено приложением
SIGUSR2 Может быть определено приложением

Типы сигналов (принято задавать номерами в диапазоне от 1 до 31 включительно или специальными символьными обозначениями, которые начинаются с приставки SIG) и способы их возникновения в системе жестко регламентированы. Процесс может получить сигнал:

— от аппаратного обеспечения (hardware) при возникновении исключительной ситуации;

— от процесса, выполнившего системный вызов передачи сигнала;

— от операционной системы при наступлении некоторых событий;

— от терминала при нажатии определенной комбинации клавиш;

— от системы управления заданиями;

— при выполнении команды kill.

Например, в Linux существуют несколько причин генерации сигналов или ситуаций, в которых отправляются сигналы:

— терминальные прерывания – нажатие пользователем определенной комбинации клавиш вызывают отправку сигнала процессу, связанному с текущим терминалом;

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

— межпроцессорное взаимодействие – процесс отправляет специальный сигнал другому процессу с помощью системного вызова kill;

— управление заданиями – командные интерпретаторы, поддерживающие управление заданиями, используют сигналы для манипулирования текущими и фоновыми заданиями;

— установка квот – процесс превышает установленную для него квоту на использования ресурсов и ему отправляется соответствующий сигнал;

— уведомления – процессам посылаются уведомления о готовности устройств или наступлении других событий;

— установка таймера – процессу будет послан сигнал, когда значение таймера станет равным нулю.

Когда процесс получает сигнал, то возможен один из двух вариантов развития событий. Если в исполняемой процессом программе для данного сигнала определена подпрограмма обработки (перехвата сигнала), то вызывается эта подпрограмма. В противном случае ядро выполняет от имени процесса действие, определенное по умолчанию для данного сигнала. Когда завершается выполнение подпрограммы обработки, процесс возобновляется с той точки, где был получен сигнал. В ряде случаев процесс игнорирует сигнал, и он не оказывает влияния на процесс. В других случаях процесс блокирует сигнал, и он ставится в очередь ожидания события, которое его разблокирует. Таким образом, существует три варианта реакции процесса на сигнал:

— принудительно проигнорировать сигнал;

— произвести обработку по умолчанию: проигнорировать, остановить процесс (перевести в состояние ожидания до получения другого специального сигнала), либо завершить работу;


— выполнить обработку сигнала, специфицированную пользователем.

Изменить реакцию процесса на сигнал можно с помощью специальных системных вызовов. Реакция на некоторые сигналы не допускает изменения, поэтому они могут быть обработаны только по умолчанию, например, сигнал SIGKILL обрабатывается только по умолчанию и приводит к завершению процесса. В операционной системе UNIX при системном вызове fork() все установленные реакции на сигналы наследуется порожденным процессом. При системном вызове exec() сохраняются реакции только для тех сигналов, которые игнорировались или обрабатывались по умолчанию. Получение любого сигнала, который до вызова exec() обрабатывался пользователем, приведет к завершению процесса.

2.Разделяемая память (shared memory) – взаимодействие процессов осуществляется посредством совместного использования некоторой области (сегмента) адресного пространства. Созданием разделяемой памяти занимается операционная система, а ее использование для передачи/получения информации осуществляется с помощью средств языков программирования. Проблема совместного обращения двух процессов к одним и тем же данным (синхронизация доступа) решается с помощью семафоров[3].

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

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

Например, в операционной системе Linux механизм разделяемых сегментов памяти обеспечивается четырьмя системными вызовами: shmget, shmctl, shmat, shmdt. Так для создания нового сегмента разделяемой памяти или подключения к существующему сегменту используется функция:

int shmget(key_t key, size_t size, int flag)

key – уникальный ключ, необходимый для создания идентификатора;

size – указывает требуемый размер сегмента в байтах;

flag – комбинация флагов доступа на чтение и запись.

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

void * shmat(int shmid, void *shmaddr, int flag),

shmid – идентификатор разделяемой памяти, возвращенный shmget;

shmaddr – определяет желаемый адрес привязки сегмента (в большинстве случаев система сама выбирает начальный адрес для вызвавшего процесса);

flag – комбинация флагов доступа на чтение и запись.

По умолчанию сегмент подключается для чтения и записи, но в аргументе flag можно указать константу shm_rdonly, которая позволит установить доступ только на чтение. После завершения работы с сегментом его следует отключить вызовом int shmdt(void *shmaddr), который получает в качестве аргумента адрес, возвращенный функцией shmat. Эта функция не удаляет сегмент разделяемой памяти, а только снимает его привязку к процессу.

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

int shmctl(int shmid, int cmd, struct shmid_ds *buff)

cmd может принимать следующие значения:

IPC_RMID – удаление сегмента с идентификатором shmid;

IPC_SET – установка значений полей структуры shmid_ds;

IPC_STAT – возвращает вызывающему процессу (через аргумент buff) текущее значение структуры shmid_ds для указанного сегмента разделяемой памяти.

Разделяемые сегменты памяти в Unix/Linux не имеют внешних имен. При получении идентификатора сегмента процесс пользуется числовым ключом. Гарантированно уникальный сегмент можно создать с использованием ключа IPC_PRIVATE, но такой ключ не может быть внешним. Поэтому сегменты используются, как правило, родственными процессами, которые имеют возможность передавать друг другу их идентификаторы, например, через наследуемые ресурсы или через параметры вызова дочерней программы. Таким образом, для создания монитора разделяемой памяти из таблицы системных вызовов следует получить адрес обработчика sys_ipc(), перехватить его, а затем, зная, какой из вышеописанных вызовов совершает система, считать соответствующие данные.

3.Канальные средства обмена информацией – взаимодействие процессов осуществляется с помощью специальных системных вызовов через линии связи, предоставленные операционной системой. Объем передаваемой информации в единицу времени ограничивается пропускной способностью канала. Передача информации по линиям связи может потребовать первоначального резервирования линии для процессов, желающих обменяться информацией и для выбора способа адресации. Различают два способа адресации.

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

симметричная прямая адресация – передающий процесс и процесс, принимающий данные, указывают имена своих партнеров по взаимодействию (посторонний процесс не может вмешаться в процедуру общения двух процессов);

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

Способ непрямой адресации. Передающий процесс помещает данные в промежуточный объект хранения (буфер, имеющий адрес[4]), из которого данные могут быть изъяты другим процессом. При этом передающий процесс не знает об идентификаторе процесса, который получит информацию, а принимающий процесс не знает об идентификаторе процесса, который передал информацию.

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

Однонаправленная связь (симплексная связь) – связь, при которой каждый процесс, ассоциированный с ней, может использовать средство связи только для приема информации или только для ее передачи.

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

полудуплексная связь – двунаправленная связь с поочередной передачей информации в разных направлениях;

дуплексная связь – двунаправленная связь с возможностью одновременной передачи информации в разных направлениях.

Для передачи данных по каналам связи используется две модели: поток ввода-вывода и сообщения.

1.Межпроцессное взаимодействие посредством потока ввода-вывода – передача данных через pipe (канал, трубу, конвейер), является одним из наиболее простых способов передачи информации между процессами по линиям связи. Pipe служит для организации однонаправленной или симплексной связи. Для использования одного канала в двух направлениях необходимы специальные средства синхронизации процессов. Более простой способ организации двунаправленной связи между родственными процессами заключается в использовании двух pipe.

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

Для организации потокового взаимодействия любых процессов в операционной системе применяется средство связи FIFO (First Input First Output) или именованный канал (Named pipe). FIFO подобен каналу, за одним исключением: данные о расположении FIFO в адресном пространстве ядра и его состоянии процессы могут получать не через родственные связи, а через файловую систему. Для этого при создании именованного канала на диске заводится файл специального типа, обращаясь к которому процессы могут получить интересующую их информацию. Для создания FIFO в операционной системе UNIX используется системный вызов mknod() или функция mkfifo(). В процессе работы они заводятся файл-метку, которая позволяет осуществить реальную организацию FIFO в памяти при его открытии с помощью системного вызова open().

После открытия именованные каналы работает аналогично неименованным каналам (анонимным каналам, Anonymous Pipes). Для дальнейшей работы с ними используются системные вызовы read(), write() и close(). Время существования FIFO в адресном пространстве ядра операционной системы, как и в случае с каналом, не может превышать время жизни последнего из использовавших его процессов. Когда все процессы, работающие с FIFO, закрывают все файловые дескрипторы, ассоциированные с ним, система освобождает ресурсы, выделенные под FIFO. Вся непрочитанная информация теряется. В то же время файл-метка остается на диске и может использоваться для новой реальной организации FIFO.

В среде операционной системы Microsoft Windows NT каналы типа pipe используются для передачи данных между параллельно работающими процессами и позволяют организовать передачу данных между локальными процессами и между процессами, запущенными на рабочих станциях в сети. Через канал данные передаются только между двумя процессами. Один из процессов создает канал, другой открывает его. После этого оба процесса могут передавать данные через канал в одну или обе стороны, используя для этого функции, предназначенные для работы с файлами: ReadFile и WriteFile. Приложения могут выполнять над каналами Pipe синхронные или асинхронные операции, аналогично операциям, производимым с файлами. В случае использования асинхронных операций дополнительно реализуются механизм синхронизации процессов. Имена каналов в общем случае имеют следующий вид:

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

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

В простейшем случае один серверный процесс создает один канал для работы с одним клиентским процессом. Однако часто требуется организовать взаимодействие одного серверного процесса с несколькими клиентскими. Например, сервер базы данных может принимать от клиентов запросы и рассылать ответы на них. В случае такой необходимости серверный процесс может создать несколько реализаций канала, по одной реализации для каждого клиентского процесса.

2.Межпроцессное взаимодействие путем передачи сообщения. Данный механизм используется в распределенных системах, где не существует памяти, непосредственно доступной процессам, работающим на разных компьютерах. Сообщение – это блок информации, отформатированный процессом-отправителем таким образом, чтобы он был понятен процессу-получателю. На основе механизма передачи сообщений работают все сетевые службы предоставляющие пользователям сети разнообразные услуги: доступ к удаленным файлам, принтерам, почтовым ящикам и пр. Путем сообщений реализуются запросы от клиентов к серверам, например запрос на просмотр содержимого каталога.

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

Илон Маск рекомендует:  Iis специальные сообщения об ошибках

— адрес — набор символов, уникально определяющих процесс-отправитель и процесс-получатель;

— последовательный номер – номер, который является идентификатором сообщения и используется идентификации потерянных сообщений и дубликатов сообщений в случае отказов в сети;

— структурированная информация – информация, состоящая из поля типа данных, поля длины данных и поля значения данных.

Передачей сообщений занимается транспортная подсистема, которая обеспечивает набор средств для организации взаимодействия процессов по сети. Например, для обеспечения надежности передачи данных каждое передаваемое сообщение снабжается контрольной суммой. При приеме сообщения контрольная сумма вычисляется заново и проверяется ее соответствие пришедшему значению. Если данные не повреждены (контрольные суммы совпадают), то подтверждается правильность их получения. Если через некоторый интервал времени подтверждение правильности полученной информации не придет, то информация считается утерянной и посылается повторно. Во избежание двойного получения одной и той же информации, на приемном конце линии связи осуществляется контроль получения информации. Для гарантии правильного порядка получения сообщений производится их нумерация. При передаче данных операционная система обнаруживает ошибки и извещает об этом взаимодействующие процессы для принятия ими решения о дальнейшем поведении.

Важным аспектом реализации средств обмена данными является вопрос прекращения их обмена. Для способов связи, которые не производили инициализирующих действий, прекращением обмена считается окончание взаимодействия процессов. Если же установление связи требовало инициализации, то, как правило, при ее завершении нужно выполнить ряд операций, например, сообщить операционной системе об освобождении выделенного связного ресурса. В случае, когда кооперативные процессы прекращают взаимодействие согласованно, окончание их работы не влияет на их дальнейшее поведение. Если какой-либо из взаимодействующих процессов не завершил общение и находится в состоянии ожидания получения данных либо в состоянии блокировки, то операционная система должна предпринять определенные действия для исключения блокирования этого процесса. Например, прекращение работы ожидающего процесса либо его извещение о том, что связи больше нет (например, с помощью передачи сигнала).

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

| следующая лекция ==>
Потоки. Реализация мультипрограммирования | Механизмы синхронизации

Дата добавления: 2014-01-07 ; Просмотров: 1560 ; Нарушение авторских прав? ;

Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет

Взаимодействие процессов в ОС Linux.

Основными активными сущностями в Линукс являются процессы. По определению эти процессы являются классическими последовательностями процессов. Каждый поток изначально получает один поток управления (имеет один счётчик команд, который отслеживает следующую исполняеую команду). Также в Линукс процессу позволяется создавать дополнительные потоки. ОС Линукс представляет собой многозадачную систему, следовательно несколько независимых процессов могут работать одновременно. Для создания процессов в ОС Линукс используется системный вызов fork. При этом создаются точные копии исходного процесса(родительский процесс) дочерний процесс. У каждого процесса есть свои области памяти. Открытые файлы используются обеими процессами совместно; изменения произведённые с этим файлом будут видны каждому из процессов. У каждого процесса есть свой идентификатор (PID — Для дочернего он равен «0», для родительского отличен от «0»).

2) Взаимодействие между процессами.

Взаимодействие между процессами ОС Линукс осуществляется с помощью двух механизмов:

— трубы(pipes) — канал между двумя процессами, в который один процесс может писать поток байтов, а другой считывать.

— сигналы(signals) — в данном случае взаимодействие происходит посредством программных прерываний; процесс может посылать сигналы только своей группе процессов (родитель, братья, сёстры и т.д.)

Реализация потоков в ОС Linux.

Исторически процессы были контейнерами ресурсов, а потоки — единицами исполнения. Процесс содержал один или несколько потоков, которые совместно использовали адресное пространство, открытые файлы, обработчики сигналов и все остальное.

В 2000 году в Линукс был введён новый системный вызов clone, который размыл различия между процессами и потоками. Ни в одной другой системе Unix вызова clone нет. Вызов clone создаёт новый поток либо в текущем процессе, либо в новом процессе. В первом случае, новый поток совместно использует совместно с уже существующими единое адресное пространство. Во втором случае, поток получает копию адресного пространства, которое становится недоступным для старых потоков (те, от которых он произошёл). Для сохранения совместимости с другими системами Линукс различает PID и идентификаторы задач (TID — Task IDentifer); оба эти поля хранятся с структуре задач. Когда вызов clone используется для создания нового процесса, PID устанавливается в новое значение, в противном случае задача получает новый TID, но наследует PID. Таким образом все потоки процесса получат тот же самый PID, что и первый поток процесса.

Планирование в ОС Linux.

Операционная система linux алгоритмом планирования различает 3 класса потоков (планирование linux происходит только на основе потоков, а не процессов) :

1. потоки реального времени, обслуживаемые по алгоритму fifo. данные потоки имеют наивысший приоритет и могут вытесняться только потоками того же типа.

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

3. потоки разделения времени.

потоки реального времени имеют приоритеты 1 — 99, остальые — 100-139.

остальные потоки планируются в соответствии с приоритетами и алгоритмом циклической очереди.

Планировщик linux использует ключевую структуру данных, которая называется «очередь исполнения» (runqueue). Эта очередь связана с процессором и поддерживает 2 массива: active & expired. Каждый из этих массивов состоит из заголовков 140 списков.

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

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


Разным уровням приоритетов, присваивается различный квант времени: чем больше приоритет, тем больший квант времени получает данный поток для приоритета 100 — 800млс, для приоритета 139 выделяется 5млс.

Приимущества данного подхода:

а) данный способ планирования гарантирует, что потоки с более низким приоритетом будут выполнены;

б) если, например, потоку необходимо считывание с диска, то, после предоставления такой возможности, ему дается право на быстрое выполнение.

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

Статический приоритет является постоянным для данной задачи и назначается по умолчанию в начале работы. Динамический приоритет назначается и изменяется во время работы в соответствии с политикой планирования.

Загрузка в ОС Linux

Точные детали процесса загрузки варьируется от системы к системе, однако общая схема этапов сохраняется. При включении компьютера начинается работа bios, которая производит тестирование при включении, а также начальное обнаружение устройств и их инициализацию. Затем, в память считывается и исполняется первый сектор загрузочного диска. Этот сектор содержит небольшую программу, считывающую автономную программу с загрузочного устройства (boot). Программа сначала копирует сама себя в старшие адреса памяти. после этого она считывает корневой каталог из загрузочного устройства. для этого она должна понимать формат файловой системы (существую загрузчики, которым не обязательна эта инфомарция). После этого программа boot считывает ядро операционной системы и передает ему управление. На этом этапе команда boot завершает свою работу.

Начальный код ядра написан на ассемблере. И является машинно-зависимым. Данный код выполняет следующие действия:

а) настраивает стэк ядра;

б) определяет тип центрального процессора;

в) вычисляет количество имеющейся оперативной памяти;

г) инициализирует прерывания;

д) разрешает работу блока управления памятью;

е) вызывает процедуру main. (написана на языке C).

Процедура main проделывает работу по инициализации остальной части операционной системы. В первую очередь выделяется память под буфер сообщений. В данный буфер записываются сообщения о ходе инициализации системы. Далее выделяется память для структур данных ядра. Большенство структур имеет фиксированный размер. Однако существуют такие, которые зависят от размера оперативной памяти (кэш страниц,таблица страниц). Далее операционная система начинает определение конфигурации компьютера. Для этого OS считывает файлы конфигурации, в которых сообщаются возможные устройства ввода\вывода, и проверяет, какие из них присутствуют на самом деле. Если устройство присутствует, оно добавляется в таблицу подключенных устройств. Иначе, устройство игнорируется.

После завершения конфигурации всего аппаратного обеспечения нужно настроить стек и загрузить процесс 0 (с самым высоким приоритетов). Процесс — продолжает инициализацию:

а) программирование таймера;

б) монтирование корневой файловой системы;

в) создание процесса 1 (init);

г) создание процесса 2 (page deamon).

Процесс init проверяет свои флаги, в зависимости от которых он запускает систему в однопользовательском или многопользовательском режиме.

Однопользовательский режим — в этом случае init создает процесс исполняющий оболочку и ждет, когда тот завершит свою работу.

Многопользовательский режим — в этом случае init создает процесс rc, который проверяет противоречивость файловой системы. Далее считывается файл ttys, в котором перечисляются терминалы и некоторые их свойства. для каждого из разрешенных терминалов он создает копию самого себя и запускает программу getty. Эта программа устанавливает для каждой линии скорость и прочие свойства, а также выводит сообщение для приглашения в систему (login:). Далее она считывает данные логина с клавиатуры, запускает программу login и завершает свою работу. Login запращивает у пользователя пароль, сравнивает с зашифрованным паролем. Если пароль верен, то логин запускает оболочку, если нет, то процесс запроса повторяется.

Поперечные профили набережных и береговой полосы: На городских территориях берегоукрепление проектируют с учетом технических и экономических требований, но особое значение придают эстетическим.

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого.

Папиллярные узоры пальцев рук — маркер спортивных способностей: дерматоглифические признаки формируются на 3-5 месяце беременности, не изменяются в течение жизни.

Межпроцессное взаимодействие в Unix

Межпроцессное взаимодействие.

Сигналы

Сигналы (signals) – программные прерывания, уведомляющие процесс о наступлении определенного события.

В отличии от других методов не позволяют процессам обмениваться друг с другом какой – либо информацией. При поступлении сигнала ОС сначала определяет, какому процессу предназначен данный сигнал, а потом – как процесс должен на него отреагировать.

Сообщения

Передача сообщений может являться двунаправленной:

1. Send (receiver Process, message);

2. Receive (sender Process, message);

При блокирующей передаче( blocking send) процесс вынужден ожидать до тех пор, пока сообщение не будет доставлено получателю, требуя подтверждения приёма.

При неблокирующей передаче(nonblocking send) процесс – отправитель может продолжать выполнение других операций, даже если сообщение ещё не было доставлено получателю.

Блокирующая – синхронная связь

Неблокирующая – асинхронная связь

Канал

Канал(pipe) – механизм передачи сообщений. Защищённая область памяти, которая выступает в качестве буфера, позволяющего нескольким процессам обмениваться между собой данными.

Process B
shared

1

2

kernel

сообщения общая память

Разделяемая память

Сокеты

Сокеты (socket) – программная конструкция, являющаяся конечным элементом соединения, которая позволяет общаться процессам в сети.

Удаленный вызов процедуры (RPC) – взаимодействие между процессами в распределенных системах.

Межпроцессное взаимодействие в Unix

способы взамодействия тип связи способ применения системные вызовы
разделяемая память общий доступ высокопроизводит, обмен данными shmem, mmap
переменные окружения односторонняя, при запуске режим работы программы setenv
сигналы односторонняя уведомления signal
каналы односторонняя базовый ввод-вывод pipe
сокеты двусторонняя сетевой обмен socket, .

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

Разделяемая память

Самый простой способ «обойти» разделение виртуальных пространств процессов – использования разделяемой памяти. Это специальный механизм, с помощью которого средствами операционной системы два процесса могут обращаться к общему участку физической памяти – каждый через свое адресное пространство.

Для операционной системы этот способ является наиболее простым — ведь все страницы виртуальной памяти процессов в любом случае проецируются на какую-то область физической памяти. Такое взаимодействие не требует каких-либо накладных расходов, процессы обмениваются информацией со скоростью обращения к памяти.

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

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

Дата добавления: 2020-01-29 ; просмотров: 733 ; ЗАКАЗАТЬ НАПИСАНИЕ РАБОТЫ

Межпроцессного взаимодействия — Inter-process communication

В информатике , межпроцессного взаимодействия или межпроцессного взаимодействия ( IPC ) относится конкретно к механизмам операционная система обеспечивает , чтобы позволить процессам для управления общими данными. Как правило, приложения могут использовать IPC, классифицированный в качестве клиентов и серверов , где данные клиентских запросов , а сервер отвечают на запросы клиентов. Многие приложения являются клиентами и серверами, как это обычно видели в распределенных вычислениях . Методы ведения МПК разделены на категории , которые зависят от требований к программному обеспечению , таких как производительность и модульности требований и системных обстоятельств, таких как пропускная способность сети и задержки .

IPC является очень важным для процесса проектирования для микроядер и nanokernels . Микроядра уменьшить количество функциональных возможностей, предоставляемых ядром. Эти функциональные затем получают путем взаимодействия с серверами через IPC, увеличивая значительно количество МПК по сравнению с обычным монолитным ядром.

содержание

подходы

метод Краткое описание Предоставлено ( операционных систем или других средах)
файл Запись хранится на диске, или запись, синтезированная по требованию файлового сервера, которые могут быть доступны с помощью нескольких процессов. Большинство операционных систем
Сигнал ; также Asynchronous System Trap Системное сообщение было отправлено от одного процесса к другому, как правило, не используется для передачи данных, но вместо этого используется для удаленного командовать партнером процесса. Большинство операционных систем
Разъем Данные передаются по сетевому интерфейсу, либо к другому процессу на тот же компьютер или на другой компьютер в сети. Поток-ориентированный ( TCP ; данные , записанные через сокет требует форматирования , чтобы сохранить границы сообщений) или более редко ориентированное на обработку сообщений ( UDP , SCTP на ). Большинство операционных систем
доменный сокет Unix Подобно интернет-розетку, но все общение происходит в ядре. сокеты используют файловую систему в качестве адресного пространства. Процессы ссылаться на сокет домена в качестве дескриптора, и множественные процессы могут взаимодействовать с одним гнездом Все операционные системы POSIX и Windows, 10
очередь сообщений Поток данных похож на сокет, но обычно сохраняет границы сообщений. Как правило, реализовано в операционной системе, они позволяют несколько процессов чтения и записи в очередь сообщений, не будучи непосредственно связан друг с другом. Большинство операционных систем
труба Однонаправленный канал данных. Данные , записанные в конце записи трубы буферизуются операционной системой до тех пор, пока не будет читать из считанного конца трубы. Двухсторонняя потоки данных между процессами может быть достигнута путем создания двух труб при помощи стандартный ввод и вывод . Все POSIX системы, Windows ,
Названный трубы Труба осуществляется через файл в файловой системе вместо стандартного ввода и вывода . Несколько процессов могут читать и записывать в файл в качестве буфера для данных IPC. Все системы POSIX, Windows, AmigaOS 2.0+
Общая память Множественные процессы имеют доступ к одному блоку памяти , который создает общий буфер для процессов , чтобы общаться друг с другом. Все системы POSIX, Windows,
Передача сообщений Позволяет несколько программ для общения с помощью очередей сообщений и / или не-OS управляемых каналов, обычно используемых в модели параллелизма. Используется в RPC , RMI , и MPI парадигм, Java RMI , CORBA , DDS , MSMQ , почтовые слоты , QNX , другие
Файл Отображаемые в памяти Файл отображается в ОЗУ и может быть изменен путем изменения адреса памяти непосредственно вместо вывода в поток. Это разделяет те же преимущества, что и стандартный файл . Все системы POSIX, Windows,

синхронизация

В зависимости от раствора, МПК механизм может обеспечивать синхронизацию или оставить его до процессов и нитей, чтобы взаимодействовать друг с другом (например, с помощью совместно используемой памяти).

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

Примеры примитивов синхронизации являются:

Приложения

интерфейсы удаленного вызова процедур


  • Java «s Remote Method Invocation (RMI)
  • ОНК RPC
  • XML-RPC или SOAP
  • JSON-RPC
  • Сообщение Bus (Mbus) (указано в RFC 3259 )
  • .NET Remoting

стек Платформа связи

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

  • KDE «s Desktop Communications Protocol (DCOP) — устаревший D-Bus
  • D-Bus
  • OpenWrt использует UBUS микро архитектуру шины
  • MCAPI многожильных Communications API
  • SIMPL проекта Interprocess сообщений синхронного для Linux (SIMPL)
  • 9P (Plan 9 Filesystem Protocol)
  • Распределенная вычислительная среда (АКД)
  • бережливость
  • TIPC
  • ZeroC «S Internet Communications Engine (ICE)
  • ØMQ
  • Эндуро / X Middleware
  • YAMI4

Операционная система связи стек

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

  • Apple Computer «s Apple , событие (ранее известное как межпрограммные связи (МАГ)).
  • Enea вLINX для Linux ( с открытым исходным кодом) и различных DSP и процессоров общего назначения под OSE
  • Маха ядро Mach порты «s
  • Microsoft «s ActiveX , Component Object Model (COM), Microsoft Transaction Server ( COM + ), Distributed модели компонентных объектов (DCOM), динамического обмена данными (DDE), Связывание и внедрение объектов (OLE), анонимные трубы , названные трубы , локальная процедура Вызов , почтовые слоты , цикл сообщений , MSRPC , .NET Remoting и Windows Communication Foundation , (WCF)
  • Novell «s SPX
  • POSIXММАП , очереди сообщений , семафоры и разделяемая память
  • RISC OS сообщений «s
  • SolarisДвери
  • System V очереди сообщений, семафоры и разделяемая память «s
  • OpenBinder Открытое связующее
  • QNX службы «s PPS (Publish / Subscribe Persistent)

Распределенные объектные модели

Следующие платформы или языка программирования Specific-интерфейсы, использующие IPC, но сами не реализуют его:

Межпроцессное взаимодействие и синхронизация в Linux

Работающая система Linux состоит из большого количества процессов, многие из которых работают независимо друг от друга. Но некоторые процессы для достижения своих на­меченных целей сотрудничают друг с другом, и им необходимы методы обмена данными и синхронизация их действий.

Одним из способов обмена данными между процессами является чтение информации с дисковых файлов и ее запись в эти файлы. Но для многих приложений этот способ является слишком медленным и негибким. Поэтому в Linux, как и во всех современных реализациях UNIX, предоставляется обширный набор механизмов для межпроцессного взаимодействия (Interprocess Communication, IPC), включая следующие:

  • сигналы, которые используются в качестве признака возникновения события;
  • конвейеры (известные пользователям оболочек в виде оператора | ) и FIFO-буферы, которые могут применяться для передачи данных между процессами;
  • сокеты, которые могут использоваться для передачи данных от одного процесса к другому; данные при этом находятся на одном и том же базовом компьютере либо на различных хостах, связанных по сети;
  • файловая блокировка, позволяющая процессу блокировать области файла с целью предотвращения их чтения или обновления содержимого файла другими процессами;
  • очереди сообщений, которые используются для обмена сообщениями (пакетами дан­ных) между процессами;
  • семафоры, которые применяются для синхронизации действий процессов;
  • совместно используемая память, позволяющая двум и более процессам совместно ис­пользовать часть памяти. Когда один процесс изменяет содержимое совместно использу­емой области памяти, изменения тут же могут быть видимы всем остальным процессам.

Широкое разнообразие IPC-механизмов в системах UNIX с иногда перекрываемыми функциональными возможностями частично объясняется их различием в отдельных вариантах UNIX-систем и требованиями со стороны различных стандартов. Например, FIFO-буферы и доменные сокеты UNIX, по сути, выполняют одну и ту же функцию, позволяющую неродственным процессам в одной и той же системе осуществлять обмен данными. Их совместное существование в современных системах UNIX объясняется тем, что FIFO-буферы пришли из System V, а сокеты были взяты из BSD.

Межпроцессовые коммуникации LINUX

6.1. Введение

Система Linux IPC (Inter-process communication) предоставляет средства для взаимодействия процессов между собой.

В распоряжении программистов есть несколько методов IPC:

  • полудуплексные каналы UNIX
  • FIFO (именованные каналы)
  • Очереди сообщений в стиле SYSV
  • Множества семафоров в стиле SYSV
  • Разделяемые сегменты памяти в стиле SYSV
  • Сетевые сокеты (в стиле Berkeley) (не охватывается этой статьей)
  • Полнодуплексные каналы (каналы потоков) (не охватывается этой статьей)

Если эти возможности эффективно используются, то они обеспечивают солидную базу для поддержания идеологии клиент/сервер в любой UNIX-системе, включая Linux.

6.2. Полудуплексные каналы UNIX

6.2.1. Основные понятия

Канал — это средство связи стандартного вывода одного процесса со стандартным вводом другого. Каналы — это старейший из инструментов IPC, существующий приблизительно со времени появления самых ранних версий оперативной системы UNIX. Они предоставляют метод односторонних коммуникаций (отсюда термин half-duplex) между процессами.

Эта особенность широко используется даже в командной строке UNIX (в shell-е).

Приведенный выше канал принимает вывод ls как ввод sort, и вывод sort за ввод lp. Данные проходят через полудуплексный канал, перемещаясь (визуально) слева направо.

Хотя большинство из нас использует каналы в программировании на shell-е довольно часто, мы редко задумываемся о том, что происходит на уровне ядра.

Когда процесс создает канал, ядро устанавливает два файловых дескриптора для пользования этим каналом. Один такой дескриптор используется, чтобы открыть путь ввода в канал (запись), в то время как другой применяется для получения данных из канала (чтение). В этом смысле, канал мало применим практически, так как создающий его процесс может использовать канал только для взаимодействия с самим собой. Рассмотрим следующее изображение процесса и ядра после создания канала:

Из этого рисунка легко увидеть, как файловые дескрипторы связаны друг с другом. Если процесс посылает данные через канал (fd0), он имеет возможность получить эту информацию из fd1. Однако этот простенький рисунок отображает и более глобальную задачу. Хотя канал первоначально связывает процесс с самим собой, данные, идущие через канал, проходят через ядро. В частности, в Linux-е каналы внутренне представлены корректным inode-ом. Конечно, этот inode существует в пределах самого ядра, а не в какой-либо физической файловой системе. Эта особенность откроет нам некоторые привелекательные возможности для ввода/вывода, как мы увидим немного позже.

Зачем же нам неприятности с созданием канала, если мы всего-навсего собираемся поговорить сами с собой? На самом деле, процесс, создающий канал, обычно порождает дочерний процесс. Как только дочерний процесс унаследует какой-нибудь открытый файловый дескриптор от родителя, мы получаем базу для мультипроцессовой коммуникации (между родителем и потомком). Рассмотрим эту измененную версию нашего рисунка:

Теперь мы видим, что оба процесса имеют доступ к файловым дескрипторам, которые основывают канал. На этой стадии должно быть принято критическое решение. В каком направлении мы хотим запустить данные? Потомок посылает информацию к родителю или наоборот? Два процесса взаимно согласовываются и «закрывают» неиспользуемый конец канала. Пусть потомок выполняет несколько действий и посылает информацию к родителю обратно через канал. Наш новый рисунок выглядел бы примерно так:

Конструкция канала теперь полная. Все, что осталось сделать — это использовать его. Чтобы получить прямой доступ к каналу, можно применять системные вызовы, подобные тем, которые нужны для ввода/вывода в файл или из файла на низком уровне (вспомним, что в действительности каналы внутренне представлены как корректный inode).

Чтобы послать данные в канал, мы используем системный вызов write(), а чтобы получить данные из канала — системный вызов read(). Вспомним, что системные вызовы ввода/вывода в файл или из файла работают с файловыми дескрипторами! (Однако, не забывайте, что некоторые системные вызовы, как, например, lseek(), не работают с дескрипторами.)

6.2.2. Создание каналов на Си

Создание каналов на языке программирования Си может оказаться чуть более сложным, чем наш простенький shell-пример. Чтобы создать простой канал на Си, мы прибегаем к использованию системного вызова pipe(). Для него требуется единственный аргумент, который является массивом из двух целых (integer), и, в случае успеха, массив будет содержать два новых файловых дескриптора, которые будут использованы для канала. После создания канала процесс обычно порождает новый процесс (вспомним, что процесс-потомок наследует открытые файловые дескрипторы).

Первое целое в массиве (элемент 0) установлено и открыто для чтения, в то время как второе целое (элемент 1) установлено и открыто для записи. Наглядно говоря, вывод fd1 становится вводом для fd0. Еще раз отметим, что все данные, проходящие через канал, перемещаются через ядро.

Вспомните, что имя массива decays в указатель на его первый член. fd — это эквивалент &fd[0]. Раз мы установили канал, то ответвим нашего нового потомка:

Если родитель хочет получить данные от потомка, то он должен закрыть fd1, а потомок должен закрыть fd0. Если родитель хочет послать данные потомку, то он должен закрыть fd0, а потомок — fd1. С тех пор как родитель и потомок делят между собой дескрипторы, мы должны всегда быть уверены, что не используемый нами в данный момент конец канала закрыт; EOF никогда не будет возвращен, если ненужные концы канала не закрыты.

2. Ядро LINUX


Как было упомянуто ранее, раз канал был установлен, то файловые дескрипторы могут обрабатываться подобно дескрипторам нормальных файлов.

Илон Маск рекомендует:  Добавляем компонент в стандартный message dialog

Часто дескрипторы потомка раздваиваются на стандартный ввод или вывод. Потомок может затем exec() другую программу, которая наследует стандартные потоки. Давайте посмотрим на системный вызов dup():

NOTES: старый дескриптор не закрыт! Оба работают совместно!

Несмотря на то, что старый и новосозданный дескрипторы взаимозаменяемы, мы будем сначала закрывать один из стандартных потоков. Системный вызов dup() использует наименьший по номеру неиспользуемый дескриптор для нового.

Поскольку файловый дескриптор 0 (stdin) был закрыт, вызов dup() дублировал дескриптор ввода канала (fd0) на его стандартный ввод. Затем мы сделали вызов execlp(), чтобы покрыть код потомка кодом программы sort. Поскольку стандартные потоки exec()-нутой программы наследуются от родителей, это означает, что вход канала ста для потомка стандартным вводом! Теперь все, что первоначальный процесс-родитель посылает в канал, идет в sort.

Существует другой системный вызов, dup2(), который также может использоваться. Этот особенный вызов произошел с Version 7 of UNIX и был поддержан BSD, и теперь требуется по стандарту POSIX.

Благодаря этому особенному вызову мы имеем закрытую операцию и действующую копию за один системный вызов. Вдобавок, он гарантированно неделим, что означает, что он никогда не будет прерван поступающим сигналом. С первым системным вызовом dup() программисты были вынуждены предварительно выполнять операцию close(). Это приводилок наличию двух системных вызовов с малой степенью защищенности в краткий промежуток времени между ними. Если бы сигнал поступил в течение этого интервала времени, копия дескриптора не состоялась бы. dup2() разрешает для нас эту проблему.

6.2.3. Каналы — легкий путь!

Если все изложенные выше изыскания кажутся слишком размытым способом создания и использования каналов, то вот альтернатива этому.

Эта стандартная библиотечная функция создает полудуплексный канал посредством вызывания pipe() внутренне. Затем она порождает дочерний процесс, запускает Bourne shell и исполняет аргумент command внутри shell-а. Управление потоком данных определяется вторым аргументом, type. Он может быть «r» или «w» — для чтения или записи, но не может быть и то, и другое! Под Linux-ом канал будет открыт в виде, определенном первой литерой аргумента «type». Поэтому, если вы попытаетесь ввести «rw», канал будет открыт только в виде «read».

Каналы, созданные popen(), должны быть закрыты pclose(). К этому моменту вы, вероятно, уже использовали [реализовали] popen/pclose share, удивительно похожий на стандартный файловый поток I/O функций

Функция pclose() выполняет wait4() над процессом, порожденным popen()-ом. Когда она возвращается, то уничтожает канал и файловый поток. Повторим еще раз, что этот эффект аналогичен эффекту, вызываемому функцией fclose() для нормального, основанного на потоке файлового ввода/вывода.

Рассмотрим пример, который открывает канал для команды сортировки и начинает сортировать массив строк:

В качестве другого примера popen()-а, рассмотрим маленькую программу, открывающую два канала (один — для команды ls, другой — для сортировки):

В качестве последней демонстрации popen(), давайте создадим программу, характерную для открытия канала между отданной командой и именем файла:

Попробуйте выполнить эту программу с последующими заклинаниями:

6.2.4. Атомарные (неделимые) операции с каналами

Для того чтобы операция рассматривалась как «атомарная», она не должна прерываться ни по какой причине. Неделимая операция выполняется сразу. POSIX стандарт говорит в /usr/include/posix_lim.h, что максимальные размеры буфера для атомарной операции в канале таковы:

Атомарно по каналу может быть получено или записано до 512 байт. Все, что выходит за эти пределы, будет разбито и не будет выполняться атомарно. Однако, под Linux-ом этот атомарный операционный лимит определен в «linux/limits.h» следующим образом:

Как вы можете заметить, Linux предоставляет минимальное количество байт, требуемое POSIX-ом, довольно щедро. Атомарность операции с каналом становится важной, если вовлечено более одного процесса (FIFOS). Например, если количество байтов, записанных в канал, превышает лимит, отпущенный на отдельную операцию, а в канал записываются многочисленные процессы, то данные будут смешаны, т.е. один процесс может помещать данные в канал между записями других.

6.2.5. Примечания к полудуплексным каналам

  • Двусторонние каналы могут быть созданы посредством открывания двух каналов и правильным переопределением файловых дескрипторов в процессе-потомке.
  • Вызов pipe() должен быть произведен ПЕРЕД вызовом fork(), или дескрипторы не будут унаследованы процессом-потомком! (то же для popen()).
  • С полудуплексными каналами любые связанные процессы должны разделять происхождение. Поскольку канал находится в пределах ядра, любой процесс, не состоящий в родстве с создателем канала, не имеет способа адресовать его. Это не относится к случаю с именованными каналами (FIFOS).

6.3. Именованные каналы (FIFOs — First In First Out)

6.3.1. Основные понятия

Именованные каналы во многом работают так же, как и обычные каналы, но все же имеют несколько заметных отличий.

  • Именованные каналы существуют в виде специального файла устройства в файловой системе.
  • Процессы различного происхождения могут разделять данные через такой канал.
  • Именованный канал остается в файловой системе для дальнейшего использования и после того, как весь ввод/вывод сделан.

6.3.2. Создание FIFO

Есть несколько способов создания именованного канала. Первые два могут быть осуществлены непосредственно из shell-а.

Эти две команды выполняют идентичные операции, за одним исключением. Команда mkfifo предоставляет возможность для изменения прав доступа к файлу FIFO непосредственно после создания. При использовании mknod будет необходим вызов команды chmod.

Файлы FIFO могут быть быстро идентифицированы в физической файловой системе посредством индикатора «p», представленного здесь в длинном листинге директории.

Также заметьте, что вертикальный разделитель располагается непосредственно после имени файла. Другая веская причина запустить

Чтобы создать FIFO на Си, мы можем прибегнуть к использованию системного вызова mknod():

Оставим более детальное обсуждение mknod()-а man page, а сейчас давайте рассмотрим простой пример создания FIFO на Си:

В данном случае файл «/tmp/MYFIFO» создан как FIFO-файл. Требуемые права — это «0666», хотя они находятся под влиянием установки umask, как например:

Общая хитрость — использовать системный вызов umask() для того, чтобы временно устранить значение umask-а:

Кроме того, третий аргумент mknod()-а игнорируется, в противном случае мы создаем файл устройства. В этом случае он должен отметить верхнее и нижнее числа файла устройства.

6.3.3. Операции FIFO

Операции ввода/вывода FIFO, по существу, такие же, как для обычных каналов, за одним исключением. Чтобы физически открыть проход к каналу, должен быть использован системный вызов «open» или библиотечная функция. С полудуплексными каналами это невозможно, поскольку канал находится в ядре, а не в физической файловой системе. В нашем примере мы будем трактовать канал как поток, открывая его fopen()-ом и закрывая fclose()-ом.

Рассмотрим простой сервер-процесс:

Поскольку FIFO блокирует по умолчанию, запустим сервер фоном после того, как его откомпилировали:

Скоро мы обсудим действие блокирования, но сначала рассмотрим следующего простого клиента для нашего сервера:

6.3.4. Действие блокирования над FIFO

Если FIFO открыт для чтения, процесс его блокирует до тех пор, пока какой-нибудь другой процесс не откроет FIFO для записи. Аналогично для обратной ситуации. Если такое поведение нежелательно, то может быть использован флаг O_NONBLOCK в системном вызове open(), чтобы отменить действие блокирования.

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

6.3.5. Неизвестный SIGPIPE

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

6.4 System V IPC

6.4.1. Базовые понятия

Вместе с System V AT&T предложил три новых типа IPC средств (очереди сообщений, семафоры и разделяемая память). POSIX еще не стандартизировал эти средства, но большинство разработок их уже поддерживает. Впрочем, Беркли (BSD) в качестве базовой формы IPC использует скорее сокеты, чем элементы System V. Linux имеет возможность использовать оба вида IPC (BSD и System V), хотя мы не будем обсуждать сокеты в этой главе.

Версия System V IPC для LINUX-а авторизована Кришной Баласубраманьяном (Krishna Balasubramanian), balasub@cis.ohio-state.edu.

Каждый объект IPC имеет уникальный IPC идентификатор. (Когда мы говорим «объект IPC», мы подразумеваем очередь единичных сообщений, множество семафоров или разделяемый сегмент памяти.) Этот идентификатор требуется ядру для однозначного определения объекта IPC. Например, чтобы сослаться на определенный разделяемый сегмент, единственное, что вам потребуется, это уникальное значение ID, которое привязано к этому сегменту.

Идентификатор IPC уникален только для своего типа объектов. То есть, скажем, возможна только одна очередь сообщений с идентификатором «12345», так же как номер «12345» может иметь какое-нибудь одно множество семафоров или (и) какой-то разделяемый сегмент.

Чтобы получить уникальный ID нужен ключ. Ключ должен быть взаимно согласован процессом-клиентом и процессом-сервером. Для приложения это согласование должно быть первым шагом в построении среды.

(Чтобы позвонить кому-либо по телефону, вы должны знать его номер. Кроме того, телефонная компания должна знать как провести ваш вызов к адресату. И только когда этот адресат ответит, связь состоится.)

В случае System V IPC «телефон» соединяет объекты IPC одного типа. Под «телефонной компанией», или методом маршрутизации, следует понимать ключ IPC.

Ключ, генерируемый приложением самостоятельно, может быть каждый раз один и тот же. Это неудобно, полученный ключ может уже использоваться в настоящий момент. Функцию ftok() используют для генерации ключа и для клиента, и для сервера:

Возвращаемый ftok()-ом ключ инициируется от значения inode и нижним числом устройства файла — первого аргумента, и от литеры — второго аргумента. Это не гарантирует уникальности, но приложение может проверить наличие коллизий и, если понадобится, сгенерировать новый ключ.

В предложенном выше куске директория /tmp/myapp смешивается с однолитерным идентификатором ‘a’. Другой распространенный пример — использовать текущую директорию.

Выбор алгоритма генерации ключа полностью отдается на усмотрение прикладного программиста. Так же как и меры по предотвращению ситуации гонок, дедлоков и т.п., любой метод имеет право на жизнь. Для наших демонстрационных целей мы ограничимся ftok()-ом. Если условиться, что каждый процесс-клиент запускается со своей уникальной «домашней» директории, то генерируемые ключи будут всегда удовлетворительны.

Итак, значение ключа, когда оно получено, используется в последующих системных вызовах IPC для создания или улучшения доступа к объектам IPC.

Команда ipcs выдает статус всех объектов System V IPC.

LINUX-версия ipcs также была авторизована Кришной Баласубраманьяном.

По умолчанию показывают все три категории объектов. Посмотрим на следующий незатейливый вывод ipcs-а:

Здесь мы видим одинокую очередь с идентификатором «0». Она принадлежит пользователю root и имеет восьмеричные права доступа 660, или -rw-rw—. Очередь содержит одно пятибайтное сообщение.

Команда ipcs — это очень мощное средство, позволяющее подсматривать за механизмом ядреной памяти для IPC-объектов. Изучайте его, пользуйтесь им, благоговейте перед ним.

Команда ipcrm удаляет объект IPC из ядра. Однако, поскольку объекты IPC можно удалить через системные вызовы в программе пользователя (как это делать мы увидим чуть позднее), часто нужды удалять их «вручную» нет. Особенно это касается всяких программных оболочек.

Внешний вид ipcrm прост:


Требуется сказать, является ли удаляемый объект очередью сообщений (msg), набором семафоров (sem), или сегментом разделяемой памяти (shm). IPC ID может быть получен через команду ipcs. Напомним, что ID уникален в пределах одного из трех типов объектов IPC, поэтому мы обязаны назвать этот тип.

6.4.2. Очереди сообщений

Очереди сообщений представляют собой связный список в адресном пространстве ядра. Сообщения могут посылаться в очередь по порядку и доставаться из очереди несколькими разными путями. Каждая очередь сообщений однозначно определена идентификатором IPC.

Внутренние и пользовательские структуры данных

Ключом к полному осознанию такой сложной системы, как System V IPC, является более тесное знакомство с различными структурами данных, которые лежат внутри самого ядра. Даже для большинства примитивных операций необходим прямой доступ к некоторым из этих структур, хотя другие используются только на гораздо более низком уровне.

Первой структурой, которую мы рассмотрим, будет msgbuf. Его можно понимать как шаблон для данных сообщения. Поскольку данные в сообщении программист определяет сам, он обязан понимать, что на самом деле они являются структурой msgbuf. Его описание находится в linux/msg.h:

Тип сообщения, представленный натуральным числом. Он обязан быть натуральным!

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

С другой стороны, старайтесь дать наглядное имя элементу данных сообщения (в примере был mtext). В это поле можно записывать не только массивы литер, но и вообще любые данные в любой форме. Поле действительно полностью произвольно, поэтому вся структура может быть переопределена программистом, например, так:

Здесь мы также видим структуру сообщения, но второй элемент заменился на два, причем один из них — другая структура! В этом прелесть очередей сообщений, ядро не разбирает данные, какими бы они ни были.

Существует, однако, ограничение на максимальный размер сообщения.В LINUX-е он определен в linux/msg.h:

Сообщения не могут быть больше, чем 4056 байт, сюда входит и элемент mtype, который занимает 4 байта (long).

Структура msg ядра

Ядро хранит сообщение в очереди структуры msg. Она определена в linux/msg.h следующим образом:

Указатель на следующее сообщение в очереди. Сообщения объединены в односвязный список и находятся в адресном пространстве ядра.

Каждый из трех типов IPC-объектов имеет внутреннее представление, которое поддерживается ядром. Для очередей сообщений это структура msqid_ds. Ядро создает, хранит и сопровождает образец такой структуры для каждой очереди сообщений в системе. Она определена в linux/msg.h следующим образом:

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

Экземпляр структуры ipc_perm, определенной в linux/ipc.h. Она содержит информацию о доступе для очереди сообщений, включая права доступа и информацию о создателе сообщения (uid и т.п.).

Ссылка на первое сообщение в очереди (голова списка).

Ссылка на последний элемент списка (хвост списка).

Момент времени (time_t) посылки последнего сообщения из очереди.

Момент времени последнего изъятия элемента из очереди.

Момент времени последнего изменения, проделанного в очереди (подробнее об этом позже).

Указатели в очередь ожидания ядра. Они используются, когда операция над очередью сообщений переводит процесс в состояние спячки (то есть очередь переполнена, и процесс ждет открытия).

Число байт, стоящих в очереди (суммарный размер всех сообщений).

Количество сообщений в очереди на настоящий момент.

Максимальный размер очереди.

PID процесса, пославшего последнее в очереди сообщение.

PID последнего процесса, взявшего из очереди сообщение.

Структура ipc_perm ядра

Информацию о доступе к IPC-объектам ядро хранит в структуре ipc_perm. Например, описанная выше структура очереди сообщений содержит одну структуру типа ipc_perm в качестве элемента. Следующее ее определение дано в linux/ipc.h.

Все приведенное выше говорит само за себя. Сохраняемая отдельно вместе с ключом IPC-объекта информация содержит данные о владельце и создателе этого объекта (они могут различаться). Режимы восьмеричного доступа также хранятся здесь, как unsigned short. Наконец, сохраняется порядковый номер использования гнезда. Каждый раз когда IPC объект закрывается через системный вызов (уничтожается), этот номер уменьшается на максимальное число объектов IPC, которые могут находиться в системе. Касается вас это значение? Нет.

Системный вызов msgget() нужен для того, чтобы создать очередь сообщений или подключиться к существующей.

Первый аргумент msgget() значение ключа (мы его получаем при помощи ftok()). Этот ключ сравнивается с ключами уже существующих в ядре очередей. При этом операция открытия или доступа к очереди зависит от содержимого аргумента msgflg:

Создает очередь, если она не была создана ранее.

При использовании совместно с IPC_CREAT, приводит к неудаче если очередь уже существует.

Вызов msgget() с IPC_CREAT, но без IPC_EXCL всегда выдает идентификатор (существующей с таким ключом или созданной) очереди. Использование IPC_EXCL вместе с IPC_CREAT либо создает новую очередь, либо, если очередь уже существует, заканчивается неудачей. Самостоятельно IPC_EXCL бесполезен, но вместе c IPC_CREAT он дает гарантию, что ни одна из существующих очередей не открывается для доступа.

Восьмеричный режим может быть OR-нут в маску доступа. Каждый IPC-объект имеет права доступа, аналогичные правам доступа к файлу в файловой системе UNIX-а.

Напишем оберточную функцию для открытия или создания очереди сообщений.

Отметьте использование точного ограничителя доступа 0660. Эта небольшая функция возвращает идентификатор очереди (int) или -1 в случае ошибки. Единственный требуемый аргумент — ключевое значение.

Системный вызов msgsnd()

Получив идентификатор очереди, мы можем выполнять над ней различные действия. Чтобы поставить сообщение в очередь, используйте системный вызов msgsnd():

Первый аргумент msgsnd — идентификатор нашей очереди, возвращенный предварительным вызовом msgget. Второй аргумент, msgp — это указатель на редекларированный и загруженный буфер сообщения. Аргумент msgsz содержит длину сообщения в байтах, не учитывая тип сообщения (long 4 байта).

Если очередь переполнена, то сообщение не записывается в очередь, и управление передается вызывающему процессу. Если эта ситуация не обрабатывается вызывающим процессом, то он приостанавливается (блокируется), пока сообщение не будет прочитано.

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

Эта функция пытается послать сообщение, лежащее по указанному адресу (qbuf), в очередь сообщений, идентифицированную qid. Напишем небольшую утилиту с нашими двумя оберточными функциями:

После создания/открытия нашей очереди принимаемся за загрузку буфера сообщения с тестовыми данными (заметьте отсутствие текстовых данных для иллюстрации нашего положения о посылке двоичной информации). Вызов нашего send_message ловко доставит сообщение в очередь.

Теперь, когда мы имеем сообщение в очереди, попытайтесь при помощи ipcs посмотреть на статус нашей очереди. Обсудим, как забрать из очереди сообщение. Для этого используется системный вызов msgrcv():

Конечно, первый аргумент определяет очередь, из которой будет взято сообщение (должен быть возвращен сделанным предварительно вызовом msgget). Второй аргумент (msgp) представляет собой адрес буфера, куда будет положено изъятое сообщение. Третий аргумент, msgsz, ограничивает размер структуры-буфера без учета длины элемента mtype.

Еще раз повторимся, это может быть легко вычислено:

Четвертый аргумент, mtype — это тип сообщения, изымаемого из очереди. Ядро будет искать в очереди наиболее старое сообщение такого типа и вернет его копию по адресу, указанному аргументом msgp. Существует один особый случай: если mtype = 0, то будет возвращено наиболее старое сообщение, независимо от типа.

Если IPC_NOWAIT был послан флагом, и нет ни одного удовлетворительного сообщения, msgrcv вернет вызывающему процессу ENOMSG. В противном случае вызывающий процесс блокируется, пока в очередь не прибудет сообщение, соответствующее параметрам msgrcv(). Если, пока клиент ждет сообщения, очередь удаляется, то ему возвращается EIDRM. EINTR возвращается, если сигнал поступил, пока процесс находился на промежуточной стадии между ожиданием и блокировкой.

Давайте рассмотрим функцию-переходник для изъятия сообщения из нашей очереди.

После успешного изъятия сообщения удаляется из очереди и его ярлык.

Бит MSG_NOERROR в msgflg предоставляет некоторые дополнительные возможности. Если физическая длина сообщения больше, чем msgsz, и MSG_NOERROR установлен, то сообщение обрезается и возвращается только msgsz байт. Нормальный же msgrcv() возвращает -1 (E2BIG), и сообщение остается в очереди до последующих запросов. Такое поведение можно использовать для создания другой оберточной функции, которая позволит нам «подглядывать» внутрь очереди, чтобы узнать, пришло ли сообщение, удовлетворяющее нашему запросу.

Выше вы заметили отсутствие адреса буфера и длины. В этом конкретном случае мы хотели, чтобы вызов прошел неудачно. Однако мы проверили возвращение E2BIG, которое должно показать, существует ли сообщение затребованного типа. Оберточная функция возвращает TRUE в случае успеха, и FALSE — в противном случае. Отметьте также установленный флаг IPC_NOWAIT, который помешает блокировке, о которой мы говорили раньше.

Системный вызов msgсtl()

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

Для осуществления контроля над очередью предназначен системный вызов msgсtl.

Теперь из общих соображений ясно, что прямые манипуляции с внутреностями ядра могут привести к очень занимательным последствиям. К сожалению, по-настоящему весело будет только тому, кто любит вдребезги и с наслаждением крушить подсистему IPC. Однако при использовании msgctl() с некоторыми командами вероятность огорчительных результатов не очень велика. Вот их и рассмотрим.

Сохраняет по адресу buf структуру msqid_ds для очереди сообщений.

Устанавливает значение элемента ipc_perm структуры msqid. Значения выбирает из буфера.

Удаляет очередь из ядра.

Вернемся к нашему разговору о внутреннем представлении очереди сообщений: msqid_ds. Ядро держит экземпляр этой структуры для каждой очереди, существующей в системе. IPC_STAT дает возможность заиметь копию такой структуры для испытаний. Посмотрим на оберточную функцию,которая берет эту структуру и размещает копию по указанному адресу.

Если копирование во внутренний буфер невозможно, то вызывающей функции возвращается -1. Если же все прошло нормально, то возвращается 0, и посланный буфер должен содержать копию внутренней структуры данных для очереди с идентификатором qid.

Что же мы можем делать с полученной копией структуры? Единственное, что можно поменять, это элемент ipc_perm. Это права доступа очереди, информация о создателе и владельце очереди. Однако и отсюда менять позволено только mode, uid и gid.

Давайте напишем оберточную функцию, изменяющую режим доступа очереди. Режим должен быть передан как массив литер (например, «660»).

Мы взяли текущую копию внутренней структуры данных посредством вызова нашей get_queue_ds; затем sscanf() меняет элемент mode структуры msg_perm. Однако ничего не произойдет, пока msgctl c IPC_SET не обновил внутреннюю версию.

ОСТОРОЖНО! Изменяя права доступа, можно случайно лишить прав себя самого! Помните, что IPC-объекты не исчезают, пока они не уничтожены должным образом или не перезагружена система. Поэтому то, что вы не видите очереди ipcs-ом, не означает, что ее нет на самом деле.

После того, как сообщение взято из очереди, оно удаляется. Однако, как отмечалось ранее, IPC-объекты остаются в системе до персонального удаления или перезапуска всей системы. Поэтому наша очередь сообщений все еще существует в ядре и пригодна к употреблению в любое время, несмотря на то, что последнее его соообщение уже давно на небесах. Чтобы и душа нашей очереди с миром отошла к богам, нужен вызов msgctl(), использующий команду IPC_RMID:

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

msgtool: интерактивный обработчик очередей сообщений


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

До сей поры все то хорошее, что мы сделали — это оберточные функции для манипуляций с очередями сообщений. Хотя они чрезвычайно полезны, ими неудобно пользоваться для дальнейшего обучения и экспериментов. Существует средство, позволяющее работать с IPC-очередями из командной строки — msgtool(). Хотя msgtool() будет использован в целях обучения, он пригодится и реально при написании скриптов.

Поведение msgtool()-а зависит от аргументов командной строки, что удобно для вызова из скрипта shell. Позволяет делать все что угодно, от создания, посылки и получения сообщений до редактирования прав доступа и удаления очереди. Изначально данными сообщений могут быть только литерные массивы. Упражнение — измените это так, чтобы можно было посылать и другие данные.

Следующее, что мы рассмотрим, это исходный текст msgtool. Его следует компилировать в версии системы, которая поддерживает System V IPC. Убедитесь в наличии System V IPC в ядре, когда будете пересобирать программу!

На полях отметим, что наша утилита будет всегда создавать очередь, если ее не было.

Замечание. Поскольку msgtool использует ftok() для генерации ключей IPC, вы можете нарваться на конфликты, связанные с директориями. Если вы где-то в скрипте меняете директории, то все это наверняка не сработает. Это обходится путем более явного указания пути в msgtool, вроде «/tmp/msgtool», или даже запроса пути из командной строки вместе с остальными аргументами.

10.7. Сигналы для межпроцессного взаимодействия

10.7. Сигналы для межпроцессного взаимодействия

«ЭТО УЖАСНАЯ МЫСЛЬ! СИГНАЛЫ НЕ ПРЕДНАЗНАЧЕНЫ ДЛЯ ЭТОГО! Просто скажите НЕТ».

— Джефф Колье (Geoff Collyer) —

Одним из главных механизмов межпроцессного взаимодействия (IPC) являются каналы, которые описаны в разделе 9.3 «Базовая межпроцессная коммуникация каналы и FIFO». Сигналы также можно использовать для очень простого IPC[111]. Это довольно грубо; получатель может лишь сказать, что поступил определенный сигнал. Хотя функция sigaction() позволяет получателю узнать PID и владельца процесса, пославшего сигнал, эти сведения обычно не очень помогают.

ЗАМЕЧАНИЕ. Как указывает цитата в начале, использование сигналов для IPC почти всегда является плохой мыслью. Мы рекомендуем по возможности избегать этого. Но нашей целью является научить вас, как использовать возможности Linux/Unix, включая их отрицательные моменты, оставляя за вами принятие информированного решения, что именно использовать.

Сигналы в качестве IPC для многих программ могут быть иногда единственным выбором. В частности, каналы не являются альтернативой, если две взаимодействующие программы не запущены общим родителем, а файлы FIFO могут не быть вариантом, если одна из взаимодействующих программ работает лишь со стандартными вводом и выводом. (Примером обычного использования сигналов являются определенные системные программы демонов, таких, как xinetd , которые принимают несколько сигналов, уведомляющих, что нужно повторно прочесть файл настроек, осуществить проверку непротиворечивости и т.д. См. xinetd(8) в системе GNU/Linux и inetd(8) в системе Unix.)

Типичная высокоуровневая структура основанного на сигналах приложения выглядит таким образом:

Оригинальным интерфейсом V7 для ожидания сигнала является pause() :

pause() приостанавливает процесс; она возвращается лишь после того, как сигнал будет доставлен и его обработчик вернется из вызова. По определению, pause() полезна лишь с перехваченными сигналами — игнорируемые сигналы при их появлении игнорируются, а сигналы с действием по умолчанию, завершающим процесс (с созданием файла образа или без него), продолжают действовать так же.

Илон Маск рекомендует:  Что такое код pdf_clip

Проблема в только что описанной высокоуровневой структуре приложения кроется в части «Обработка сигнала». Когда этот код запускается, вы не захотите обрабатывать другой сигнал; вы хотите завершить обработку текущего сигнала до перехода к следующему. Одним из возможных решений является структурирование обработчика сигнала таким образом, что он устанавливает флаг и проверяет его в главном цикле: volatile sig_atomic_t signal_waiting = 0; /* true, если не обрабатываются сигналы */

void handler(int sig) <

/* Установка других данных, указывающих вид сигнала */

В основном коде флаг проверяется:

pause(); /* этот код пропускается */

/* Определение поступившего сигнала */

К сожалению, этот код изобилует условиями гонки:

pause(); /* pause() будет вызвана в любом случае */

/* Определение поступившего сигнала

Решением является блокирование интересующего сигнала в любое время, кроме ожидания его появления. Например, предположим, что интересующим нас сигналом является SIGINT :

void handler(int sig) <

/* sig автоматически блокируется функцией sigaction() */

/* Установить глобальные данные, касающиеся этого сигнала */

int main(int argc, char **argv) <

struct sigaction act;

/* . обычная настройка, опции процесса и т.д. . */

sigemptyset(&set); /* Создать пустой набор */

sigaddset(&set, SIGINT); /* Добавить в набор SIGINT */

sigprocmask(SIG_BLOCK, &set, NULL); /* Заблокировать его */

act.sa_mask = set; /* Настроить обработчик */

sigaction(sig, &act, NULL); /* Установить его */

. /* Возможно, установить отдельные обработчики */

. /* для других сигналов */

sigemptyset(&set); /* Восстановить пустой, допускает SIGINT */

sigsuspend(&set); /* Ждать появления SIGINT */

/* Обработка сигнала. SIGINT здесь снова блокируется */

/* . любой другой код. */

Ключом к использованию этого является то, что sigsuspend() временно заменяет маску сигналов процесса маской, переданной в аргументе. Это дает SIGINT возможность появиться. При появлении он обрабатывается; обработчик сигнала возвращается, а вслед за ним возвращается также sigsuspend() . Ко времени возвращения sigsuspend() первоначальная маска процесса снова на месте.

Вы легко можете расширить этот пример для нескольких сигналов, блокируя в main() и в обработчике все интересующие сигналы и разблокируя их лишь в вызове sigsuspended() .

При наличии всего этого не следует в новом коде использовать pause() . pause() был стандартизован POSIX главным образом для поддержки старого кода. То же самое верно и для функции sigpause() System V Release 3. Вместо этого, если нужно структурировать свое приложение с использованием сигналов для IPC, используйте исключительно функции API sigsuspend() и sigaction() .

ЗАМЕЧАНИЕ. Приведенный выше код предполагает, что маска сигналов процесса начинается пустой. Код изделия должен вместо этого работать с любой маской сигналов, имеющейся на момент запуска программы.

Похожие главы из других книг

5.4.2. Единицы взаимодействия

Понятие удаленного взаимодействия .NET

Понятие удаленного взаимодействия .NET Вы должны помнить из главы 13, что домен приложения [AppDomain] задает логические границы выполнения компоновочного блока .NET в рамках процесса Win32. Понимание этого очень важно для дальнейшего обсуждения распределенных приложений .NET,

Каркас удаленного взаимодействия .NET

Каркас удаленного взаимодействия .NET Когда клиенты и серверы обмениваются информацией через границы приложений, среда CLR вынуждена использовать низкоуровневые примитивы, обеспечивающие настолько «прозрачное» взаимодействие сторон, насколько это возможно. Это значит,

Термины удаленного взаимодействия .NET

Термины удаленного взаимодействия .NET Подобно любой новой парадигме, удаленное взаимодействие .NET предлагает свой собственный набор трехбуквенных акронимов. Поэтому, перед тем как рассмотреть первый пример программного кода, нам с вами придется определить несколько

5.5. Диаграммы взаимодействия

5.5. Диаграммы взаимодействия Существенное: объекты и их взаимодействия Диаграмма взаимодействии используется, чтобы проследить выполнение сценария в том же контексте, что и диаграмма объектов [Эти диаграммы обобщают диаграммы трассировки событий Румбаха и диаграммы

Модель сетевого взаимодействия OSI

Модель сетевого взаимодействия OSI В основу работы стека протоколов положена модель OSI (Open System Interconnection — взаимодействие открытых систем). Данная модель предусматривает семь уровней сетевого взаимодействия, на каждом из которых решаются конкретные задачи. Источником

Пример сеанса взаимодействия по протоколу POP

Настройка X-сервера для взаимодействия с X-клиентом

Основы межпрограммного взаимодействия

Глава 5. Изучение взаимодействия объектов

Глава 5. Изучение взаимодействия объектов Реализация прецедентовДиаграмма прецедентов представляет внешний вид системы. Выполнение прецедентов отображается с помощью потока событий. Сценарии используются для описания того, как реализуются прецеденты, взаимодействуя

6.1.2. Уровни взаимодействия OSI

6.1.2. Уровни взаимодействия OSI Физический уровень (Physical Layer)Физический уровень передает биты по физическим каналам связи, например, коаксиальному кабелю или витой паре. На этом уровне определяются характеристики электрических сигналов, которые передают дискретную

26.1. Способы взаимодействия

26.1. Способы взаимодействия Процессы, как и люди, могут «общаться» между собой, то есть обмениваться информацией. В главе 3 мы бегло рассмотрели два средства межпроцессного взаимодействия (IPC, Inter-Process Communication); полудуплексные каналы (конвейеры) и сигналы, но в UNIX-системах

Построение взаимодействия

Построение взаимодействия Мы изучили довольно простой пример, добавляя различные свойства CSS3 к элементам нашей страницы, которые относятся исключительно к взаимодействию. Браузеры, поддерживающие эти свойства, будут показывать анимацию полупрозрачного фона

Сравнение различных систем межпроцессного взаимодействия

Сравнение различных систем межпроцессного взаимодействия Заканчивая разговор о межпроцессном взаимодействии в UNIX, приведем сводную сравнительную таблицу рассмотренных систем. Каналы FIFO Сообщения Разделяемая память Сокеты (домен UNIX) Пространство имен — Имя


Общая модель сетевого взаимодействия OSI

Общая модель сетевого взаимодействия OSI При знакомстве с семейством протоколов TCP/IP мы отметили уровневую структуру этих протоколов. Каждый из уровней выполняет строго определенную функцию, изолируя в то же время особенности этой обработки и связанные с ней данные от

2.4 Разработка dll-библиотеки для взаимодействия с драйвером

2.4 Разработка dll-библиотеки для взаимодействия с драйвером dll-библиотека (Dynamic Link Library) — программный модуль, который может быть динамически подключен к выполняющемуся процессу. Dll–библиотека может содержать функции и данные. При подключении dll к процессу она

Национальная библиотека им. Н. Э. Баумана
Bauman National Library

Персональные инструменты

Межпроцессное взаимодействие (Операционные Системы)

Межпроцессное взаимодействие (англ. inter-process communication, IPC ) — обмен данными между потоками одного или разных процессов. Реализуется посредством механизмов, предоставляемых ядром ОС или процессом, использующим механизмы ОС и реализующим новые возможности IPC. Может осуществляться как на одном компьютере, так и между несколькими компьютерами сети [1] .

Содержание

Общие положения

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

В идеальной многозадачной системе все процессы, выполняющиеся параллельно, независимы друг от друга (т.е., асинхронны). На практике такая ситуация маловероятна, поскольку рано или поздно возникают ситуации, когда параллельным процессам необходим доступ к некоторым общим ресурсам. Для этого необходимо введение на уровне ОС средств, предоставляющих такую возможность.

При выполнении параллельных процессов может возникать проблема, когда каждый процесс, обращающийся к разделяемым данным, исключает для всех других процессов возможность одновременного с ним обращения к этим данным — это называется взаимоисключением (mutual exclusion).

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

Участки процесса, в которых происходит обращение к кри¬тическим ресурсам, называются критическими участками. Для организации коммуникации между одновременно работающими процессами применяются средства межпроцессного взаимодействия (Interprocess Communication — IPC).

Выделяются три уровня средств IPC:

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

Удаленные IPC предоставляют механизмы, которые обеспечивают взаимодействие как в пределах одного процессора, так и между программами на различных процессорах, соединенных через сеть. Сюда относятся удаленные вызовы процедур (Remote Procedure Calls — RPC), сокеты Unix, а также TLI (Transport Layer Interface — интерфейс транспортного уровня) фирмы Sun [2] .

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

Наряду с обеспечением взаимодействия процессов средства IPC призваны решать проблемы, возникающие при организации параллельных вычислений. Сюда относятся:

  • Синхронный доступ. Если все процессы считывают данные из файла, то в большинстве случае проблем не возникает. Однако, при попытке одним из процессов изменить этот файл, могут возникнуть так называемые конкурентные ситуации (race conditions).
  • Дисциплина доступа. Если нужно, чтобы как можно большее количество пользователей могли записывать данные, организуется так называемая очередь (по правилу один пишет, несколько читают). Практически организуется две очереди: одна — для чтения, другая — для записи. Такую дисциплину доступа можно организовать с помощью барьеров (блокировок). При этом создается общий барьер для считывателей, так как несколько процессов могут одновременно считывать данные, а также отдельный барьер для процесса-писателя. Такая организация предотвращает взаимные помехи в работе.
  • Голодание процессов. Организация дисциплины доступа может привести к ситуации, когда процесс будет длительно ждать своей очереди для записи данных. Поэтому иногда нужно организовывать очереди с приоритетами.
  • Управление потоком. Если нельзя точно определить, какой из процессов запрашивает или возвращает свои данные в нужный компьютер первым, используется так называемое взаимодействие по модели «клиент-сервер». При этом используются один или несколько клиентов и один сервер. Клиент посылает запрос серверу, а сервер отвечает на него. После этого клиент должен дождаться ответа сервера, чтобы продолжать дальнейшее взаимодействие. Такое поведение называется управлением потоком. При одновременном доступе здесь также могут использоваться очереди с приоритетами [3] .
  • Тупик (deadlock). Классический тупик возникает, если процесс A получает доступ к файлу A и ждет освобождения файла B. Одновременно процесс B, получив доступ к файлу B, ждет освобождения файла A. Оба процесса теперь ждут освобождения ресурсов другого процесса и не освобождают при этом собственный файл.

Тупика можно избежать, если ресурсы будут пронумерованы и предоставляться только в восходящем порядке номеров. Кроме того, все ресурсы должны освобождаться сразу после окончания использования.

Каналы

Канал (pipe) представляет собой средство связи стандартного вывода одного процесса со стандартным вводом другого. Каналы старейший из инструментов IPC, существующий приблизительно со времени появления самых ранних версий оперативной системы UNIX.

Для реализации IPC возможно использование полудуплексных и/или именованных каналов (FIFO).

Полудуплексные каналы

Когда процесс создает канал, ядро устанавливает два файловых дескриптора для пользования этим каналом. Один такой дескриптор используется, чтобы открыть путь ввода в канал (запись), в то время как другой применяется для получения данных из канала (чтение). В этом смысле, канал мало применим практически, так как создающий его процесс может использовать канал только для взаимодействия с самим собой. Рассмотрим следующее изображение процесса и ядра после создания канала:

Из этого рисунка легко увидеть, как файловые дескрипторы связаны друг с другом. Если процесс посылает данные через канал (fd0), он имеет возможность получить эту информацию из fd1. Однако этот простенький рисунок отображает и более глобальную задачу. Хотя канал первоначально связывает процесс с самим собой, данные, идущие через канал, проходят через ядро. В частности, в Linux каналы внутренне представлены корректным inode. Конечно, этот inode существует в пределах самого ядра, а не в какой-либо физической файловой системе. Эта особенность откроет нам некоторые привелекательные возможности для ввода/вывода, как мы увидим немного позже. Зачем же нам неприятности с созданием канала, если мы всего-навсего собираемся поговорить сами с собой? На самом деле, процесс, создающий канал, обычно порождает дочерний процесс. Как только дочерний процесс унаследует какой-нибудь открытый файловый дескриптор от родителя, мы получаем базу для мультипроцессовой коммуникации (между родителем и потомком). Рассмотрим эту измененную версию нашего рисунка:

Теперь мы видим, что оба процесса имеют доступ к файловым дескрипторам, которые основывают канал. На этой стадии должно быть принято критическое решение. В каком направлении мы хотим запустить данные? Потомок посылает информацию к родителю или наоборот? Два процесса взаимно согласовываются и «закрывают» неиспользуемый конец канала. Пусть потомок выполняет несколько действий и посылает информацию родителю обратно через канал. Наш новый рисунок выглядел бы примерно так:

Конструкция канала теперь полная. Чтобы использовать его, можно применять системные вызовы, подобные тем, которые нужны для ввода/вывода в файл или из файла на низком уровне (вспомним, что в действительности каналы внутренне представлены как корректный inode). Это означает, что для чтения из канала можно использовать вызов read(), а для записи — write(). Примечания:

  • Двусторонние каналы могут быть созданы посредством открывания двух каналов и правильным переопределением файловых дескрипторов в процессе-потомке.
  • Вызов pipe() должен быть произведен ПЕРЕД вызовом fork(), или дескрипторы не будут унаследованы процессом-потомком! (то же для popen()).
  • С полудуплексными каналами любые связанные процессы должны разделять происхождение. Поскольку канал находится в пределах ядра, любой процесс, не состоящий в родстве с создателем канала, не имеет способа адресовать его. Это не относится к случаю с именованными каналами (FIFOS).

Именованные каналы (FIFO: First In First Out)

Именованные каналы во многом работают так же, как и обычные каналы, но все же имеют несколько заметных отличий:

  • Именованные каналы существуют в виде специального файла устройства в файловой системе.
  • Процессы различного происхождения могут разделять данные через такой канал.
  • Именованный канал остается в файловой системе для дальнейшего использования и после того, как весь ввод/вывод сделан.

Операции ввода/вывода с FIFO, по существу, такие же, как для обычных каналов, за одним исключением. Чтобы физически открыть проход к каналу, должен быть использован системный вызов «open» или библиотечная функция. С полудуплексными каналами это невозможно, поскольку канал находится в ядре, а не в физической файловой системе.

Если FIFO открыт для чтения, процесс его блокирует до тех пор, пока какой-нибудь другой процесс не откроет FIFO для записи.

Сигналы

Сигналы являются программными прерываниями, которые посылаются процессу, когда случается некоторое событие. Сигналы могут возникать синхронно с ошибкой в приложении, например SIGFPE (ошибка вычислений с плавающей запятой) и SIGSEGV (ошибка адресации), но большинство сигналов является асинхронными. Сигналы могут посылаться процессу, если система обнаруживает программное событие, например, когда пользователь дает команду прервать или остановить выполнение, или получен сигнал на завершение от другого процесса. Сигналы могут прийти непосредственно от ядра ОС, когда возникает сбой аппаратных средств ЭВМ. Система определяет набор сигналов, которые могут быть отправлены процессу. При этом каждый сигнал имеет целочисленное значение и приводит к строго определенным действиям.

Механизм передачи сигналов состоит из следующих частей:

  • установление и обозначение сигналов в форме целочисленных значений;
  • маркер в строке таблицы процессов для прибывших сигналов;
  • таблица с адресами функций, которые определяют реакцию на прибывающие сигналы.

Отдельные сигналы подразделяются на три класса:

  • системные сигналы (ошибка аппаратуры, системная ошибка и т.д.);
  • сигналы от устройств;
  • сигналы, определенные пользователем.

Как только сигнал приходит, он отмечается записью в таблице процессов. Если этот сигнал предназначен для процесса, то по таблице указателей функций в структуре описания процесса выясняется, как нужно реагировать на этот сигнал. При этом номер сигнала служит индексом таблицы.

Известно три варианта реакции на сигналы:

  • вызов собственной функции обработки;
  • игнорирование сигнала (не работает для SIGKILL);
  • использование предварительно установленной функции обработки по умолчанию.

Чтобы реагировать на разные сигналы, необходимо знать концепции их обработки. Процесс должен организовать так называемый обработчик сигнала в случае его прихода.

Очереди сообщений

Очереди сообщений представляют собой связный список в адресном пространстве ядра. Сообщения могут посылаться в очередь по порядку и доставаться из очереди несколькими разными путями. Каждая очередь сообщений однозначно определена идентификатором IPC. Очереди сообщений как средство межпроцессной связи позволяют процессам взаимодействовать, обмениваясь данными. Данные передаются между процессами дискретными порциями, называемымисообщениями. Процессы, использующие этот тип межпроцессной связи, могут выполнять две операции:послать или принять сообщение.

Процесс, прежде чем послать или принять какое-либо сообщение, должен запросить систему породить программные механизмы, необходимые для обработки данных операций. Процесс делает это при помощи системного вызова msgget. Обратившись к нему, процесс становится владельцем или создателем некоторого средства обмена сообщениями; кроме того, процесс специфицирует первоначальные права на выполнение операций для всех процессов, включая себя. Впоследствии владелец может уступить право собственности или изменить права на операции при помощи системного вызова msgctl, однако на протяжении всего времени существования определенного средства обмена сообщениями его создатель остается создателем. Другие процессы, обладающие соответствующими правами для выполнения различных управляющих действий, также могут использовать системный вызов msgctl.

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

Процесс, выполняющий операцию с блокировкой, может быть приостановлен до тех пор, пока не будет удовлетворено одно из условий:

  • операция завершилась успешно;
  • процесс получил сигнал;
  • очередь сообщений ликвидирована.

Системные вызовы позволяют процессам пользоваться этими возможностями обмена сообщениями. Вызывающий процесс передает системному вызову аргументы, а системный вызов выполняет (успешно или нет) свою функцию. Если системный вызов завершается успешно, он выполняет то, что от него требуется, и возвращает некоторую содержательную информацию. В противном случае процессу возвращается значение -1, известное как признак ошибки, а внешней переменной errno присваивается код ошибки.

Семафоры

Семафоры являются одним из классических примитивов синхронизации. Семафор (semaphore) — это целая переменная, значение которой можно опрашивать и менять только при помощи неделимых (атомарных) операций. Двоичный семафор может принимать только значения 0 или 1. Считающий семафор может принимать целые неотрицательные значения. В приложениях как правило требуется использование более одного семафора, ОС должна представлять возможность создавать множества семафоров.

Над каждым семафором, принадлежащим некоторому множеству, можно выполнить любую из трех операций:

  • увеличить значение;
  • уменьшить значение;
  • дождаться обнуления.

Для выполнения первых двух операций у процесса должно быть право на изменение, для выполнения третьей достаточно права на чтение. Чтобы увеличить значение семафора, системному вызову semop следует передать требуемое число. Чтобы уменьшить значение семафора, нужно передать требуемое число, взятое с обратным знаком; если результат получается отрицательным, операция не может быть успешно выполнена. Для третьей операции нужно передать 0; если текущее значение семафора отлично от нуля, операция не может быть успешно выполнена.

Разделяемая память

Разделяемая память может быть наилучшим образом описана как отображение участка (сегмента) памяти, которая будет разделена между более чем одним процессом. Это гораздо более быстрая форма IPC, потому что здесь нет никакого посредничества (т.е. каналов, очередей сообщений и т.п.). Вместо этого, информация отображается непосредственно из сегмента памяти в адресное пространство вызывающего процесса. Сегмент может быть создан одним процессом и впоследствии использован для чтения/записи любым количеством процессов.

Сокеты

Сокеты обеспечивают двухстороннюю связь типа «точка-точка между двумя процессами. Они являются основными компонентами межсистемной и межпроцессной связи. Каждый сокет представляет собой конечную точку связи, с которой может быть совмещено некоторое имя. Он имеет определенный тип, и один процесс или несколько, связанных с ним процессов.

Основные типы сокетов

Поточный — обеспечивает двухсторонний, последовательный, надежный, и недублированный поток данных без определенных границ. Тип сокета — SOCK_STREAM, в домене Интернета он использует протокол TCP.

Датаграммный— поддерживает двухсторонний поток сообщений. Приложение, использующее такие сокеты, может получать сообщения в порядке, отличном от последовательности, в которой эти сообщения посылались. Тип сокета — SOCK_DGRAM, в домене Интернета он использует протокол UDP.

Сокет последовательных пакетов — обеспечивает двухсторонний, последовательный, надежный обмен датаграммами фиксированной максимальной длины. Тип сокета — SOCK_SEQPACKET. Для этого типа сокета не существует специального протокола.

Простой сокет — обеспечивает доступ к основным протоколам связи.

Средства межпроцессного обмена данными

Средства межпроцессной синхронизации.

СРЕДСТВА МЕЖПРОЦЕССНОГО ВЗАИМОДЕЙСТВИЯ

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

Во многих операционных системах эти средства называются средствами межпроцессного взаимодействия — Inter Process Communications (IPC), что отражает историческую первичность понятия «процесс» по отношению к понятию «поток».

Сначала появилась необходимость в общении процессов, выполняющихся на одном компьютере. В дальнейшем с бурным развитием сетевых технологий все острее стала чувствоваться потребность в средствах для взаимодействия процессов, выполняющихся на разных компьютерах в сети. Особенно трудна такая задача, если это компьютеры на базе разных платформ и/или с разными операционными системами.

Средств, обеспечивающих взаимодействие между процессами, создано достаточно много. Огромное их количество было реализовано в Windows 9x, еще больше — в Windows XP. Классифицировать их, в общем случае, можно на два вида (рис.1):

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

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

Любое взаимодействие процессов или потоков связано с ихсинхронизацией, ко­торая заключается в согласовании скоростей потоков путем приостановки потока до наступления некоторого события и последующей его активизации при наступ­лении этого события.Синхронизация лежит в основе любого взаимодействия потоков, связано ли это взаимодействие с разделением ресурсов или с обменом данными. При совместном использовании аппаратных ресурсов синхронизация также со­вершенно необходима.

Таким образом, потоки должны взаимодействовать друг с другом в двух основных случаях:

− совместно используя разделяемый ресурс (чтобы не разрушить его);

− когда нужно уведомлять другие потоки о завершении каких-либо операций.

Для синхронизации потоков прикладных программ программист может исполь­зовать как собственные средства и приемы синхронизации, так и средства опера­ционной системы.

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

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

Таким образом, для совместной работы потоков без разрушения памяти в интерфейсе Win API предоставляются объекты синхронизации. Объекты синхронизации обеспечивают доступ к системным ресурсам, которые могут находится под управлением потоков одних и тех же либо других процессов.

Обычно для синхронизации используются шесть типов объектов (рис.1):

− критические секции (разделы);

− исключающие семафоры (объекты типа mutex);

Все объекты синхронизации отличаются друг от друга условием установки состояний.

Объект синхронизации обладает двумя состояниями:

— сигнальным (signaled state);

— несигнальным (non signaled state).

Когда объект синхронизации находится в состоянии занятости, или несигнальном состоянии, выполнение ожидающего потока НЕ может быть продолжено. А когда объект синхронизации оказывается в сигнальном состоянии, ожидающий поток может продолжить свое выполнение.

Функции ожидания представляют собой набор вызовов API, которые приостанавливают выполнение потоков до тех пор, пока не станет истинным заданный ряд условий. Функции ожидания проверяют сигнальное состояние объектов синхронизации.

Если указанный объект оказывается в сигнальном состоянии, функция ожидания завершается, а выполнение потока продолжается. В противном случае функция ожидания будет поддерживать поток в цикле, опрашивая состояние объекта синхронизации до тех пор, пока:

— оно не станет сигнальным;

— не истечет время ожидания.

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

Другое дело — обмен данными потоков, выполняющихся в рамках разных процессов. Для защиты процессов друг от друга ОС возводит мощные изолирующие преграды, которые не только защищают процессы, но и не позволяют им передавать друг другу данные. Потоки разных процессов работают в разных адресных пространствах. Однако операционная система имеет доступ ко всем областям памяти, поэтому она может играть роль посредника в информационном обмене прикладных потоков. При возникновении необходимости в обмене данными поток обращается с запросом к ОС. По этому запросу ОС, пользуясь своими привилегиями, создает различные системные средства связи, такие, например, как каналы или очереди сообщений.

Эти средства, так же как и рассмотренные выше средства синхронизации процессов, относятся к классу средств межпроцессного взаимодействия, то есть IPC (Inter Process Communications).

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

Таким образом, IPC становится необходим в том случае, если поток одного процесса должен передать данные потоку другого процесса.

Не нашли то, что искали? Воспользуйтесь поиском:

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL