The design of the unix operating system by Maurice J
Вид материала | Реферат |
- Лекция 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.
│ │
│ main() │
│ { │
│ register int i; │
│ │
│ for (i = 0; i < 18; i++) │
│ { │
│ switch (fork()) │
│ { │
│ case -1: /* ошибка */ │
│ printf("операция fork не выполнена из-за ошибки\n");│
│ exit(); │
│ │
│ default: /* родительский процесс */ │
│ break; │
│ │
│ case 0: /* порожденный процесс */ │
│ for (;;) │
│ { │
│ read(0,input,256); /* чтение строки */ │
│ printf("%d чтение %s\n",i,input); │
│ } │
│ } │
│ } │
│ } │
L---------------------------------------------------------------
Рисунок 10.16. Конкуренция за данные, вводимые с терминала
На Рисунке 10.16 приведена программа, в которой родительский
процесс порождает несколько процессов, осуществляющих чтение из
файла стандартного ввода, конкурируя за получение данных, вводи-
мых с терминала. Ввод с терминала обычно осуществляется слишком
медленно для того, чтобы удовлетворить все процессы, ведущие чте-
ние, поэтому процессы большую часть времени находятся в приоста-
новленном состоянии в соответствии с алгоритмом terminal_read,
ожидая ввода данных. Когда пользователь вводит строку данных,
программа обработки прерываний от терминала возобновляет выполне-
ние всех процессов, ведущих чтение; поскольку они были приоста-
новлены с одним и тем же уровнем приоритета, они выбираются для
запуска с одинаковым уровнем приоритета. Пользователь не в состо-
янии предугадать, какой из процессов выполняется и считывает
строку данных; успешно созданный процесс печатает значение пере-
менной i в момент его создания. Все другие процессы в конце кон-
цов будут запущены, но вполне возможно, что они не обнаружат
введенной информации в списках для хранения вводных данных и их
выполнение снова будет приостановлено. Вся процедура повторяется
для каждой введенной строки; нельзя дать гарантию, что ни один из
процессов не захватит все введенные данные.
Одновременному чтению с терминала несколькими процессами при-
суща неоднозначность, но ядро справляется с ситуацией наилучшим
образом. С другой стороны, ядро обязано позволять процессам од-
новременно считывать данные с терминала, иначе порожденные ко-
мандным процессором shell процессы, читающие из стандартного вво-
да, никогда не будут работать, поскольку shell тоже обращается к
стандартному вводу. Короче говоря, процессы должны синхронизиро-
вать свои обращения к терминалу на пользовательском уровне.
Когда пользователь вводит символ "конец файла" (Ctrl-d в
ASCII), строковый интерфейс передает функции read введенную стро-
ку до символа конца файла, но не включая его. Он не передает дан-
ные (код возврата 0) функции read, если в символьном списке
встретился только символ "конец файла"; вызывающий процесс сам
распознает, что обнаружен конец файла и больше не следует считы-
вать данные с терминала. Если еще раз обратиться к примерам прог-
рамм по shell'у, приведенным в главе 7, можно отметить, что цикл
работы shell'а завершается, когда пользователь нажимает
функция read возвращает 0 и производится выход из shell'а.
В этом разделе рассмотрена работа терминалов ввода-вывода,
которые передают данные на машину по одному символу за одну опе-
рацию, в точности как пользователь их вводит с клавиатуры. Интел-
лектуальные терминалы подготавливают свой вводной поток на внеш-
нем устройстве, освобождая центральный процессор для другой
работы. Структура драйверов для таких терминалов походит на
структуру драйверов для терминалов ввода-вывода, несмотря на то,
что функции строкового интерфейса различаются в зависимости от
возможностей внешних устройств.
10.3.3 Терминальный драйвер в режиме без обработки символов
Пользователи устанавливают параметры терминала, такие как
символы стирания и удаления, и извлекают значения текущих устано-
вок с помощью системной функции ioctl. Сходным образом они уста-
навливают необходимость эхо-сопровождения ввода данных с термина-
ла, задают скорость передачи информации в бодах, заполняют
очереди символов ввода и вывода или вручную запускают и останав-
ливают выводной поток символов. В информационной структуре терми-
нального драйвера хранятся различные управляющие установки (см.
[SVID 85], стр.281), и строковый интерфейс получает параметры
функции ioctl и устанавливает или считывает значения соответству-
ющих полей структуры данных. Когда процесс устанавливает значения
параметров терминала, он делает это для всех процессов, использу-
ющих терминал. Установки терминала не сбрасываются автоматически
при выходе из процесса, сделавшего изменения в установках.
Процессы могут также перевести терминал в режим без обработки
символов, в котором строковый интерфейс передает символы в точном
соответствии с тем, как пользователь ввел их: обработка вводного
потока полностью отсутствует. Однако, ядро должно знать, когда
выполнить вызванную пользователем системную функцию read, пос-
кольку символ возврата каретки трактуется как обычный введенный
символ. Оно выполняет функцию read после того, как с терминала
будет введено минимальное число символов или по прохождении фик-
сированного промежутка времени от момента получения с терминала
любого набора символов. В последнем случае ядро хронометрирует
ввод символов с терминала, помещая записи в таблицу ответных сиг-
налов (глава 8). Оба критерия (минимальное число символов и фик-
сированный промежуток времени) задаются в вызове функции ioctl.
Когда соответствующие критерии удовлетворены, программа обработки
прерываний строкового интерфейса возобновляет выполнение всех
приостановленных процессов. Драйвер пересылает все символы из
списка для хранения неструктурированных вводных данных в канони-
ческий список и выполняет запрос процесса на чтение, следуя тому
же самому алгоритму, что и в случае работы в каноническом режиме.
Режим без обработки символов особенно важен в экранно-ориентиро-
ванных приложениях, таких как экранный редактор vi, многие из ко-
манд которого не заканчиваются символом возврата каретки. Напри-
мер, команда dw удаляет слово в текущей позиции курсора.
На Рисунке 10.17 приведена программа, использующая функцию
ioctl для сохранения текущих установок терминала для файла с
дескриптором 0, что соответствует значению дескриптора файла
стандартного ввода. Функция ioctl с командой TCGETA приказывает
-----------------------------------------------------------------┐
│ #include
│ #include
│ struct termio savetty; │
│ main() │
│ { │
│ extern sigcatch(); │
│ struct termio newtty; │
│ int nrd; │
│ char buf[32]; │
│ signal(SIGINT,sigcatch); │
│ if (ioctl(0,TCGETA,&savetty) == -1) │
│ { │
│ printf("ioctl завершилась неудачно: нет терминала\n"); │
│ exit(); │
│ } │
│ newtty = savetty; │
│ newtty.c_lflag &= ~ICANON;/* выход из канонического режима */│
│ newtty.c_lflag &= ~ECHO; /* отключение эхо-сопровождения*/ │
│ newtty.c_cc[VMIN] = 5; /* минимум 5 символов */ │
│ newtty.c_cc[VTIME] = 100; /* интервал 10 секунд */ │
│ if (ioctl(0,TCSETAF,&newtty) == -1) │
│ { │
│ printf("не могу перевести тер-л в режим без обработки\n");│
│ exit(); │
│ } │
│ for(;;) │
│ { │
│ nrd = read(0,buf,sizeof(buf)); │
│ buf[nrd] = 0; │
│ printf("чтение %d символов '%s'\n",nrd,buf); │
│ } │
│ } │
│ sigcatch() │
│ { │
│ ioctl(0,TCSETAF,&savetty); │
│ exit(); │
│ } │
L-----------------------------------------------------------------
Рисунок 10.17. Режим без обработки - чтение 5-символьных блоков
драйверу извлечь установки и сохранить их в структуре с именем
savetty в адресном пространстве задачи. Эта команда часто исполь-
зуется для того, чтобы определить, является ли файл терминалом
или нет, поскольку она ничего не изменяет в системе: если она за-
вершается неудачно, процессы предполагают, что файл не является
терминалом. Здесь же, процесс вторично вызывает функцию ioctl для
того, чтобы перевести терминал в режим без обработки: он отключа-
ет эхо-сопровождение ввода символов и готовится к выполнению опе-
раций чтения с терминала по получении с терминала 5 символов, как
минимум, или по прохождении 10 секунд с момента ввода первой пор-
ции символов. Когда процесс получает сигнал о прерывании, он
сбрасывает первоначальные параметры терминала и завершается.
-----------------------------------------------------------------┐
│ #include
│ │
│ main() │
│ { │
│ register int i,n; │
│ int fd; │
│ char buf[256]; │
│ │
│ /* открытие терминала только для чтения с опцией "no delay" */ │
│ if((fd = open("/dev/tty",O_RDONLY│O_NDELAY)) == -1) │
│ exit(); │
│ │
│ n = 1; │
│ for(;;) /* всегда */ │
│ { │
│ for(i = 0; i < n; i++) │
│ ; │
│ │
│ if(read(fd,buf,sizeof(buf)) > 0) │
│ { │
│ printf("чтение с номера %d\n",n); │
│ n--; │
│ } │
│ else │
│ /* ничего не прочитано; возврат вследствие "no delay" */ │
│ n++; │
│ } │
│ } │
L-----------------------------------------------------------------
Рисунок 10.18. Опрос терминала
10.3.4 Опрос терминала
Иногда удобно производить опрос устройства, то есть считывать
с него данные, если они есть, или продолжать выполнять обычную
работу - в противном случае. Программа на Рисунке 10.18 иллюстри-
рует этот случай: после открытия терминала с параметром "no
delay" (без задержки) процессы, ведущие чтение с него, не приос-
тановят свое выполнение в случае отсутствия данных, а вернут уп-
равление немедленно (см. алгоритм terminal_read, Рисунок 10.15).
Этот метод работает также, если процесс следит за множеством уст-
ройств: он может открыть каждое устройство с параметром "no
delay" и опросить всех из них, ожидая поступления информации с
каждого. Однако, этот метод растрачивает вычислительные мощности
системы.
В системе BSD есть системная функция select, позволяющая про-
изводить опрос устройства. Синтаксис вызова этой функции:
select(nfds,rfds,wfds,efds,timeout)
где nfds - количество выбираемых дескрипторов файлов, а rfds,
wfds и efds указывают на двоичные маски, которыми "выбирают"
дескрипторы открытых файлов. То есть, бит 1 << fd (сдвиг на 1
разряд влево значения дескриптора файла) соответствует установке
на тот случай, если пользователю нужно выбрать этот дескриптор
файла. Параметр timeout (тайм-аут) указывает, на какое время сле-
дует приостановить выполнение функции select, ожидая поступления
данных, например; если данные поступают для любых дескрипторов и
тайм-аут не закончился, select возвращает управление, указывая в
двоичных масках, какие дескрипторы были выбраны. Например, если
пользователь пожелал приостановиться до момента получения данных
по дескрипторам 0, 1 или 2, параметр rfds укажет на двоичную мас-
ку 7; когда select возвратит управление, двоичная маска будет за-
менена маской, указывающей, по каким из дескрипторов имеются го-
товые данные. Двоичная маска wfds выполняет похожую функцию в от-
ношении записи дескрипторов, а двоичная маска efds указывает на
существование исключительных условий, связанных с конкретными
дескрипторами, что бывает полезно при работе в сети.
10.3.5 Назначение операторского терминала
Операторский терминал - это терминал, с которого пользователь
регистрируется в системе, он управляет процессами, запущенными
пользователем с терминала. Когда процесс открывает терминал,
драйвер терминала открывает строковый интерфейс. Если процесс
возглавляет группу процессов как результат выполнения системной
функции setpgrp и если процесс не связан с одним из операторских
терминалов, строковый интерфейс делает открываемый терминал опе-
раторским. Он сохраняет старший и младший номера устройства для
файла терминала в адресном пространстве, выделенном процессу, а
номер группы процессов, связанной с открываемым процессом, в
структуре данных терминального драйвера. Открываемый процесс ста-
новится управляющим процессом, обычно входным (начальным) команд-
ным процессором, что мы увидим далее.
Операторский терминал играет важную роль в обработке сигна-
лов. Когда пользователь нажимает клавиши "delete" (удаления),
"break" (прерывания), стирания или выхода, программа обработки
прерываний загружает строковый интерфейс, который посылает соот-
ветствующий сигнал всем процессам в группе. Подобно этому, когда
пользователь "зависает", программа обработки прерываний от терми-
нала получает информацию о "зависании" от аппаратуры, и строковый
интерфейс посылает соответствующий сигнал всем процессам в груп-
пе. Таким образом, все процессы, запущенные с конкретного терми-
нала, получают сигнал о "зависании"; реакцией по умолчанию для
большинства процессов будет выход из программы по получении сиг-
нала; это похоже на то, как при завершении работы пользователя с
терминалом из системы удаляются побочные процессы. После посылки
сигнала о "зависании" программа обработки прерываний от терминала
разъединяет терминал с группой процессов, чтобы процессы из этой
группы не могли больше получать сигналы, возникающие на термина-
ле.
10.3.6 Драйвер косвенного терминала
Зачастую процессам необходимо прочитать ил записать данные
непосредственно на операторский терминал, хотя стандартный ввод и
вывод могут быть переназначены в другие файлы. Например, shell
может посылать срочные сообщения непосредственно на терминал,
несмотря на то, что его стандартный файл вывода и стандартный
файл ошибок, возможно, переназначены в другое место. В версиях
системы UNIX поддерживается "косвенный" доступ к терминалу через
файл устройства "/dev/tty", в котором для каждого процесса опре-
делен управляющий (операторский) терминал. Пользователи, прошед-
шие регистрацию на отдельных терминалах, могут обращаться к файлу
"/dev/tty", но они получат доступ к разным терминалам.
Существует два основных способа поиска ядром операторского
терминала по имени файла "/dev/tty". Во-первых, ядро может специ-
ально указать номер устройства для файла косвенного терминала с
отдельной точкой входа в таблицу ключей устройств посимвольного
ввода-вывода. При запуске косвенного терминала драйвер этого тер-
минала получает старший и младший номера операторского терминала
из адресного пространства, выделенного процессу, и запускает
драйвер реального терминала, используя данные таблицы ключей уст-
ройств посимвольного ввода-вывода. Второй способ, обычно исполь-
зуемый для поиска операторского терминала по имени "/dev/tty",
связан с проверкой соответствия старшего номера устройства номеру
косвенного терминала перед вызовом процедуры open, определяемой
типом данного драйвера. В случае совпадения номеров освобождается
индекс файла "/dev/tty", выделяется индекс операторскому термина-
лу, точка входа в таблицу файлов переустанавливается так, чтобы
указывать на индекс операторского терминала, и вызывается проце-
дура open, принадлежащая терминальному драйверу. Дескриптор фай-
ла, возвращенный после открытия файла "/dev/tty", указывает не-
посредственно на операторский терминал и его драйвер.
10.3.7 Вход в систему
Как показано в главе 7, процесс начальной загрузки, имеющий
номер 1, выполняет бесконечный цикл чтения из файла
"/etc/inittab" инструкций о том, что нужно делать, если загружае-
мая система определена как "однопользовательская" или "многополь-
зовательская". В многопользовательском режиме самой первой обя-
занностью процесса начальной загрузки является предоставление
пользователям возможности регистрироваться в системе с терминалов
(Рисунок 10.19). Он порождает процессы, именуемые getty-процесса-
ми (от "get tty" - получить терминал), и следит за тем, какой из
процессов открывает какой терминал; каждый getty-процесс устанав-
ливает свою группу процессов, используя вызов системной функции
setpgrp, открывает отдельную терминальную линию и обычно приоста-
навливается во время выполнения функции open до тех пор, пока ма-
шина не получит аппаратную связь с терминалом. Когда функция open
возвращает управление, getty-процесс исполняет программу login
(регистрации в системе), которая требует от пользователей, чтобы
они идентифицировали себя указанием регистрационного имени и па-
роля. Если пользователь зарегистрировался успешно, программа
login наконец запускает командный процессор shell и пользователь
приступает к работе. Этот вызов shell'а именуется "login shell"
(регистрационный shell, регистрационный интерпретатор команд).
Процесс, связанный с shell'ом, имеет тот же идентификатор, что и
начальный getty-процесс, поэтому login shell является процессом,
возглавляющим группу процессов. Если пользователь не смог успешно
зарегистрироваться, программа регистрации завершается через опре-
деленный промежуток времени, закрывая открытую терминальную ли-
нию, а процесс начальной загрузки порождает для этой линии следу-
ющий getty-процесс. Процесс начальной загрузки делает паузу до
получения сигнала об окончании порожденного ранее процесса. После
возобновления работы он выясняет, был ли прекративший существова-
ние процесс регистрационным shell'ом и если это так, порождает
еще один getty-процесс, открывающий терминал, вместо прекративше-
го существование.
-------------------------------------------------------------┐
│ алгоритм login /* процедура регистрации */ │
│ { │
│ исполняется getty-процесс: │
│ установить группу процессов (вызов функции setpgrp); │
│ открыть терминальную линию; /* приостанов до завершения│
│ открытия */ │
│ если (открытие завершилось успешно) │
│ { │
│ исполнить программу регистрации: │
│ запросить имя пользователя; │
│ отключить эхо-сопровождение, запросить пароль; │
│ если (регистрация прошла успешно) │
│ /* найден соответствующий пароль в /etc/passwd */ │
│ { │
│ перевести терминал в канонический режим (ioctl);│
│ исполнить shell; │
│ } │
│ в противном случае │
│ считать количество попыток регистрации, пытаться│
│ зарегистрироваться снова до достижения опреде- │
│ ленной точки; │
│ } │
│ } │
L-------------------------------------------------------------
Рисунок 10.19. Алгоритм регистрации
10.4 ПОТОКИ
Схема реализации драйверов устройств, хотя и отвечает зало-
женным требованиям, страдает некоторыми недостатками, которые с
годами стали заметнее. Разные драйверы имеют тенденцию дублиро-
вать свои функции, в частности драйверы, которые реализуют сете-
вые протоколы и которые обычно включают в себя секцию управления
устройством и секцию протокола. Несмотря на то, что секция прото-
кола должна быть общей для всех сетевых устройств, на практике
это не так, поскольку ядро не имеет адекватных механизмов для об-