The design of the unix operating system by Maurice J

Вид материалаРеферат
Многопроцессорные системы
12.1 Проблемы, связанные с многопроцессорными системами
12.2 Главный и подчиненный процессоры
Подобный материал:
1   ...   47   48   49   50   51   52   53   54   55

данных out-of-band. В дейтаграммных версиях указанных функций,

sendto и recvfrom, в качестве дополнительных параметров указыва-

ются адреса. После выполнения подключения к гнездам потокового

типа процессы могут вместо функций send и recv использовать функ-

ции read и write. Таким образом, согласовав тип протокола, серве-

ры могли бы порождать процессы, работающие только с функциями

read и write, словно имеют дело с обычными файлами.

Функция shutdown закрывает гнездовую связь:

shutdown(sd,mode)

где mode указывает, какой из сторон (посылающей, принимающей или

обеим вместе) отныне запрещено участие в процессе передачи дан-

ных. Функция сообщает используемому протоколу о завершении сеанса

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

гнезд в неприкосновенности. Освобождается дескриптор гнезда толь-

ко в результате выполнения функции close.

Системная функция getsockname получает имя гнездовой связи,

установленной ранее с помощью функции bind:

getsockname(sd,name,length);

Функции getsockopt и setsockopt получают и устанавливают зна-

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

типом домена и протокола.

Рассмотрим обслуживающую программу, представленную на Рисунке

11.20. Процесс создает в домене "UNIX system" гнездо потокового

типа и присваивает ему имя sockname. Затем с помощью функции

listen устанавливается длина очереди поступающих сообщений и на-

чинается цикл ожидания поступления запросов. Функция accept при-

останавливает свое выполнение до тех пор, пока протоколом не бу-

дет зарегистрирован запрос на подключение к гнезду с означенным

именем; после этого функция завершается, возвращая поступившему

запросу новый дескриптор гнезда. Процесс-сервер порождает потом-

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

том; родитель и потомок при этом закрывают свои дескрипторы, что-

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

другого процесса. Процесс-потомок ведет разговор с клиентом и за-

вершается после выхода из функции read. Процесс-сервер возвраща-


-------------------------------------------------------------┐

│ #include

│ #include

│ │

│ main() │

│ { │

│ int sd,ns; │

│ char buf[256]; │

│ struct sockaddr sockaddr; │

│ int fromlen; │

│ │

│ sd = socket(AF_UNIX,SOCK_STREAM,0); │

│ │

│ /* имя гнезда - не может включать пустой символ */ │

│ bind(sd,"sockname",sizeof("sockname") - 1); │

│ listen(sd,1); │

│ │

│ for (;;) │

│ { │

│ │

│ ns = accept(sd,&sockaddr,&fromlen); │

│ if (fork() == 0) │

│ { │

│ /* потомок */ │

│ close(sd); │

│ read(ns,buf,sizeof(buf)); │

│ printf("сервер читает '%s'\n",buf); │

│ exit(); │

│ } │

│ close(ns); │

│ } │

│ } │

L-------------------------------------------------------------

Рисунок 11.20. Процесс-сервер в домене "UNIX system"


ется к началу цикла и ждет поступления следующего запроса на

подключение.

На Рисунке 11.21 показан пример процесса-клиента, ведущего

общение с сервером. Клиент создает гнездо в том же домене, что и

сервер, и посылает запрос на подключение к гнезду с именем

sockname. В результате подключения процесс-клиент получает вирту-

альный канал связи с сервером. В рассматриваемом примере клиент

передает одно сообщение и завершается.

Если сервер обслуживает процессы в сети, указание о том, что

гнездо принадлежит домену "Internet", можно сделать следующим об-

разом:

socket(AF_INET,SOCK_STREAM,0);


-------------------------------------------------------------┐

│ #include

│ #include

│ │

│ main() │

│ { │

│ int sd,ns; │

│ char buf[256]; │

│ struct sockaddr sockaddr; │

│ int fromlen; │

│ │

│ sd = socket(AF_UNIX,SOCK_STREAM,0); │

│ │

│ /* имя в запросе на подключение не может включать │

│ /* пустой символ */ │

│ if (connect(sd,"sockname",sizeof("sockname") - 1) == -1)│

│ exit(); │

│ │

│ write(sd,"hi guy",6); │

│ } │

L-------------------------------------------------------------

Рисунок 11.21. Процесс-клиент в домене "UNIX system"


и связаться с сетевым адресом, полученным от сервера. В системе

BSD имеются библиотечные функции, выполняющие эти действия. Вто-

рой параметр вызываемой клиентом функции connect содержит адрес-

ную информацию, необходимую для идентификации машины в сети (или

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

также дополнительную информацию, идентифицирующую приемное гнездо

машины-адресата. Если серверу нужно одновременно следить за сос-

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

гнезда и с помощью функции select определяет, с каким клиентом

устанавливается связь в данный момент.

11.5 ВЫВОДЫ

Мы рассмотрели несколько форм взаимодействия процессов. Пер-

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

процессов - взаимодействие двух процессов, выступающее в качестве

полезного средства отладки программ. При всех своих преимуществах

трассировка процессов с помощью функции ptrace все же достаточно

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

функция способна передать строго ограниченный объем данных, тре-

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

ограничивается только формой отношений родитель-потомок, и нако-

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

участвующих в ней процессов. В версии V системы UNIX имеется па-

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

обмена сообщениями, работы с семафорами и разделения памяти. К

сожалению, все эти механизмы имеют узкоспециальное назначение, не

имеют хорошей стыковки с другими элементами операционной системы

и не действуют в сети. Тем не менее, они используются во многих

приложениях и по сравнению с другими схемами отличаются более вы-

сокой эффективностью.

Система UNIX поддерживает широкий спектр вычислительных се-

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

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

типах сетей они реализуются по-разному. В системе BSD имеются

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

универсальную структуру сетевого взаимодействия. В будущем в вер-

сию V предполагается включить описанный в главе 10 потоковый ме-

ханизм, повышающий согласованность работы в сети.

11.6 УПРАЖНЕНИЯ


1. Что произойдет в том случае, если в программе debug будет

отсутствовать вызов функции wait (Рисунок 11.3) ? (Намек:

возможны два исхода.)

2. С помощью функции ptrace отладчик считывает данные из прост-

ранства трассируемого процесса по одному слову за одну опе-

рацию. Какие изменения следует произвести в ядре операцион-

ной системы для того, чтобы увеличить количество считываемых

слов ? Какие изменения при этом необходимо сделать в самой

функции ptrace ?

3. Расширьте область действия функции ptrace так, чтобы в ка-

честве параметра pid можно было указывать идентификатор про-

цесса, не являющегося потомком текущего процесса. Подумайте

над вопросами, связанными с защитой информации: При каких

обстоятельствах процессу может быть позволено читать данные

из адресного пространства другого, произвольного процесса ?

При каких обстоятельствах разрешается вести запись в адрес-

ное пространство другого процесса ?

4. Организуйте из функций работы с сообщениями библиотеку поль-

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

нованных каналов и элементов блокировки. Создавая очередь

сообщений, откройте управляющий файл для записи в него ин-

формации о состоянии очереди; защитите файл с помощью

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

Посылая сообщение данного типа, создавайте поименованный ка-

нал для всех сообщений этого типа, если такого канала еще не

было, и передавайте сообщение через него (с подсчетом пере-

данных байт). Управляющий файл должен соотносить тип сообще-

ния с именем поименованного канала. При чтении сообщений уп-

равляющий файл направляет процесс к соответствующему поиме-

нованному каналу. Сравните эту схему с механизмом, описанным

в настоящей главе, по эффективности, сложности реализации и

функциональным возможностям.

5. Какие действия пытается выполнить программа, представленная

на Рисунке 11.22 ?

*6. Напишите программу, которая подключала бы область разделяе-

мой памяти слишком близко к вершине стека задачи и позволяла

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

ласти. В какой момент произойдет фатальная ошибка памяти ?

7. Используйте в программе, представленной на Рисунке 11.14,

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

трируйте, как за счет этого можно избежать возникновения

взаимных блокировок.

8. Покажите, как операции над семафорами типа P и V реализуются

при работе с поименованными каналами. Как бы вы реализовали

операцию P условного типа ?

9. Составьте программы захвата ресурсов, использующие (а) пои-

менованные каналы, (б) системные функции creat и unlink, (в)

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

их эффективности.

10. На практических примерах работы с поименованными каналами

сравните эффективность использования функций обмена сообще-

ниями, с одной стороны, с функциями read и write, с другой.

11. Сравните на конкретных программах скорость передачи данных

при работе с разделяемой памятью и при использовании меха-

низма обмена сообщениями. Программы, использующие разделяе-

мую память, для синхронизации завершения операций чтения-за-

писи должны опираться на семафоры.


-------------------------------------------------------------┐

│ #include

│ #include

│ #include

│ #define ALLTYPES 0 │

│ │

│ main() │

│ { │

│ struct msgform │

│ { │

│ long mtype; │

│ char mtext[1024]; │

│ } msg; │

│ register unsigned int id; │

│ │

│ for (id = 0; ; id++) │

│ while (msgrcv(id,&msg,1024,ALLTYPES,IPC_NOWAIT) > 0)│

│ ; │

│ } │

L-------------------------------------------------------------


МНОГОПРОЦЕССОРНЫЕ СИСТЕМЫ


В классической постановке для системы UNIX предполагается ис-

пользование однопроцессорной архитектуры, состоящей из одного ЦП,

памяти и периферийных устройств. Многопроцессорная архитектура,

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

общую память и периферийные устройства (Рисунок 12.1), располагая

большими возможностями в увеличении производительности системы,

связанными с одновременным исполнением процессов на разных ЦП.

Каждый ЦП функционирует независимо от других, но все они работают

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

в такой системе ничем не отличается от поведения в однопроцессор-

ной системе - с сохранением семантики обращения к каждой систем-

ной функции - но при этом они могут открыто перемещаться с одного

процессора на другой. Хотя, к сожалению, это не приводит к сниже-

нию затрат процессорного времени, связанного с выполнением процес-

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

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

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

оговоренных случаев, в настоящей главе не проводится никаких раз-

личий между системами с присоединенными процессорами и остальными

классами многопроцессорных систем.

Параллельная работа нескольких процессоров в режиме ядра по

выполнению различных процессов создает ряд проблем, связанных с

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

соответствующих механизмов защиты. Ниже будет показано, почему

классический вариант системы UNIX не может быть принят в многоп-

роцессорных системах без внесения необходимых изменений, а также

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

занной среде.


------------┐ ------------┐ ------------┐

│ Процессор │ │ Процессор │ │ Процессор │

│ 1 │ │ 2 │ ........... │ n │

L-----T------ L-----T------ L-----T------

│ │ │

----------+-------T-------+--------------T------------+-----------

│ │

----+----┐ ------------+-------------┐

│ Память │ │ Периферийные устройства │

L--------- L--------------------------

Рисунок 12.1. Многопроцессорная конфигурация


12.1 ПРОБЛЕМЫ, СВЯЗАННЫЕ С МНОГОПРОЦЕССОРНЫМИ СИСТЕМАМИ

В главе 2 мы говорили о том, что защита целостности структур

данных ядра системы UNIX обеспечивается двумя способами: ядро не

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

если работа производится в режиме ядра, кроме того, если при вы-

полнении критического участка программы обработчик возникающих

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

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

однако, если два и более процессов выполняются одновременно в ре-

жиме ядра на разных процессорах, нарушение целостности ядра может

произойти даже несмотря на принятие защитных мер, с другой сторо-

ны, в однопроцессорной системе вполне достаточных.

--------------------------------------------------------┐

│ struct queue { │

│ │

│ } *bp, *bp1; │

│ bp1->forp=bp->forp; │

│ bp1->backp=bp; │

│ bp->forp=bp1; │

│ /* рассмотрите возможность переключения контекста в │

│ * этом месте */ │

│ bp1->forp->backp=bp1; │

L--------------------------------------------------------

Рисунок 12.2. Включение буфера в список с двойными указателя-

ми


В качестве примера рассмотрим фрагмент программы из главы 2

(Рисунок 12.2), в котором новая структура данных (указатель bp1)

помещается в список после существующей структуры (указатель bp).

Предположим, что этот фрагмент выполняется одновременно двумя

процессами на разных ЦП, причем процессор A пытается поместить

вслед за структурой bp структуру bpA, а процессор B - структуру

bpB. По поводу сопоставления быстродействия процессоров не прихо-

дится делать никаких предположений: возможен даже наихудший слу-

чай, когда процессор B исполняет 4 команды языка Си, прежде чем

процессор A исполнит одну. Пусть, например, выполнение программы

процессором A приостанавливается в связи с обработкой прерывания.

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

целостность данных будет поставлена под угрозу (в главе 2 этот

момент уже пояснялся).

Ядро обязано удостовериться в том, что такого рода нарушение

не сможет произойти. Если вопрос об опасности возникновения нару-

шения целостности оставить открытым, как бы редко подобные нару-

шения ни случались, ядро утратит свою неуязвимость и его поведе-

ние станет непредсказуемым. Избежать этого можно тремя способами:

1. Исполнять все критические операции на одном процессоре,

опираясь на стандартные методы сохранения целостности данных

в однопроцессорной системе;

2. Регламентировать доступ к критическим участкам программы, ис-

пользуя элементы блокирования ресурсов;

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

соответствующей переделки алгоритмов.

Первые два способа здесь мы рассмотрим подробнее, третьему спосо-

бу будет посвящено отдельное упражнение.

12.2 ГЛАВНЫЙ И ПОДЧИНЕННЫЙ ПРОЦЕССОРЫ

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

(master) - может работать в режиме ядра, а другой - подчиненный

(slave) - только в режиме задачи, впервые реализовал на машинах

типа VAX 11/780 Гобл (см. [Goble 81]). Эта система, реализованная

вначале на двух машинах, получила свое дальнейшее развитие в сис-

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

Главный процессор несет ответственность за обработку всех обраще-

ний к операционной системе и всех прерываний. Подчиненные процес-

соры ведают выполнением процессов в режиме задачи и информируют

главный процессор о всех производимых обращениях к системным

функциям.

Выбор процессора, на котором будет выполняться данный про-

цесс, производится в соответствии с алгоритмом диспетчеризации

(Рисунок 12.3). В соответствующей записи таблицы процессов появ-

ляется новое поле, в которое записывается идентификатор выбранно-

го процессора; предположим для простоты, что он показывает, явля-

ется ли процессор главным или подчиненным. Когда процесс

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

ном процессоре, подчиненное ядро переустанавливает значение поля

идентификации процессора таким образом, чтобы оно указывало на

главный процессор, и переключает контекст на другие процессы (Ри-

сунок 12.4). Главное ядро запускает на выполнение процесс с наи-

высшим приоритетом среди тех процессов, которые должны выполнять-

ся на главном процессоре. Когда выполнение системной функции за-

вершается, поле идентификации процессора перенастраивается обрат-

но, и процесс вновь возвращается на подчиненный процессор.

Если процессы должны выполняться на главном процессоре, жела-

тельно, чтобы главный процессор обрабатывал их как можно скорее и

не заставлял их ждать своей очереди чересчур долго. Похожая моти-

вировка приводится в объяснение выгрузки процесса из памяти в од-

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

бождением соответствующих ресурсов для выполнения более насущных

счетных операций. Если в тот момент, когда подчиненный процессор

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

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

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

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

при этом глобальный флаг; проверяя установку флага во время обра-

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

вел бы в итоге переключение контекста максимум через один таймер-

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


-------------------------------------------------------------┐

│ алгоритм schedule_process (модифицированный) │

│ входная информация: отсутствует │

│ выходная информация: отсутствует │

│ { │

│ выполнять пока (для запуска не будет выбран один из про-│

│ цессов) │

│ { │

│ если (работа ведется на главном процессоре) │

│ для (всех процессов в очереди готовых к выполне- │

│ нию) │

│ выбрать процесс, имеющий наивысший приоритет │

│ среди загруженных в память; │

│ в противном случае /* работа ведется на подчинен- │

│ * ном процессоре */ │

│ для (тех процессов в очереди, которые не нуждают-│