- •Н.В.Вдовикина, а.В.Казунин, и.В.Машечкин, а.Н.Терехин Системное программное обеспечение: взаимодействие процессов.
- •Часть I. Теоретические основы. 5
- •Часть II. Реализация процессов. 34
- •Часть III. Реализация взаимодействия процессов. 62
- •6.4Семафоры. 116
- •Часть I. Теоретические основы.
- •Введение.
- •Понятие процесса.
- •Некоторые типы процессов.
- •«Полновесные процессы»
- •«Легковесные процессы»
- •Жизненный цикл процесса.
- •Синхронизация параллельных процессов.
- •Способы реализации взаимного исключения.
- •Запрещение прерываний и специальные инструкции.
- •Алгоритм Петерсона.
- •Активное ожидание.
- •Семафоры.
- •Мониторы.
- •Дополнительная синхронизация: переменные-условия.
- •Обмен сообщениями.
- •Синхронизация.
- •Адресация.
- •Длина сообщения.
- •Классические задачи синхронизации процессов.
- •«Обедающие философы»
- •Задача «читателей и писателей»
- •Задача о «спящем парикмахере»
- •Часть II. Реализация процессов.
- •Реализация процессов в ос unix
- •Понятие процесса в unix.
- •Контекст процесса.
- •Тело процесса.
- •Аппаратный контекст.
- •Системный контекст.
- •Аппарат системных вызов в oc unix.
- •Порождение новых процессов.
- •Порождение сыновнего процесса. Идентификаторы процессов.
- •Порождение сыновнего процесса. Одновременное выполнение.
- •Механизм замены тела процесса.
- •Запуск на выполнение команды ls.
- •Вызов программы компиляции.
- •Использование схемы fork-exec
- •Завершение процесса.
- •Использование системного вызова wait()
- •Использование системного вызова wait()
- •Жизненный цикл процесса в ос unix.
- •Начальная загрузка. Формирование о и 1 процессов.
- •Планирование процессов в ос unix.
- •Планирование процессов.
- •Принципы организация свопинга.
- •Часть III. Реализация взаимодействия процессов.
- •Элементарные средства межпроцессного взаимодействия.
- •Сигналы.
- •Обработка сигнала.
- •Удаление временных файлов при завершении программы.
- •Программа “Будильник”.
- •Двухпроцессный вариант программы “Будильник”.
- •Надежные сигналы.
- •Работа с сигнальной маской.
- •Использование надежных сигналов.
- •Программные каналы
- •Использование канала.
- •Реализация конвейера.
- •Совместное использование сигналов и каналов – «пинг-понг».
- •Именованные каналы (fifo)
- •Модель «клиент-сервер».
- •Нелокальные переходы.
- •Использование нелокальных переходов.
- •Трассировка процессов.
- •Общая схема использования механизма трассировки.
- •Трассировка процессов.
- •Средства межпроцессного взаимодействия System V.
- •Организация доступа и именования в разделяемых ресурсах.
- •Именование разделяемых объектов.
- •Генерация ключей: функция ftok().
- •Общие принципы работы с разделяемыми ресурсами.
- •Очередь сообщений.
- •Доступ к очереди сообщений.
- •Отправка сообщения.
- •Получение сообщения.
- •Управление очередью сообщений.
- •Использование очереди сообщений.
- •Основной процесс.
- •Очередь сообщений. Модель «клиент-сервер»
- •Разделяемая память
- •Создание общей памяти.
- •Доступ к разделяемой памяти.
- •Открепление разделяемой памяти.
- •Управление разделяемой памятью.
- •Общая схема работы с общей памятью в рамках одного процесса.
- •Семафоры.
- •Доступ к семафору
- •Операции над семафором
- •Управление массивом семафоров.
- •Работа с разделяемой памятью с синхронизацией семафорами.
- •1Й процесс:
- •2Й процесс:
- •Взаимодействие процессов в сети.
- •Механизм сокетов.
- •Типы сокетов. Коммуникационный домен.
- •Создание и конфигурирование сокета. Создание сокета.
- •Связывание.
- •Предварительное установление соединения. Сокеты с установлением соединения. Запрос на соединение.
- •Сервер: прослушивание сокета и подтверждение соединения.
- •Прием и передача данных.
- •Завершение работы с сокетом.
- •Резюме: общая схема работы с сокетами.
- •Работа с локальными сокетами.
- •Пример работы с сокетами в рамках сети.
- •Среда параллельного программирования mpi
- •Краткий обзор параллельных архитектур.
- •Системы с распределенной памятью – mpp.
- •Системы с общей памятью – smp.
- •Системы с неоднородным доступом к памяти – numa.
- •Кластерные системы.
- •Модель программирования mpi.
- •Функции общего назначения. Общая структура программы.
- •Коммуникаторы и группы.
- •Обрамляющие функции. Инициализация и завершение.
- •Синхронизация: барьеры.
- •Использование барьерной синхронизации.
- •Прием и передача данных. Общие замечания.
- •Сообщения и их атрибуты.
- •Поддержка типов данных в mpi.
- •Коммуникации «точка-точка». Блокирующий режим.
- •Отправка сообщений в блокирующем режиме.
- •Режимы буферизации.
- •Прием сообщений в блокирующем режиме.
- •Mpi: прием сообщения, размер которого неизвестен заранее.
- •Коммуникации «точка-точка». Неблокирующий режим.
- •Отсылка и прием сообщений в неблокирующем режиме.
- •Работа с квитанциями.
- •Mpi: коммуникации «точка-точка». «Пинг-понг».
- •Коллективные коммуникации.
- •Коллективный обмен данными.
- •Коллективный обмен, совмещенный с обработкой данных.
- •Mpi: применение коллективных коммуникаций.
- •Алфавитный указатель упоминаемых библиотечных функций и системных вызовов.
- •Список литературы
-
Использование канала.
Пример использования канала в рамках одного процесса – копирование строк. Фактически осуществляется посылка данных самому себе.
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char *s = ”chanel”;
char buf[80];
int pipes[2];
pipe(pipes);
write(pipes[1], s, strlen(s) + 1);
read(pipes[0], buf, strlen(s) + 1);
close(pipes[0]);
close(pipes[1]);
printf(“%s\n”, buf);
return 0;
}
Рис. 15 Обмен через канал в рамках одного процесса.
Чаще всего, однако, канал используется для обмена данными между несколькими процессами. При организации такого обмена используется тот факт, что при порождении сыновнего процесса посредством системного вызова fork() наследуется таблица файловых дескрипторов процесса-отца, т.е. все файловые дескрипторы, доступные процессу-отцу, будут доступны и процессу-сыну. Таким образом, если перед порождением потомка был создан канал, файловые дескрипторы для доступа к каналу будут унаследованы и сыном. В итоге обоим процессам оказываются доступны дескрипторы, связанные с каналом, и они могут использовать канал для обмена данными (см. Рис. 16 и xvii).
Рис. 16 Пример обмена данными между процессами через канал.
-
Схема взаимодействия процессов с использованием канала.
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd[2];
pipe(fd);
if (fork())
{/*процесс-родитель*/
close(fd[0]); /* закрываем ненужный дескриптор */
write (fd[1], …);
…
close(fd[1]);
…
}
else
{/*процесс-потомок*/
close(fd[1]); /* закрываем ненужный дескриптор */
while(read (fd[0], …))
{
…
}
…
}
}
Аналогичным образом может быть организован обмен через канал между двумя потомками одного порождающего процесса и вообще между любыми родственными процессами, единственным требованием здесь, как уже говорилось, является необходимость создавать канал в порождающем процессе прежде, чем его дескрипторы будут унаследованы порожденными процессами.
Как правило, канал используется как однонаправленное средство передачи данных, т.е. только один из двух взаимодействующих процессов осуществляет запись в него, а другой процесс осуществляет чтение10, при этом каждый из процессов закрывает не используемый им дескриптор. Это особенно важно для неиспользуемого дескриптора записи в канал, так как именно при закрытии пишущей стороны канала в него помещается символ конца файла. Если, к примеру, в рассмотренном xvii процесс-потомок не закроет свой дескриптор записи в канал, то при последующем чтении из канала, исчерпав все данные из него, он будет заблокирован, так как записывающая сторона канала будет открыта, и следовательно, читающий процесс будет ожидать очередной порции данных.
-
Реализация конвейера.
Пример реализации конвейера print|wc – вывод программы print будет подаваться на вход программы wc. Программа print печатает некоторый текст. Программа wc считает количество прочитанных строк, слов и символов.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd[2];
pipe(fd); /*организован канал*/
if (fork())
{
/*процесс-родитель*/
dup2(fd[1], 1); /* отождествили стандартный вывод с файловым дескриптором канала, предназначенным для записи */
close(fd[1]); /* закрыли файловый дескриптор канала, предназначенный для записи */
close(fd[0]); /* закрыли файловый дескриптор канала, предназначенный для чтения */
exelp(“print”, ”print”, 0); /* запустили программу print */
}
/*процесс-потомок*/
dup2(fd[0], 0); /* отождествили стандартный ввод с файловым дескриптором канала, предназначенным для чтения*/
close(fd[0]); /* закрыли файловый дескриптор канала, предназначенный для чтения */
close(fd[1]); /* закрыли файловый дескриптор канала, предназначенный для записи */
execl(“/usr/bin/wc”, ”wc”, 0); /* запустили программу wc */
}