The design of the unix operating system by Maurice J

Вид материалаРеферат
11.1 Трассировка процессов
Подобный материал:
1   ...   41   42   43   44   45   46   47   48   ...   55

управление через короткий промежуток времени в случае отсутс-

твия данных (например, если newtty.c_cc[VMIN] = 0 на Рисунке

10.17). Эту особенность сложно реализовать в потоковой среде

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

тока.

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

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

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

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

зовательского процесса.

Несмотря на эти несоответствия, с потоками связываются боль-

шие надежды в совершенствовании разработки модулей драйвера.


10.5 ВЫВОДЫ


Данная глава представляет собой обзор драйверов устройств в

системе UNIX. Устройства могут быть либо блочного, либо символь-

ного типа; интерфейс между устройствами и остальной частью ядра

определяется типом устройств. Интерфейсом для устройств блочного

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

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

закрытия устройств и стратегической процедуре. Стратегическая

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

типа. Интерфейсом для устройств символьного типа выступает табли-

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

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

устройства, чтения, записи и процедуре ioctl. Системная функция

ioctl использует при обращении к устройствам символьного типа

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

дачу управляющей информации между процессами и устройствами. По

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

ботки соответствующего прерывания, опираясь на информацию, храня-

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

устройством, от которого поступило прерывание.

Дисковые драйверы превращают номера логических блоков, ис-

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

ный интерфейс дает возможность ядру буферизовать данные. Взаимо-

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

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

тему.

Терминальные драйверы осуществляют непосредственное взаимо-

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

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

туры, один для ввода с обработкой символов стирания, удаления и

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

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

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

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

волов. Getty-процесс открывает терминальные линии и ждет связи:

он формирует группу процессов во главе с регистрационным

shell'ом, инициализирует с помощью функции ioctl параметры терми-

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

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

процессам в группе сигналы в ответ на возникновение таких собы-

тий, как "зависание" пользователя или нажатие им клавиши прерыва-

ния.

Потоки выступают средством повышения модульности построения

драйверов устройств и протоколов. Поток - это полнодуплексная

связь между процессами и драйверами устройств, которая может

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

ной обработки данных. Модули потоков характеризуются четко опре-

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

в сочетании с другими модулями. Эта гибкость имеет особое значе-


ние для сетевых протоколов и драйверов.


10.6 УПРАЖНЕНИЯ


*1. Предположим, что в системе имеются два файла устройств с од-

ними и теми же старшим и младшим номерами, при том, что оба

устройства - символьного типа. Если два процесса желают од-

новременно открыть физическое устройство, не будет никакой

разницы, открывают ли они один и тот же файл устройства или

же разные файлы. Что произойдет, когда они станут закрывать

устройство ?

*2. Вспомним из главы 5, что системной функции mknod требуется

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

файла устройства. Если доступ к устройству управляется пра-

вами доступа к файлу, почему функции mknod нужно разрешение

суперпользователя ?

3. Напишите программу, которая проверяет, что файловые системы

на диске не перекрываются. Этой программе потребовались бы

два аргумента: файл устройства, представляющий дисковый том,

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

мер для диска данного типа. Для проверки отсутствия перекры-

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

ков. Будет ли такая программа всегда правильной ?

4. Программа mkfs инициализирует файловую систему на диске

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

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

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

mkfs ? Как изменится эта программа при наличии таблицы со-

держимого тома ? Каким образом следует инициализировать таб-

лицу содержимого тома ?

5. Программы mkfs и fsck (глава 5) являются программами пользо-

вательского уровня, а не частью ядра. Прокомментируйте это.

6. Предположим, что программисту нужно разработать базу данных,

работающую в среде ОС UNIX. Программы базы данных выполняют-

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

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

ком ? Подумайте над следующими вопросами:

* Использование стандартного интерфейса файловой системы

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

ными на диске,

* Потребность в быстродействии,

* Необходимость знать, когда фактически данные располагаются

на диске,

* Размер базы данных: должна ли она помещаться в одной фай-

ловой системе, занимать собой весь дисковый том или же

располагаться на нескольких дисковых томах ?

7. Ядро системы UNIX по умолчанию предполагает, что файловая

система располагается на идеальных дисках. Однако, диски мо-

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

из строя определенные сектора, несмотря на то, что остальная

часть диска осталась "пригодной". Как дисковому драйверу

(или интеллектуальному контроллеру диска) следует учитывать

небольшое количество плохих секторов. Как это отразилось бы

на производительности системы ?

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

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

специального файла устройства по завершении выполнения вызо-

ва системной функции mount. При демонтировании файловой сис-

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

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

вновь освобождает индекс. Сравните эту последовательность

операций над индексом, а также обращений к процедурам откры-

тия и закрытия драйвера, с последовательностью действий, со-

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

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

9. Выполните программу, приведенную на Рисунке 10.14, но нап-

равьте вывод данных в файл. Сравните содержимое файла с со-

держимым выводного потока, когда вывод идет на терминал. Вам

придется прервать процессы, чтобы остановить их; только

прежде пусть они получат достаточно большое количество дан-

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

заменить на

printf(output);

10. Что произойдет, если пользователь попытается выполнить ре-

дактирование текста на фоне программы:

ed file &

Обоснуйте ответ.

11. К файлам терминалов обычно устанавливаются следующие права

доступа

crw--w--w- 2 mjb lus 33,11 Oct 25 20:27 tty61

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

разрешаются пользователю с именем "mjb", а остальным пользо-

вателям разрешена только запись. Почему ?

12. Предположим, что вам известно имя файла терминала вашего то-

варища. Напишите программу записи сообщений с вашего терми-

нала на терминал вашего товарища. Какая еще информация вам

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

команды write ?

13. Выполните команду stty: если параметры не указаны, она выби-

рает значения установок терминала и сообщает их пользовате-

лю. В противном случае пользователь может в интерактивном

режиме сделать различные установки сам.

14. Напишите элементарный строковый интерфейс, записывающий

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

ка.

15. В каноническом режиме пользователь может на время приостано-

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

виш , и продолжить вывод, нажав . Как в

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

ность ?

*16. Процесс начальной загрузки порождает getty-процесс для каж-

дой терминальной линии в системе. Что произошло бы, если бы

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

два getty-процесса, ожидающие регистрации пользователя ? Мо-

жет ли ядро помешать этому ?

17. Пусть командный процессор shell реализован таким образом,

что он "игнорирует" конец файла и продолжает считывать дан-

ные из стандартного ввода. Что произошло бы, если бы пользо-

ватель (в регистрационном shell'е) угадал конец файла и про-

должил ввод с клавиатуры ?

*18. Предположим, что процесс считывает данные с операторского

терминала, но игнорирует или улавливает сигналы о "зависа-

нии". Что произойдет, когда процесс продолжит считывать дан-

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

19. Программа getty-процесса несет ответственность за открытие

терминальной линии, а программа login - за проверку регист-

рационных имен и паролей. Какие преимущества в том, что эти

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

20. Рассмотрим два метода реализации драйвера косвенного терми-

нала ("/dev/tty"), описанные в разделе 10.3.6. Какие разли-

чия между ними чувствует пользователь ? (Совет: подумайте о

системных функциях stat и fstat).

21. Разработайте метод планирования выполнения модулей потока, в

соответствии с которым ядро имеет в своем составе специаль-

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

да, когда выполнение этих процедур запланировано.

*22. Разработайте схему построения виртуальных терминалов (окон)

с использованием традиционных (не потоковых) драйверов.

*23. Разработайте метод реализации виртуальных терминалов с ис-

пользованием потоков, в котором мультиплексированием вво-

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

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

процесс. Опишите механизм соединения потоков со сверткой и

разверткой. Что лучше: включить модуль, осуществляющий муль-

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

вательский процесс ?

24. Команда ps сообщает интересную информацию об активности про-

цессов в работающей системе. В традиционных реализациях ps

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

ядра. Такой метод не совсем удобен в среде разработки, когда

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

новится нелегко обнаружить в таблице соответствующие поля. ВЗАИМОДЕЙСТВИЕ ПРОЦЕССОВ


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

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

выполнение с другими процессами. Мы уже рассмотрели несколько

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

зование поименованных каналов и посылка сигналов. Каналы (непои-

менованные) имеют недостаток, связанный с тем, что они известны

только потомкам процесса, вызвавшего системную функцию pipe: не

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

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

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

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

ваться ни в сети (см. главу 13), ни в организации множественных

связей между различными группами взаимодействующих процессов: по-

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

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

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

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

ции kill, однако такое "сообщение" состоит из одного только номе-

ра сигнала.

В данной главе описываются другие формы взаимодействия про-

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

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

затем рассматривается пакет IPC: сообщения, разделяемая память и

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

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

дается представление о "гнездах", применяющихся в системе BSD.

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

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

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


11.1 ТРАССИРОВКА ПРОЦЕССОВ


В системе UNIX имеется простейшая форма взаимодействия про-

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

Процесс-отладчик, например sdb, порождает трассируемый процесс и

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

расставляя и сбрасывая контрольные точки, считывая и записывая

данные в его виртуальное адресное пространство. Трассировка про-

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

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

нием последнего.


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

│ if ((pid = fork()) == 0) │

│ { │

│ /* потомок - трассируемый процесс */ │

│ ptrace(0,0,0,0); │

│ exec("имя трассируемого процесса"); │

│ } │

│ /* продолжение выполнения процесса-отладчика */ │

│ for (;;) │

│ { │

│ wait((int *) 0); │

│ read(входная информация для трассировки команд) │

│ ptrace(cmd,pid,...); │

│ if (условие завершения трассировки) │

│ break; │

│ } │

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


Рисунок 11.1. Структура процесса отладки


Псевдопрограмма, представленная на Рисунке 11.1, имеет типич-

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

цесс, запускающий системную функцию ptrace, в результате чего в

соответствующей процессу-потомку записи таблицы процессов ядро


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

запуска (exec) трассируемой программы. Например, если пользова-

тель ведет отладку программы a.out, процесс-потомок запускает

файл с тем же именем. Ядро отрабатывает функцию exec обычным по-

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

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

exec, как и на выходе из любой другой функции, ядро проверяет на-

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

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

обработки сигналов. Заметив установку бита трассировки, про-

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

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

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

танова (но не показанное на диаграмме состояний процесса, см. Ри-

сунок 6.1), и выполняет переключение контекста.

Тем временем в обычной ситуации процесс-родитель (отладчик)

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

от трассируемого процесса. Когда соответствующее известие процес-

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

(wait), прочитает (read) введенные пользователем команды и прев-

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

ровкой процесса-потомка. Синтаксис вызова системной функции

ptrace:


ptrace(cmd,pid,addr,data);

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

ния данных, записи данных, возобновления выполнения и т.п., pid -

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

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

или запись, data - целое значение, предназначенное для записи. Во

время исполнения системной функции ptrace ядро проверяет, имеется

ли у отладчика потомок с идентификатором pid и находится ли этот

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

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

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

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

ядро записывает в нее параметры cmd, addr и data, возобновляет

процесс-потомок, переводит его в состояние "готовности к выполне-

нию" и приостанавливается до получения от него ответа. Когда про-

цесс-потомок продолжит свое выполнение (в режиме ядра), он

исполнит соответствующую (трассируемую) команду, запишет резуль-

тат в глобальную структуру и "разбудит" отладчика. В зависимости

от типа команды потомок может вновь перейти в состояние трасси-

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

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

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

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

возвращает управление пользователю.

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

ровки отладчик не находится в состоянии приостанова (wait), он

не обнаружит потомка, пока не обратится к функции wait, после че-

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

ному плану.


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

│ int data[32]; │

│ main() │

│ { │

│ int i; │

│ for (i = 0; i < 32; i++) │

│ printf("data[%d] = %d\n@,i,data[i]); │

│ printf("ptrace data addr Ox%x\n",data); │

│ } │

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


Рисунок 11.2. Программа trace (трассируемый процесс)


Рассмотрим две программы, приведенные на Рисунках 11.2 и 11.3

и именуемые trace и debug, соответственно. При запуске программы

trace с терминала массив data будет содержать нулевые значения;

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

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

веденного программой trace, происходит следующее: программа запо-

минает значение параметра в переменной addr, создает новый про-

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

ке, и запускает программу trace. На выходе из функции exec ядро

посылает процессу-потомку (назовем его тоже trace) сигнал SIGTRAP