The design of the unix operating system by Maurice J
Вид материала | Реферат |
11.2 Взаимодействие процессов в версии v системы |
- Лекция 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.
(сигнал прерывания), процесс trace переходит в состояние трасси-
ровки, ожидая поступления команды от программы debug. Если про-
цесс, реализующий программу debug, находился в состоянии приоста-
нова, связанного с выполнением функции wait, он "пробуждается",
обнаруживает наличие порожденного трассируемого процесса и выхо-
дит из функции wait. Затем процесс debug вызывает функцию ptrace,
записывает значение переменной цикла i в пространство данных про-
цесса trace по адресу, содержащемуся в переменной addr, и увели-
чивает значение переменной addr; в программе trace переменная
addr хранит адрес точки входа в массив data. Последнее обращение
процесса debug к функции ptrace вызывает запуск программы trace,
и в этот момент массив data содержит значения от 0 до 31. Отлад-
-------------------------------------------------------------┐
│ #define TR_SETUP 0 │
│ #define TR_WRITE 5 │
│ #define TR_RESUME 7 │
│ int addr; │
│ │
│ main(argc,argv) │
│ int argc; │
│ char *argv[]; │
│ { │
│ int i,pid; │
│ │
│ sscanf(argv[1],"%x",&addr); │
│ │
│ if ((pid = fork() == 0) │
│ { │
│ ptrace(TR_SETUP,0,0,0); │
│ execl("trace","trace",0); │
│ exit(); │
│ } │
│ for (i = 0; i < 32, i++) │
│ { │
│ wait((int *) 0); │
│ /* записать значение i в пространство процесса с │
│ * идентификатором pid по адресу, содержащемуся в │
│ * переменной addr */ │
│ if (ptrace(TR_WRITE,pid,addr,i) == -1) │
│ exit(); │
│ addr += sizeof(int); │
│ } │
│ /* трассируемый процесс возобновляет выполнение */ │
│ ptrace(TR_RESUME,pid,1,0); │
│ } │
L-------------------------------------------------------------
Рисунок 11.3. Программа debug (трассирующий процесс)
чики, подобные sdb, имеют доступ к таблице идентификаторов трас-
сируемого процесса, из которой они получают информацию об адресах
данных, используемых в качестве параметров функции ptrace.
Использование функции ptrace для трассировки процессов явля-
ется обычным делом, но оно имеет ряд недостатков.
* Для того, чтобы произвести передачу порции данных длиною в
слово между процессом-отладчиком и трассируемым процессом,
ядро должно выполнить четыре переключения контекста: оно пе-
реключает контекст во время вызова отладчиком функции ptrace,
загружает и выгружает контекст трассируемого процесса и пе-
реключает контекст вновь на процесс-отладчик по получении от-
вета от трассируемого процесса. Все вышеуказанное необходимо,
поскольку у отладчика нет иного способа получить доступ к
виртуальному адресному пространству трассируемого процесса,
отсюда замедленность протекания процедуры трассировки.
* Процесс-отладчик может вести одновременную трассировку нес-
кольких процессов-потомков, хотя на практике эта возможность
используется редко. Если быть более критичным, следует отме-
тить, что отладчик может трассировать только своих ближайших
потомков: если трассируемый процесс-потомок вызовет функцию
fork, отладчик не будет иметь контроля над порождаемым, вну-
чатым для него, процессом, что является серьезным препятстви-
ем в отладке многоуровневых программ. Если трассируемый про-
цесс вызывает функцию exec, запускаемые образы задач тоже
подвергаются трассировке под управлением ранее вызванной
функции ptrace, однако отладчик может не знать имени исполня-
емого образа, что затрудняет проведение символьной отладки.
* Отладчик не может вести трассировку уже выполняющегося про-
цесса, если отлаживаемый процесс не вызвал предварительно
функцию ptrace, дав тем самым ядру свое согласие на трасси-
ровку. Это неудобно, так как в указанном случае выполняющийся
процесс придется удалить из системы и перезапустить в режиме
трассировки.
* Не разрешается трассировать setuid-программы, поскольку это
может привести к нарушению защиты данных (ибо в результате
выполнения функции ptrace в их адресное пространство произво-
дилась бы запись данных) и к выполнению недопустимых дейс-
твий. Предположим, например, что setuid-программа запускает
файл с именем "privatefile". Умелый пользователь с помощью
функции ptrace мог бы заменить имя файла на "/bin/sh", запус-
тив на выполнение командный процессор shell (и все программы,
исполняемые shell'ом), не имея на то соответствующих полномо-
чий. Функция exec игнорирует бит setuid, если процесс подвер-
гается трассировке, тем самым адресное пространство
setuid-программ защищается от пользовательской записи.
Киллиан [Killian 84] описывает другую схему трассировки про-
цессов, основанную на переключении файловых систем (см. главу 5).
Администратор монтирует файловую систему под именем "/proc";
пользователи идентифицируют процессы с помощью кодов идентифика-
ции и трактуют их как файлы, принадлежащие каталогу "/proc". Ядро
дает разрешение на открытие файлов, исходя из кода идентификации
пользователя процесса и кода идентификации группы. Пользователи
могут обращаться к адресному пространству процесса путем чтения
(read) файла и устанавливать точки прерываний путем записи
(write) в файл. Функция stat сообщает различную статистическую
информацию, касающуюся процесса. В данном подходе устранены три
недостатка, присущие функции ptrace. Во-первых, эта схема работа-
ет быстрее, поскольку процесс-отладчик за одно обращение к ука-
занным системным функциям может передавать больше информации, чем
при работе с ptrace. Во-вторых, отладчик здесь может вести трас-
сировку совершенно произвольных процессов, а не только своих по-
томков. Наконец, трассируемый процесс не должен предпринимать
предварительно никаких действий по подготовке к трассировке; от-
ладчик может трассировать и существующие процессы. Возможность
вести отладку setuid-программ, предоставляемая только суперполь-
зователю, реализуется как составная часть традиционного механизма
защиты файлов.
11.2 ВЗАИМОДЕЙСТВИЕ ПРОЦЕССОВ В ВЕРСИИ V СИСТЕМЫ
Пакет IPC (interprocess communication) в версии V системы
UNIX включает в себя три механизма. Механизм сообщений дает про-
цессам возможность посылать другим процессам потоки сформатиро-
ванных данных, механизм разделения памяти позволяет процессам
совместно использовать отдельные части виртуального адресного
пространства, а семафоры - синхронизировать свое выполнение с вы-
полнением параллельных процессов. Несмотря на то, что они реали-
зуются в виде отдельных блоков, им присущи общие свойства.
* С каждым механизмом связана таблица, в записях которой описы-
ваются все его детали.
* В каждой записи содержится числовой ключ (key), который
представляет собой идентификатор записи, выбранный пользова-
телем.
* В каждом механизме имеется системная функция типа "get", ис-
пользуемая для создания новой или поиска существующей записи;
параметрами функции являются идентификатор записи и различные
флаги (flag). Ядро ведет поиск записи по ее идентификатору в
соответствующей таблице. Процессы могут с помощью флага
IPC_PRIVATE гарантировать получение еще неиспользуемой запи-
си. С помощью флага IPC_CREAT они могут создать новую запись,
если записи с указанным идентификатором нет, а если еще к то-
му же установить флаг IPC_EXCL, можно получить уведомление об
ошибке в том случае, если запись с таким идентификатором су-
ществует. Функция возвращает некий выбранный ядром дескрип-
тор, предназначенный для последующего использования в других
системных функциях, таким образом, она работает аналогично
системным функциям creat и open.
* В каждом механизме ядро использует следующую формулу для по-
иска по дескриптору указателя на запись в таблице структур
данных:
указатель = значение дескриптора по модулю от числа записей в
таблице
Если, например, таблица структур сообщений состоит из 100 за-
писей, дескрипторы, связанные с записью номер 1, имеют значе-
ния, равные 1, 101, 201 и т.д. Когда процесс удаляет запись,
ядро увеличивает значение связанного с ней дескриптора на
число записей в таблице: полученный дескриптор станет новым
дескриптором этой записи, когда к ней вновь будет произведено
обращение при помощи функции типа "get". Процессы, которые
будут пытаться обратиться к записи по ее старому дескриптору,
потерпят неудачу. Обратимся вновь к предыдущему примеру. Если
с записью 1 связан дескриптор, имеющий значение 201, при его
удалении ядро назначит записи новый дескриптор, имеющий зна-
чение 301. Процессы, пытающиеся обратиться к дескриптору 201,
получат ошибку, поскольку этого дескриптора больше нет. В ко-
нечном итоге ядро произведет перенумерацию дескрипторов, но
пока это произойдет, может пройти значительный промежуток
времени.
* Каждая запись имеет некую структуру данных, описывающую права
доступа к ней и включающую в себя пользовательский и группо-
вой коды идентификации, которые имеет процесс, создавший за-
пись, а также пользовательский и групповой коды идентифика-
ции, установленные системной функцией типа "control" (об этом
ниже), и двоичные коды разрешений чтения-записи-исполнения
для владельца, группы и прочих пользователей, по аналогии с
установкой прав доступа к файлам.
* В каждой записи имеется другая информация, описывающая состо-
яние записи, в частности, идентификатор последнего из процес-
сов, внесших изменения в запись (посылка сообщения, прием со-
общения, подключение разделяемой памяти и т.д.), и время
последнего обращения или корректировки.
* В каждом механизме имеется системная функция типа "control",
запрашивающая информацию о состоянии записи, изменяющая эту
информацию или удаляющая запись из системы. Когда процесс за-
прашивает информацию о состоянии записи, ядро проверяет, име-
ет ли процесс разрешение на чтение записи, после чего копиру-
ет данные из записи таблицы по адресу, указанному
пользователем. При установке значений принадлежащих записи
параметров ядро проверяет, совпадают ли между собой пользова-
тельский код идентификации процесса и идентификатор пользова-
теля (или создателя), указанный в записи, не запущен ли про-
цесс под управлением суперпользователя; одного разрешения на
запись недостаточно для установки параметров. Ядро копирует
сообщенную пользователем информацию в запись таблицы, уста-
навливая значения пользовательского и группового кодов иден-
тификации, режимы доступа и другие параметры (в зависимости
от типа механизма). Ядро не изменяет значения полей, описыва-
ющих пользовательский и групповой коды идентификации создате-
ля записи, поэтому пользователь, создавший запись, сохраняет
управляющие права на нее. Пользователь может удалить запись,
либо если он является суперпользователем, либо если идентифи-
катор процесса совпадает с любым из идентификаторов, указан-
ных в структуре записи. Ядро увеличивает номер дескриптора,
чтобы при следующем назначении записи ей был присвоен новый
дескриптор. Следовательно, как уже ранее говорилось, если
процесс попытается обратиться к записи по старому дескрипто-
ру, вызванная им функция получит отказ.
11.2.1 Сообщения
С сообщениями работают четыре системных функции: msgget, ко-
торая возвращает (и в некоторых случаях создает) дескриптор сооб-
щения, определяющий очередь сообщений и используемый другими сис-
темными функциями, msgctl, которая устанавливает и возвращает
связанные с дескриптором сообщений параметры или удаляет дескрип-
торы, msgsnd, которая посылает сообщение, и msgrcv, которая полу-
чает сообщение.
Синтаксис вызова системной функции msgget:
msgqid = msgget(key,flag);
где msgqid - возвращаемый функцией дескриптор, а key и flag имеют
ту же семантику, что и в системной функции типа "get". Ядро хра-
нит сообщения в связном списке (очереди), определяемом значением
дескриптора, и использует значение msgqid в качестве указателя на
массив заголовков очередей. Кроме вышеуказанных полей, описываю-
щих общие для всего механизма права доступа, заголовок очереди
содержит следующие поля:
* Указатели на первое и последнее сообщение в списке;
* Количество сообщений и общий объем информации в списке в бай-
тах;
* Максимальная емкость списка в байтах;
* Идентификаторы процессов, пославших и принявших сообщения
последними;
* Поля, указывающие время последнего выполнения функций msgsnd,
msgrcv и msgctl.
Когда пользователь вызывает функцию msgget для того, чтобы
создать новый дескриптор, ядро просматривает массив очередей со-
общений в поисках существующей очереди с указанным идентификато-
ром. Если такой очереди нет, ядро выделяет новую очередь, инициа-
лизирует ее и возвращает идентификатор пользователю. В противном
случае ядро проверяет наличие необходимых прав доступа и заверша-
ет выполнение функции.
Для посылки сообщения процесс использует системную функцию
msgsnd:
msgsnd(msgqid,msg,count,flag);
где msgqid - дескриптор очереди сообщений, обычно возвращаемый
функцией msgget, msg - указатель на структуру, состоящую из типа
в виде назначаемого пользователем целого числа и массива симво-
лов, count - размер информационного массива, flag - действие,
предпринимаемое ядром в случае переполнения внутреннего буферного
пространства.
Ядро проверяет (Рисунок 11.4), имеется ли у посылающего сооб-
щение процесса разрешения на запись по указанному дескриптору, не
выходит ли размер сообщения за установленную системой границу, не
содержится ли в очереди слишком большой объем информации, а также
является ли тип сообщения положительным целым числом. Если все
условия соблюдены, ядро выделяет сообщению место, используя карту
сообщений (см. раздел 9.1), и копирует в это место данные из
пространства пользователя. К сообщению присоединяется заголовок,
после чего оно помещается в конец связного списка заголовков со-
общений. В заголовке сообщения записывается тип и размер сообще-
-------------------------------------------------------------┐
│ алгоритм msgsnd /* послать сообщение */ │
│ входная информация: (1) дескриптор очереди сообщений │
│ (2) адрес структуры сообщения │
│ (3) размер сообщения │
│ (4) флаги │
│ выходная информация: количество посланных байт │
│ { │
│ проверить правильность указания дескриптора и наличие │
│ соответствующих прав доступа; │
│ выполнить пока (для хранения сообщения не будет выделено│
│ место) │
│ { │
│ если (флаги не разрешают ждать) │
│ вернуться; │
│ приостановиться (до тех пор, пока место не освобо- │
│ дится); │
│ } │
│ получить заголовок сообщения; │
│ считать текст сообщения из пространства задачи в прост- │
│ ранство ядра; │
│ настроить структуры данных: выстроить очередь заголовков│
│ сообщений, установить в заголовке указатель на текст │
│ сообщения, заполнить поля, содержащие счетчики, время │
│ последнего выполнения операций и идентификатор процес- │
│ са; │
│ вывести из состояния приостанова все процессы, ожидающие│
│ разрешения считать сообщение из очереди; │
│ } │
L-------------------------------------------------------------
Рисунок 11.4. Алгоритм посылки сообщения
ния, устанавливается указатель на текст сообщения и производится
корректировка содержимого различных полей заголовка очереди, со-
держащих статистическую информацию (количество сообщений в очере-
ди и их суммарный объем в байтах, время последнего выполнения
операций и идентификатор процесса, пославшего сообщение). Затем
ядро выводит из состояния приостанова все процессы, ожидающие по-
полнения очереди сообщений. Если размер очереди в байтах превыша-
ет границу допустимости, процесс приостанавливается до тех пор,
пока другие сообщения не уйдут из очереди. Однако, если процессу
было дано указание не ждать (флаг IPC_NOWAIT), он немедленно
возвращает управление с уведомлением об ошибке. На Рисунке 11.5
показана очередь сообщений, состоящая из заголовков сообщений,
организованных в связные списки, с указателями на область текста.
Рассмотрим программу, представленную на Рисунке 11.6. Процесс
вызывает функцию msgget для того, чтобы получить дескриптор для
записи с идентификатором MSGKEY. Длина сообщения принимается рав-
ной 256 байт, хотя используется только первое поле целого типа, в
область текста сообщения копируется идентификатор процесса, типу
сообщения присваивается значение 1, после чего вызывается функция
msgsnd для посылки сообщения. Мы вернемся к этому примеру позже.
Процесс получает сообщения, вызывая функцию msgrcv по следую-
щему формату:
count = msgrcv(id,msg,maxcount,type,flag);
где id - дескриптор сообщения, msg - адрес пользовательской
структуры, которая будет содержать полученное сообщение, maxcount
- размер структуры msg, type - тип считываемого сообщения, flag -
действие, предпринимаемое ядром в том случае, если в очереди со-
Заголовки Область
очередей текста
-------┐ Заголовки сообщений -->-------┐
│ │ -------┐ -------┐ -------┐ │ │ │
│ --+---->│ +--->│ +--->│ │ │ │ │
│ │ L---+--- L---+--- L---+--- │ │ │
+------+ │ │ L----- │ │
│ │ L-----------│------------------>+------+
│ │ │ │ │
│ │ │ │ │
+------+ │ │ │
│ │ -------┐ │ │ │
│ --+---->│ │ │ │ │
│ │ L---+--- │ │ │
+------+ │ │ │ │
│ │ │ │ +------+
│ │ L-----------│------------------>+------+
│ │ │ │ │
│ │ │ │ │
│ │ L------------------>+------+
│ │ │ │
│ │ +------+
│ │ │ │
│ │ │ │
│ │ │ │
L------- L-------
Рисунок 11.5. Структуры данных, используемые в организации
сообщений
общений нет. В переменной count пользователю возвращается число
прочитанных байт сообщения.
Ядро проверяет (Рисунок 11.7), имеет ли пользователь необхо-
димые права доступа к очереди сообщений. Если тип считываемого
сообщения имеет нулевое значение, ядро ищет первое по счету сооб-
щение в связном списке. Если его размер меньше или равен размеру,
указанному пользователем, ядро копирует текст сообщения в пользо-
вательскую структуру и соответствующим образом настраивает свои
внутренние структуры: уменьшает счетчик сообщений в очереди и
суммарный объем информации в байтах, запоминает время получения
сообщения и идентификатор процесса-получателя, перестраивает
связный список и освобождает место в системном пространстве, где
хранился текст сообщения. Если какие-либо процессы, ожидавшие по-
лучения сообщения, находились в состоянии приостанова из-за от-
сутствия свободного места в списке, ядро выводит их из этого сос-
тояния. Если размер сообщения превышает значение maxcount, ука-
занное пользователем, ядро посылает системной функции уведомление
об ошибке и оставляет сообщение в очереди. Если, тем не менее,
процесс игнорирует ограничения на размер (в поле flag установлен