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

Содержание

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

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

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

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

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

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

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

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

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

Например, если ресурс занят, то процесс не выполняет циклический опрос, а вызывает некоторую системную функцию wait(D), где D обозначает событие, заключающееся в освобождении ресурса D. Функция wait(D) переводит активный процесс в состояние «ожидание» и делает отметку в его дескрипторе о том, что процесс ожидает события D. Процесс, который в это время использует ресурс D, после выхода из критической секции выполняет системную функцию post(D), в результате чего операционная система просматривает очередь ожидающих процессов и переводит процесс, ожидающий события D, в состояние «готовность».

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

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

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

Часто используется упрощенная версия семафора, называемая мьютексом (от англоязычного термина mutex, происходящего от сокращения словосочетания mutual exclusion – «взаимное исключение»).

Мьютекс не может считать сигналы, а может лишь управлять взаимным исключением доступа к совместно используемым ресурсам. Реализация мьютекса проста и эффективна. Мьютекс является переменной, которая может находиться в одном из двух состояний: блокированном или неблокированном. Поэтому для описания мьютекса требует­ся всего один бит, хотя чаще используется целая переменная, у которой 0 означает неблокированное состояние, а все остальные значения соответствуют блокирован­ному состоянию. Значение мьютекса устанавливается двумя процедурами. Если процесс собирается войти в критическую секцию, он вызывает проце­дуру mutex_lock. Если мьютекс не заблокирован (то есть вход в критическую секцию разрешен), запрос выполняется и вызывающий процесс может попасть в критическую секцию. Напротив, если мьютекс заблокирован, вызывающий процесс блокируется до тех пор, пока другой процесс, находящийся к критической секции, не выйдет из нее, вызвав процедуру mutex_unlock. Если мьютекс блокирует несколько процессов, то из них случайным образом выбирается один.

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

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

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

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

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

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

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

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

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

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

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

Из механизмов, предоставляемых ОС и используемых для IPC, можно выделить:

— механизмы обмена сообщениями;

— механизмы разделения памяти;

Механизмы удалённых вызовов (RPC).

— механизмы синхронизации;

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

— пропускная способность (количество сообщений в единицу времени, которое ядро ОС или процесс способна обработать);

— задержки (время между отправкой сообщения одним потоком и его получением другим потоком).

— IPC может называться терминами «межпотоковое взаимодействие» (англ. inter-thread communication) и «межпрограммное взаимодействие» (англ. inter-application communication).

Межпроцессное взаимодействие, наряду с механизмами адресации памяти, является основой для разграничения адресного пространства между процессами.

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

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

Удалённый вызов процедур, (Remote Procedure Call, RPC) — класс технологий, позволяющих вызывать функции или процедуры в другом адресном пространстве (как правило, на удалённых компьютерах). Обычно реализация RPC технологии включает в себя два компонента: сетевой протокол для обмена в режиме клиент-сервер и язык сериализации объектов (или структур, для необъектных RPC). Различные реализации RPC имеют очень отличающуюся друг от друга архитектуру и разнятся в своих возможностях.

Синхронизация процессов и потоков

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

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

Гонка — когда два или более потоков обрабатывают разделяемые данные и конечный результат зависит от соотношения скоростей потоков.

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

Критическая секция

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

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

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

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

Блокирующие переменные

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

Каждому набору критических данных ставится в соответствие двоичная переменная, которой поток присваивает значение 0, когда он входит в критическую секцию, и значение 1, когда он ее покидает.

Блокирующие переменные могут использоваться не только при доступе к разделяемым данным, но и при доступе к разделяемым ресурсам любого вида.

Управление памятью

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

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

Функциями ОС по управлению памятью в мультипрограммной системе являются:

— отслеживание свободной и занятой памяти;

— выделение памяти процессам и освобождение памяти по завершении процессов;

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

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

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

Типы адресации

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

— Символьные имена присваивает пользователь при написании программы на алгоритмическом языке или ассемблере.

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

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

Совокупность виртуальных адресов процесса называется виртуальным адресным пространством.

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

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

В разных операционных системах используются разные способы структуризации виртуального адресного пространства:

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

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

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

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

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

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

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

Илон Маск рекомендует:  CurrencyString - Переменная Delphi

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

| следующая лекция ==>
Управление процессами и потоками | Алгоритмы управления виртуальной памятью

Дата добавления: 2020-10-14 ; просмотров: 240 | Нарушение авторских прав

Межпроцессное взаимодействие в 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 ; просмотров: 732 ; ЗАКАЗАТЬ НАПИСАНИЕ РАБОТЫ

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

Каналы

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

К числу наиболее простых и в то же время самых употребительных средств межпроцессного взаимодействия принадлежат каналы , представляемые файлами соответствующего типа. Стандарт POSIX-2001 различает именованные и безымянные каналы . Напомним, что первые создаются функцией mkfifo () и одноименной служебной программой, а вторые — функцией pipe() . Именованным каналам соответствуют элементы файловой системы, ко вторым можно обращаться только посредством файловых дескрипторов . В остальном эти разновидности каналов эквивалентны.

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

Решение той же задачи, но с использованием функций буферизованного ввода/вывода , показано в листинге 8.2.

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

Подчеркнем, что при попытке чтения из пустого канала результат равен 0 (как признак конца файла ), только если канал не открыт кем-либо на запись . Под «кем-либо» понимается и сам читающий процесс; по этой причине в приведенной выше программе потребовалось закрыть все экземпляры файлового дескриптора fd [1] , возвращенного функцией pipe() как дескриптор для записи в канал .

Функция popen() , описанная выше, при рассмотрении командного интерпретатора, является более высокоуровневой по сравнению с pipe() . Она делает сразу несколько вещей: порождает процесс, обеспечивает выполнение в его рамках заданной команды, организует канал между вызывающим и порожденным процессами и формирует необходимые потоки для этого канала . Если при обращении к popen() задан режим » w «, то стандартный ввод команды, выполняющейся в рамках порожденного процесса, перенаправляется на конец канала , предназначенный для чтения; если задан режим » r «, то в канал перенаправляется стандартный вывод .

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

Функция pclose() не только закрывает поток , сформированный popen() , но и дожидается завершения порожденного процесса, возвращая его статус.

Типичное применение popen() — организация канала для выдачи динамически порождаемых данных на устройство печати командой lp (см. листинг 8.4).

Сходным образом можно организовать канал для чтения результатов выполнения команды (см. листинг 8.5).

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

Wikimedia Foundation . 2010 .

Смотреть что такое «Межпроцессное взаимодействие» в других словарях:

Unix domain socket — (Доменный сокет Unix) или IPC сокет (сокет межпроцессного взаимодействия) конечная точка обмена данными, схожая с Интернет сокетом, но не использующая сетевой протокол для взаимодействия (обмена данными). Он используется в операционных системах,… … Википедия

Сокет (программный интерфейс) — У этого термина существуют и другие значения, см. Сокет. Сокеты (англ. socket углубление, гнездо, разъём) название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на… … Википедия

Именованный канал — … Википедия

mmap — mmap POSIX совместимый системный вызов Unix, позволяющий выполнить отображение файла или устройства на память. Является методом ввода/вывода через отображение файла на память и естественным образом реализует выделение страниц по запросу,… … Википедия

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

Сравнение командных оболочек — Подробнее по этой теме см.: Оболочка операционной системы. Командная оболочка это компьютерная программа с интерфейсом командной строки операционной системы. Содержание 1 Общие характеристики 2 Интеракти … Википедия

Windows Script Host — (WSH; первоначально назывался Windows Scripting Host, был переименован ко второму выпуску) компонент Microsoft Windows, предназначенный для запуска сценариев на скриптовых языках JScript и VBScript, а также и на других дополнительно… … Википедия

NDIS — (аббр.. от англ. Network Driver Interface Specification) спецификация интерфейса сетевого драйвера, была разработана совместно фирмами Microsoft и 3Com для сопряжения драйверов сетевых адаптеров с операционной системой. Одна из первых… … Википедия

FIFO — У этого термина существуют и другие значения, см. FIFO и LIFO. FIFO планировщик проц … Википедия

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

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

Из механизмов, предоставляемых ОС и используемых для IPC, можно выделить:

* механизмы обмена сообщениями;

* механизмы разделения памяти;

механизмы удалённых вызовов (RPC).Для оценки производительности различных механизмов IPC используют следующие параметры:

* пропускная способность (количество сообщений в единицу времени, которое ядро ОС или процесс способно обработать);

задержки (время между отправкой сообщения одним потоком и его получением другим потоком).IPC может называться терминами межпотоковое взаимодействие (англ. inter-thread communication) и межпрограммное взаимодействие (англ. inter-application communication).

Межпроцессное взаимодействие, наряду с механизмами адресации памяти, является основой для разграничения адресного пространства между процессами.

Связанные понятия

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

Операционные системы используют менеджеры блокировок (англ.) для организации и координации доступа к ресурсам. Распределенный менеджер блокировок (англ. Distributed lock manager, DLM, ) работает на каждой машине в кластере, с идентичной копией базы данных блокировок кластера. Таким образом, DLM является пакетом программного обеспечения, который позволяет компьютерам в кластере координировать доступ к совместно используемым ресурсам .

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

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

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

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

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

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

Межпроцессное взаимодействие (англ. Inter-Process Communication, IPC) — набор способов обмена данными между множеством потоков в одном или более процессах. Процессы могут быть запущены на одном или более компьютерах, связанных между собой сетью. IPC-способы делятся на методы обмена сообщениями, синхронизации, разделяемой памяти и удаленных вызовов (RPC). Методы IPC зависят от пропускной способности и задержки взаимодействия между потоками и типа передаваемых данных.

IPC также может упоминаться как межпотоковое взаимодействие (англ. inter-thread communication), межпоточное взаимодействие и межпрограммное взаимодействие (англ. inter-application communication).

IPC наряду с концепцией адресного пространства является основой для разграничения адресного пространства.

Топология

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

Точка-точка

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

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

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

Хаб + спицы

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

· организация маршрутизации взаимодействия между интегрированными приложениями;

· преобразование форматов файлов и данных;

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

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

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

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

порт сервер датаграмма encapsulation

Мы не будем вдаваться в детали реализации протоколов транспортного уровня, а лишь кратко рассмотрим их основные характеристики. К протоколам транспортного уровня относятся протоколы TCP и UDP.

Протокол TCP реализует потоковую модель передачи информации, хотя в его основе, как и в основе протокола UDP, лежит обмен информацией через пакеты данных. Он представляет собой ориентированный на установление логической связи (connection-oriented), надежный (обеспечивающий проверку контрольных сумм, передачу подтверждения в случае правильного приема сообщения, повторную передачу пакета данных в случае неполучения подтверждения в течение определенного промежутка времени, правильную последовательность получения информации, полный контроль скорости передачи данных) дуплексный способ связи между процессами в сети. Протокол UDP, наоборот, является способом связи ненадежным, ориентированным на передачу сообщений (датаграмм). От протокола IP он отличается двумя основными чертами: использованием для проверки правильности принятого сообщения контрольной суммы, насчитанной по всему сообщению, и передачей информации не от узла сети к другому узлу, а от отправителя к получателю.

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

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

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

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

Для каждого транспортного протокола в стеке TCP/IP существуют собственные сокеты: UDP сокеты и TCP сокеты, имеющие различные адресные пространства своих локальных адресов — портов. В семействе протоколов TCP/IP адресные пространства портов представляют собой положительные значения целого 16-битового числа. Поэтому, говоря о локальном адресе сокета, мы часто будем использовать термин «номер порта». Из различия адресных пространств портов следует, что порт 1111 TCP — это совсем не тот же самый локальный адрес, что и порт 1111 UDP. О том, как назначаются номера портов различным сокетам, мы поговорим позже.

Итак, мы описали иерархическую систему адресации, используемую в семействе протоколов TCP/IP, которая включает в себя несколько уровней:

  • · Физический пакет данных, передаваемый по сети, содержит физические адреса узлов сети (MAC-адреса) с указанием на то, какой протокол уровня Internet должен использоваться для обработки передаваемых данных (поскольку пользователя интересуют только данные, доставляемые затем на уровень приложений / процессов, то для него это всегда IP).
  • · IP-пакет данных содержит 32-битовые IP-адреса компьютера-отправителя и компьютера-получателя, и указание на то, какой вышележащий протокол (TCP, UDP или еще что-нибудь) должен использоваться для их дальнейшей обработки.
  • · Служебная информация транспортных протоколов (UDP-заголовок к данным и TCP-заголовок к данным) должна содержать 16-битовые номера портов для сокета отправителя и сокета получателя.

Добавление необходимой информации к данным при переходе от верхних уровней семейства протоколов к нижним принято называть английским словом encapsulation (дословно: герметизация). На рисунке 1. приведена схема encapsulation при использовании протокола UDP на сети Ethernet.

Рис. 1. Encapsulation для UDP-протокола на сети Ethernet

Поскольку между MAC-адресами и IP-адресами существует взаимно однозначное соответствие, известное семейству протоколов TCP/IP, то фактически для полного задания адреса доставки и адреса отправления, необходимых для установления двусторонней связи, нужно указать пять параметров: транспортный протокол, IP-адрес отправителя, порт отправителя, IP-адрес получателя, порт получателя.

Использование модели клиент-сервер для взаимодействия удаленных процессов

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

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

Неравноправность процессов в модели клиент-сервер, как мы увидим далее, накладывает свой отпечаток на программный интерфейс, используемый между уровнем приложений / процессов и транспортным уровнем.

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

Илон Маск рекомендует:  Функции mcal

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

Организация связи между удаленными процессами с помощью датаграмм

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

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

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

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

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

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

Создание сокета производится с помощью системного вызова socket(). Для привязки созданного сокета к IP-адресу и номеру порта (настройка адреса) служит системный вызов bind(). Ожиданию получения информации, ее чтению и, при необходимости, определению адреса отправителя соответствует системный вызов recvfrom(). За отправку датаграммы отвечает системный вызов sendto().

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

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

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

Рубрика Программирование, компьютеры и кибернетика
Вид лабораторная работа
Язык русский
Дата добавления 27.11.2013
Размер файла 68,4 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

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

Размещено на http://www.allbest.ru/

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

1.Теоретическая часть. Средства межпроцессного взаимодействия

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

1.5 Разделяемые сегменты памяти

канал сигнал очередь сообщений память

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

1. Теоретическая часть. Средства межпроцессного взаимодействия.

1.1 Каналы

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

К числу наиболее простых и в то же время самых употребительных средств межпроцессного взаимодействия принадлежат каналы, представляемые файлами соответствующего типа. Стандарт POSIX-2001 различает именованные и безымянные каналы. Напомним, что первые создаются функцией mkfifo() и одноименной служебной программой, а вторые — функцией pipe(). Именованным каналам соответствуют элементы файловой системы, ко вторым можно обращаться только посредством файловых дескрипторов. В остальном эти разновидности каналов эквивалентны.

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

/* Программа копирует строки со стандартного ввода на стандартный вывод, */

/* «прокачивая» их через канал. */

/* Используются функции ввода/вывода нижнего уровня */

#define MY_PROMPT «Вводите строки\n»

#define MY_MSG «Вы ввели: «

int new_line = 1; /* Признак того, что надо выдать сообщение MY_MSG */

/* перед отображением очередной строки */

/* Создадим безымянный канал */

/* Программа копирует строки со стандартного ввода на стандартный вывод, */

/* «прокачивая» их через канал. */

/* Используются функции буферизованного ввода/вывода */

char line [LINE_MAX];

/* Создадим безымянный канал */

int pclose (FILE *stream);

Листинг 8.3. Описание функции pclose().

Функция pclose() не только закрывает поток, сформированный popen(), но и дожидается завершения порожденного процесса, возвращая его статус.

Типичное применение popen() — организация канала для выдачи динамически порождаемых данных на устройство печати командой lp (пример 8.4).

/* Программа печатает несколько первых строк треугольника Паскаля */

#define T_SIZE 16

long tp [T_SIZE]; /* Массив для хранения текущей строки треугольника */

/* Инициализируем массив, чтобы далее все элементы */

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

/* Вывод строки треугольника в канал */

#define MY_CMD «ls -l *.c»

char line [LINE_MAX];

assert ((inptr = popen (MY_CMD, «r»)) != NULL);

while (fgets (line, sizeof (line), inptr) != NULL) <

fputs (line, stdout);

return (pclose (inptr));

Листинг 8.5. Пример создания и использования канала для ввода данных.

1.2 Сигналы

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

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

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

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

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

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

С сигналом могут быть ассоциированы действия одного из трех типов.

Подразумеваемые действия, зависящие от сигнала. Они описаны в заголовочном файле .

Игнорировать сигнал. Доставка сигнала не оказывает воздействия на процесс.

указатель на функцию

Обработать сигнал, выполнив при его доставке заданную функцию. После завершения функции обработки процесс возобновляет выполнение с точки прерывания. Обычно функция обработки вызывается в соответствии со следующим C-заголовком: void func (int signo); где signo — номер доставленного сигнала.

Первоначально, до входа в функцию main(), реакция на все сигналы установлена как SIG_DFL или SIG_IGN.

Функция называется асинхронно-сигнально-безопасной (АСБ), если ее можно вызывать без каких-либо ограничений при обработке сигналов. В стандарте POSIX-2001 имеется список функций, которые должны быть либо повторно входимыми, либо непрерываемыми сигналами, что превращает их в АСБ-функции. В этот список включены 117 функций, в том числе почти все из рассматриваемых нами.

Если сигнал доставляется потоку, а реакция заключается в завершении, остановке или продолжении, весь процесс должен завершиться, остановиться или продолжиться.

Перейдем к изложению возможностей по генерации сигналов. Выше была кратко рассмотрена служебная программа kill как средство терминирования процессов извне. На самом деле она посылает заданный сигнал; то же делает и одноименная функция (пример 8.6).

int kill (pid_t pid, int sig);

Листинг 8.6. Описание функции kill().

Сигнал задается аргументом sig, значение которого может быть нулевым; в этом случае действия функции kill() сводятся к проверке допустимости значения pid (нулевой результат — признак успешного завершения kill()).

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

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

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

1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL

5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE

9) SIGKILL10) SIGUSR111) SIGSEGV12) SIGUSR2

13) SIGPIPE14) SIGALRM15) SIGTERM17) SIGCHLD

18) SIGCONT19) SIGSTOP20) SIGTSTP21) SIGTTIN

22) SIGTTOU23) SIGURG24) SIGXCPU25) SIGXFSZ

26) SIGVTALRM27) SIGPROF28) SIGWINCH29) SIGIO

30) SIGPWR31) SIGSYS32) SIGRTMIN33) SIGRTMIN+1

34) SIGRTMIN+235) SIGRTMIN+336) SIGRTMIN+437) SIGRTMIN+5

38) SIGRTMIN+639) SIGRTMIN+740) SIGRTMIN+841) SIGRTMIN+9

42) SIGRTMIN+1043) SIGRTMIN+1144) SIGRTMIN+1245) SIGRTMIN+13

46) SIGRTMIN+1447) SIGRTMIN+1548) SIGRTMAX-1549) SIGRTMAX-14

50) SIGRTMAX-1351) SIGRTMAX-1252) SIGRTMAX-1153) SIGRTMAX-10

54) SIGRTMAX-955) SIGRTMAX-856) SIGRTMAX-757) SIGRTMAX-6

58) SIGRTMAX-559) SIGRTMAX-460) SIGRTMAX-361) SIGRTMAX-2

62) SIGRTMAX-163) SIGRTMAX

Листинг 8.7. Возможный результат выполнения команды kill -l.

Мы не будем пояснять назначение всех представленных в листинге сигналов, ограничившись кратким описанием тех, что фигурируют в стандарте POSIX-2001 как обязательные для реализации. Попутно отметим, что, согласно стандарту языка C, должны быть определены имена всего шести сигналов: SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV и SIGTERM.

Сигнал аварийного завершения процесса. Подразумеваемая реакция предусматривает, помимо аварийного завершения, создание файла с образом памяти процесса.

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

Ошибка системной шины как следствие обращения к неопределенной области памяти. Подразумеваемая реакция — аварийное завершение и создание файла с образом памяти процесса.

Завершение, остановка или продолжение порожденного процесса. Подразумеваемая реакция — игнорирование.

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

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

Сигнал разъединения. Подразумеваемая реакция — аварийное завершение процесса.

Некорректная команда. Подразумеваемая реакция — аварийное завершение и создание файла с образом памяти процесса.

Сигнал прерывания, поступивший с терминала. Подразумеваемая реакция — аварийное завершение процесса.

Уничтожение процесса (этот сигнал нельзя перехватить для обработки или проигнорировать). Подразумеваемая реакция — аварийное завершение процесса.

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

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

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

Остановка выполнения (этот сигнал нельзя перехватить для обработки или проигнорировать). Подразумеваемая реакция — остановка процесса.

Сигнал терминирования. Подразумеваемая реакция — аварийное завершение процесса.

Сигнал остановки, поступивший с терминала. Подразумеваемая реакция — остановка процесса.

Попытка чтения из фонового процесса. Подразумеваемая реакция — остановка процесса.

Попытка записи из фонового процесса. Подразумеваемая реакция — остановка процесса.

Определяемые пользователем сигналы. Подразумеваемая реакция — аварийное завершение процесса.

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

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

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

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

Высокоскоростное поступление данных в сокет. Подразумеваемая реакция — игнорирование.

Срабатывание виртуального таймера. Подразумеваемая реакция — аварийное завершение процесса.

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

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

Процесс (поток управления) может послать сигнал самому себе с помощью функции raise() (пример 8.8). Для процесса вызов raise() эквивалентен kill (getpid(), sig);

int raise (int sig);

Листинг 8.8. Описание функции raise().

Посылка сигнала самому себе использована в функции abort() (пример 8.9), вызывающей аварийное завершение процесса. (Заметим, что этого не произойдет, если функция обработки сигнала SIGABRT не возвращает управления. С другой стороны, abort() отменяет блокирование или игнорирование SIGABRT.)

void abort (void);

Листинг 8.9. Описание функции abort().

Опросить и изменить способ обработки сигналов позволяет функция sigaction() (пример 8.10).

int sigaction (int sig, const struct sigaction

*restrict act, struct sigaction

Листинг 8.10. Описание функции sigaction().

Для описания способа обработки сигнала используется структура sigaction, которая должна содержать по крайней мере следующие поля:

void (*sa_handler) (int);

/* Указатель на функцию обработки сигнала */

/* или один из макросов SIG_DFL или SIG_IGN */

/* Дополнительный набор сигналов, блокируемых */

/* на время выполнения функции обработки */

/* Флаги, влияющие на поведение сигнала */

void (*sa_sigaction) (int, siginfo_t *, void *);

/* Указатель на функцию обработки сигнала */

Приложение, соответствующее стандарту, не должно одновременно использовать поля обработчиков sa_handler и sa_sigaction.

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

Тип siginfo_t должен быть структурным по крайней мере со следующими полями:

int si_signo; /* Номер сигнала */

/* Значение переменной errno, ассоциированное

с данным сигналом */

/* Код, идентифицирующий причину сигнала */

/* Идентификатор процесса, пославшего сигнал */

/* Реальный идентификатор пользователя

процесса, пославшего сигнал */

/* Адрес, вызвавший генерацию сигнала */

/* Статус завершения порожденного процесса */

/* Событие, связанное с сигналом SIGPOLL */

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

Сигнал послан функцией kill().

Сигнал послан функцией sigqueue().

Сигнал сгенерирован в результате срабатывания таймера, установленного функцией timer_settime().

Сигнал вызван завершением асинхронной операции ввода/вывода.

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

Из кодов, специфичных для конкретных сигналов, мы упомянем лишь несколько, чтобы дать представление о степени детализации диагностики, предусмотренной стандартом POSIX-2001. (Из имени константы ясно, к какому сигналу она относится.)

Некорректный код операции.

Целочисленное деление на нуль.

Переполнение при выполнении операции вещественной арифметики.

Индекс вне диапазона.

Адрес не отображен на объект.

Некорректное выравнивание адреса.

Несуществующий физический адрес.

Процесс достиг точки прерывания.

Срабатывание трассировки процесса.

Завершение порожденного процесса.

Остановка порожденного процесса.

Поступили высокоприоритетные данные.

Вернемся непосредственно к описанию функции sigaction(). Если аргумент act отличен от NULL, он указывает на структуру, специфицирующую действия, которые будут ассоциированы с сигналом sig. По адресу oact (если он не NULL) возвращаются сведения о прежних действиях. Если значение act есть NULL, обработка сигнала остается неизменной; подобный вызов можно использовать для опроса способа обработки сигналов.

Следующие флаги в поле sa_flags влияют на поведение сигнала sig.

Не генерировать сигнал SIGCHLD при остановке или продолжении порожденного процесса (значение аргумента sig должно равняться SIGCHLD).

При входе в функцию обработки сигнала sig установить подразумеваемую реакцию SIG_DFL и очистить флаг SA_SIGINFO (см. далее).

Если этот флаг не установлен и определена функция обработки сигнала sig, она вызывается с одним целочисленным аргументом — номером сигнала. Соответственно, в приложении следует использовать поле sa_handler структуры sigaction. При установленном флаге SA_SIGINFO функция обработки вызывается с двумя дополнительными аргументами, как void func (int sig, siginfo_t *info, void *context); второй аргумент указывает на данные, поясняющие причину генерации сигнала, а третий может быть преобразован к указателю на тип ucontext_t — контекст процесса, прерванного доставкой сигнала. В этом случае приложение должно использовать поле sa_sigaction и поля структуры типа siginfo_t. В частности, если значение si_code неположительно, сигнал был сгенерирован процессом с идентификатором si_pid и реальным идентификатором пользователя si_uid.

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

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

trap [действие условие . ]

Аргумент «условие» может задаваться как EXIT (завершение командного интерпретатора) или как имя доставленного сигнала (без префикса SIG). При задании аргумента «действие» минус обозначает подразумеваемую реакцию, пустая цепочка («») — игнорирование. Если в качестве действия задана команда, то при наступлении условия она обрабатывается как eval действие.

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

Листинг 8.11. Пример сохранения и восстановления способа обработки сигналов посредством специальной встроенной команды trap.

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

trap ‘$HOME/logout’ EXIT

Листинг 8.12. Пример использования специальной встроенной команды trap.

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

echo «$INITLOG_ARGS -n $0 -s \»$1\» -e 1″ >&21

Листинг 8.13. Пример использования специальной встроенной команды trap для защиты от ошибок, специфичных для каналов.

К техническим аспектам можно отнести работу с наборами сигналов, которая выполняется посредством функций, показанных в пример 8.14. Функции sigemptyset() и sigfillset() инициализируют набор, делая его, соответственно, пустым или «полным». Функция sigaddset() добавляет сигнал signo к набору set, sigdelset() удаляет сигнал, а sigismember() проверяет вхождение в набор. Обычно признаком завершения является нулевой результат, в случае ошибки возвращается -1. Только sigismember() выдает 1, если сигнал signo входит в набор set.

int sigemptyset (sigset_t *set);

int sigfillset (sigset_t *set);

int sigaddset (sigset_t *set, int signo);

int sigdelset (sigset_t *set, int signo);

int sigismember (const sigset_t *set,

Листинг 8.14. Описание функций для работы с наборами сигналов.

Функция sigprocmask() (пример 8.15) предназначена для опроса и/или изменения маски сигналов процесса, определяющей набор блокируемых сигналов.

int sigprocmask (int how, const sigset_t

*restrict set, sigset_t *restrict oset);

Листинг 8.15. Описание функции sigprocmask().

Если аргумент set отличен от NULL, он указывает на набор, используемый для изменения текущей маски сигналов. Аргумент how определяет способ изменения; он может принимать одно из трех значений: SIG_BLOCK (результирующая маска получается при объединении текущей и заданной аргументом set), SIG_SETMASK (результирующая маска устанавливается равной set) и SIG_UNBLOCK (маска set вычитается из текущей).

По адресу oset (если он не NULL) возвращается прежняя маска. Если значение set есть NULL, набор блокируемых сигналов остается неизменным; подобный вызов можно использовать для опроса текущей маски сигналов процесса.

Если к моменту завершения sigprocmask() будут существовать ждущие неблокированные сигналы, по крайней мере один из них должен быть доставлен до возврата из sigprocmask().

Нельзя блокировать сигналы, не допускающие игнорирования.

Функция sigpending() (пример 8.16) позволяет выяснить набор блокированных сигналов, ожидающих доставки вызывающему процессу (потоку управления). Дождаться появления подобного сигнала можно с помощью функции sigwait() (пример 8.17).

int sigpending (sigset_t *set);

Листинг 8.16. Описание функции sigpending().

int sigwait (const sigset_t *restrict set,

int *restrict sig);

Листинг 8.17. Описание функции sigwait().

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

Отметим, что стандарт POSIX-2001 не специфицирует воздействие функции sigwait() на обработку сигналов, включенных в набор set. Чтобы дождаться доставки обрабатываемого или терминирующего процесс сигнала, можно воспользоваться функцией pause() (пример 8.18).

Листинг 8.18. Описание функции pause().

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

Несмотря на внешнюю простоту, использование функции pause() сопряжено с рядом тонкостей. При наивном подходе сначала проверяют некоторое условие, связанное с сигналом, и, если оно не выполнено (сигнал отсутствует), вызывают pause(). К сожалению, сигнал может быть доставлен в промежутке между проверкой и вызовом pause(), что нарушает логику работы процесса и способно привести к его зависанию. Решить подобную проблему позволяет функция sigsuspend() (пример 8.19) в сочетании с рассмотренной выше функцией sigprocmask().

int sigsuspend (const sigset_t *sigmask);

Листинг 8.19. Описание функции sigsuspend().

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

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

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

void abort (void) <

struct sigaction sact;

(void) fflush (NULL);

/* Снимем блокировку сигнала SIGABRT */

if ((sigemptyset (&sset) == 0) && (sigaddset (&sset, SIGABRT) == 0)) <

/* Пошлем себе сигнал SIGABRT. */

/* Возможно, его перехватит функция обработки, */

/* и тогда вызывающий процесс может не завершиться */

/* Установим подразумеваемую реакцию на сигнал SIGABRT */

(void) sigaction (SIGABRT, &sact, NULL);

/* Снова пошлем себе сигнал SIGABRT */

/* Если сигнал снова не помог, попробуем еще одно средство завершения */

printf («Перед вызовом abort()\n»);

printf («После вызова abort()\n»);

Листинг 8.20. Упрощенная реализация функции abort() как пример использования функций работы с сигналами.

В качестве нюанса, характерного для работы с сигналами, отметим, что до первого обращения к raise() нельзя закрыть потоки (можно только вытолкнуть буфера), поскольку функция обработки сигнала SIGABRT, возможно, осуществляет вывод. Еще одним примером использования механизма сигналов может служить приведенная в пример 8.13 упрощенная реализация функции sleep(), предназначенной для «засыпания» на заданное число секунд. (Можно надеяться, что не описанные пока средства работы с временем интуитивно понятны.)

/* Функция обработки сигнала SIGALRM. */

/* Она ничего не делает, но игнорировать сигнал нельзя */

static void signal_handler (int sig) <

/* В демонстрационных целях распечатаем номер обрабатываемого сигнала */

printf («Принят сигнал %d\n», sig);

/* Функция для «засыпания» на заданное число секунд */

/* Результат равен разности между заказанной и фактической */

unsigned int sleep (unsigned int seconds) <

time_t before, after;

unsigned int slept;

sigset_t set, oset;

struct sigaction act, oact;

/* Установим будильник на заданное время, */

/* но перед этим блокируем сигнал SIGALRM */

/* и зададим свою функцию обработки для него */

struct sigaction act;

/* В демонстрационных целях установим обработку прерывания с клавиатуры */

printf («Заснем на 10 секунд\n»);

printf («Проснулись, не доспав %d секунд\n», sleep (10));

Листинг 8.21. Упрощенная реализация функции sleep() как пример использования механизма сигналов.

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

Вызвать «недосыпание» приведенной программы можно, послав ей сигнал SIGALRM (например, посредством команды kill -s SIGALRM идентификатор_процесса) или SIGINT (путем нажатия на клавиатуре терминала комбинации клавиш CTRL+C).

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

Мы переходим к рассмотрению средств локального межпроцессного взаимодействия, относящихся к необязательной части стандарта POSIX-2001, именуемой «X/Open-расширение системного интерфейса» (XSI). Будут описаны очереди сообщений, семафоры и разделяемые сегменты памяти.

Остановимся сначала на понятиях и структурах, общих для всех трех упомянутых средств. Каждая очередь сообщений, набор семафоров и разделяемый сегмент однозначно идентифицируются положительным целым числом, которое обычно обозначается, соответственно, как msqid, semid и shmid и возвращается в качестве результатов функций msgget(), semget() и shmget().

При получении идентификаторов средств межпроцессного взаимодействия используется еще одна сущность — ключ, а для его генерации предназначена функция ftok() (пример 8.22). Аргумент path должен задавать маршрутное имя существующего файла, к которому вызывающий процесс может применить функцию stat(). В качестве значения аргумента id, по соображениям мобильности, рекомендуется использовать однобайтный символ. Гарантируется, что функция ftok() сгенерирует один и тот же ключ для заданной пары (файл, символ) и разные ключи для разных пар.

key_t ftok (const char *path, int id);

Листинг 8.22. Описание функции ftok().

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

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

/* Идентификатор владеющей группы */

создавшего данное средство

/* Идентификатор создавшей группы */

/* Режим доступа на чтение/запись */

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

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

ipcs [-qms] [-a | -bcopt]

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

Следующие опции управляют форматом выдачи. Задание опции -a равносильно указанию всех опций формата. Опция -b предписывает выдавать лимиты на размер (максимальное количество байт в сообщениях очереди и т.п.), -c — имена пользователя и группы создателя средства, -o — информацию об использовании (количество сообщений в очереди, их суммарный размер и т.п.), -p — информацию о процессах (идентификаторы последнего отправителя, получателя и т.п.), -t — информацию о времени (последняя управляющая операция, последняя отправка сообщения и т.п.).

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

ipcrm [-q msgid | -Q msgkey | -s semid |

-S semkey | -m shmid | -M shmkey ] .

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

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

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

Для работы с очередями сообщений стандарт POSIX-2001 предусматривает следующие функции (пример 8.23): msgget() (получение идентификатора очереди сообщений), msgctl() (управление очередью сообщений), msgsnd() (отправка сообщения) и msgrcv() (прием сообщения).

int msgget (key_t key, int msgflg);

int msgsnd (int msqid, const void *msgp,

size_t msgsz, int msgflg);

ssize_t msgrcv (int msqid, void *msgp,

size_t msgsz, long msgtyp,

int msgctl (int msqid, int cmd,

struct msqid_ds *buf);

Листинг 8.23. Описание функций для работы с очередями сообщений.

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

struct ipc_perm msg_perm;

/* Данные о правах доступа

к очереди сообщений */

/* Текущее количество сообщений в очереди */

/* Максимально допустимый суммарный

размер сообщений в очереди */

/* Идентификатор процесса, отправившего

/* Идентификатор процесса, принявшего

/* Время последней отправки */

/* Время последнего приема */

/* Время последнего изменения

Перейдем к детальному рассмотрению функций для работы с очередями сообщений.

Функция msgget() возвращает идентификатор очереди сообщений, ассоциированный с ключом key. Новая очередь, ее идентификатор и соответствующая структура msqid_ds создаются для заданного ключа, если значение аргумента key равно IPC_PRIVATE или очередь еще не ассоциирована с ключом, а в числе флагов msgflg задан IPC_CREAT.

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

Структура msqid_ds для новой очереди инициализируется следующим образом.

· Значения полей msg_perm.cuid, msg_perm.uid, msg_perm.cgid и msg_perm.gid устанавливаются равными действующим идентификаторам пользователя и группы вызывающего процесса.

· Младшие девять бит поля msg_perm.mode устанавливаются равными младшим девяти битам значения msgflg.

· Поля msg_qnum, msg_lspid, msg_lrpid, msg_stime и msg_rtime обнуляются.

· В поле msg_ctime помещается текущее время, а в поле msg_qbytes — определенный в системе лимит.

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

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

/* Программа создает очередь сообщений. */

/* В командной строке задаются имя файла для ftok() */

/* и режим доступа к очереди сообщений */

#define FTOK_CHAR ‘G’

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

fprintf (stderr, «Использование: %s маршрутное_имя режим_доступа\n», argv [0]);

if ((key = ftok (argv [1], FTOK_CHAR)) == (key_t) (-1)) <

(void) sscanf (argv [2], «%o», (unsigned int *) &mode);

Листинг 8.24. Пример программы, создающей очередь сообщений.

Если после выполнения этой программы воспользоваться командой ipcs -q, то результат может выглядеть так, как показано в пример 8.25.

key msqid owner perms used-bytes messages

0x47034bac 163840 galat 644 0 0

Листинг 8.25. Возможный результат опроса статуса очередей сообщений.

Удалить созданную очередь из системы, соответствующей стандарту POSIX-2001, можно командой ipcrm -q 163840.

Операции отправки/приема сообщений выполняют функции msgsnd() и msgrcv(); msgsnd() помещает сообщения в очередь, а msgrcv() читает и «достает» их оттуда.

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

В зависимости от значения, указанного в качестве аргумента msgtyp функции msgrcv(), из очереди выбирается то или иное сообщение. Если значение аргумента равно нулю, запрашивается первое сообщение в очереди, если больше нуля — первое сообщение типа msgtyp, а если меньше нуля — первое сообщение наименьшего из типов, не превышающих абсолютную величину аргумента msgtyp. Пусть, например, в очередь последовательно помещены сообщения с типами 5, 3 и 2. Тогда вызов msgrcv (msqid, msgp, size, 0, flags) выберет из очереди сообщение с типом 5, поскольку оно отправлено первым; вызов msgrcv (msqid, msgp, size, -4, flags) — последнее сообщение, так как 2 — это наименьший из возможных типов в указанном диапазоне; наконец, вызов msgrcv (msqid, msgp, size, 3, flags) — сообщение с типом 3.

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

Если не указано противное, функции msgsnd() и msgrcv() выполняют операции с блокировкой, например: msgsnd (msqid, msgp, size, 0); msgrcv (msqid, msgp, size, type, 0). Чтобы выполнить операцию без блокировки, необходимо установить флаг IPC_NOWAIT: msgsnd (msqid, msgp, size, IPC_NOWAIT); msgrcv (msqid, msgp, size, type, IPC_NOWAIT).

Аргумент msgp указывает на значение структурного типа, в котором представлены тип и тело сообщения (пример 8.26).

long mtype; /* Тип сообщения */

char mtext [1]; /* Текст сообщения */

Листинг 8.26. Описание структурного типа для представления сообщений.

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

#define MAXSZTMSG 8192

long mtype; /* Тип сообщения */

char mtext [MAXSZTMSG]; /* Текст сообщения */

struct mymsgbuf msgbuf;

Листинг 8.27. Описание структуры для хранения сообщений.

В качестве аргумента msgsz обычно указывается размер текстового буфера, например: sizeof (msgbuf.text).

Если не указано противное, в случае, когда длина выбранного сообщения больше, чем msgsz, вызов msgrcv() завершается неудачей. Если же установить флаг MSG_NOERROR, длинное сообщение обрезается до msgsz байт. Отброшенная часть пропадает, а вызывающий процесс не получает никакого уведомления о том, что сообщение обрезано.

При успешном завершении msgsnd() возвращает 0, а msgrcv() — значение, равное числу реально полученных байт; при неудаче возвращается -1.

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

Управляющее действие определяется значением аргумента cmd. Допустимых значений три: IPC_STAT — получить информацию о состоянии очереди, IPC_SET — переустановить характеристики очереди, IPC_RMID — удалить очередь.

Команды IPC_STAT и IPC_SET для хранения информации об очереди используют имеющуюся в прикладной программе структуру типа msqid_ds, указатель на которую содержит аргумент buf: IPC_STAT копирует в нее ассоциированную с очередью структуру данных, а IPC_SET, наоборот, в соответствии с ней обновляет ассоциированную структуру. Команда IPC_SET позволяет переустановить значения идентификаторов владельца (msg_perm.uid) и владеющей группы (msg_perm.gid), режима доступа (msg_perm.mode), максимально допустимый суммарный размер сообщений в очереди (msg_qbytes). Увеличить значение msg_qbytes может только процесс, обладающий соответствующими привилегиями. В пример 8.28 приведена программа, изменяющая максимально допустимый суммарный размер сообщений в очереди. Предполагается, что очередь сообщений уже создана, а ее идентификатор известен. Читателю предлагается выполнить эту программу с разными значениями максимально допустимого суммарного размера (как меньше, так и больше текущего), действуя от имени обычного и привилегированного пользователя.

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

struct msqid_ds msqid_ds;

fprintf (stderr, «Использование: %s идентификатор_очереди максимальный_размер\n», argv [0]);

/* Получим исходное значение структуры данных */

printf («Максимальный размер очереди до изменения: %ld\n», msqid_ds.msg_qbytes);

/* Попробуем внести изменения */

/* Получим новое значение структуры данных */

printf («Максимальный размер очереди после изменения: %ld\n», msqid_ds.msg_qbytes);

Листинг 8.28. Пример программы управления очередями сообщений.

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

Подобные документы

Запуск из одного приложения других приложений. Технология связывания и внедрения объектов. Понятие межпроцессного взаимодействия. Сценарий использования разделяемой памяти. Библиотеки динамической компоновки. Именованные и анонимные каналы, сокеты.

курсовая работа [46,5 K], добавлен 26.07.2014

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

презентация [116,4 K], добавлен 10.02.2014

Общее понятие и специфика применения очереди в программировании. Способы реализации очереди, их сущностная характеристика. Основные проблемы в использовании списков. Представление очереди в виде массива и двух целочисленных переменных start и end.

презентация [895,9 K], добавлен 14.10.2013

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

контрольная работа [19,1 K], добавлен 10.10.2010

Структура ядра операционной системы. Основные компоненты подсистемы управления процессами и памятью. Характеристика системных и прикладных процессов в Unix. Идентификация процесса Linux, его атрибуты и вызовы. Средства межпроцессного взаимодействия.

лекция [170,1 K], добавлен 29.07.2012

Объем двухпортовой памяти, расположенной на кристалле, для хранения программ и данных в процессорах ADSP-2106x. Метод двойного доступа к памяти. Кэш-команды и конфликты при обращении к данным по шине памяти. Пространство памяти многопроцессорной системы.

реферат [28,1 K], добавлен 13.11.2009

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

презентация [5,3 M], добавлен 28.01.2015

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

курсовая работа [33,3 K], добавлен 12.05.2013

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

курсовая работа [179,6 K], добавлен 13.11.2009

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

контрольная работа [275,7 K], добавлен 05.07.2014

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.

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