The design of the unix operating system by Maurice J
Вид материала | Реферат |
Многопроцессорные системы 12.1 Проблемы, связанные с многопроцессорными системами 12.2 Главный и подчиненный процессоры |
- Лекция 10. Файловые системы Unix, 116.79kb.
- Уровни рассмотрения, 314.07kb.
- Курс по операционным системам (на примере ос windows) Основан на учебном курсе Windows, 29.21kb.
- Выполнил ученик 11 «А» класса, 443.51kb.
- Ос лекция 1 (2-й семестр – временно), 101.4kb.
- Operating System, 7686.97kb.
- Unix-подобные операционные системы, характеристики, особенности, разновидности, 40.63kb.
- 1. ms sql server. Общие сведения, 66.03kb.
- Shanti ananda maurice, 89.84kb.
- Методические материалы, 3002.45kb.
данных 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 (модифицированный) │
│ входная информация: отсутствует │
│ выходная информация: отсутствует │
│ { │
│ выполнять пока (для запуска не будет выбран один из про-│
│ цессов) │
│ { │
│ если (работа ведется на главном процессоре) │
│ для (всех процессов в очереди готовых к выполне- │
│ нию) │
│ выбрать процесс, имеющий наивысший приоритет │
│ среди загруженных в память; │
│ в противном случае /* работа ведется на подчинен- │
│ * ном процессоре */ │
│ для (тех процессов в очереди, которые не нуждают-│