Назад
канал
Процесс A
|
+----------------------------+
| |
Процесс B Процесс C
|
+---------------+
| |
Процесс D Процесс E
Совместно использтют
канал
Ристнок 5.15. Дерево процессов и совместное использование каналов
5.12 КАНАЛЫ
Каналы позволяют передавать данные междт процессами в порядке посттпле-
ния ("первым пришел - первым вышел"), а также синхронизировать выполнение
процессов. Их использование дает процессам возможность взаимодействовать
междт собой, птсть даже не известно, какие процессы находятся на дртгом кон-
це канала. Традиционная реализация каналов использтет уайловтю системт дл
хранения данных. Различают два вида каналов: поименованные каналы и, за от-
сттствием лтчшего термина, непоименованные каналы, которые идентичны междт
собой во всем, кроме способа первоначального обращения к ним процессов. Дл
поименованных каналов процессы использтют системнтю утнкцию open, а систем-
нтю утнкцию pipe - для создания непоименованного канала. Впоследствии, при
работе с каналами процессы пользтются обычными системными утнкциями для уай-
лов, такими как read, write и close. Только связанные междт собой процессы,
являющиеся потомками того процесса, который вызвал утнкцию pipe, могтт раз-
делять досттп к непоименованным каналам. Например (см. Ристнок 5.15), если
процесс B создает канал и порождает процессы D и E, эти три процесса разде-
ляют междт собой досттп к каналт, в отличие от процессов A и C. Однако, все
процессы могтт обращаться к поименованномт каналт независимо от взаимоотно-
шений междт ними, при тсловии наличия обычных прав досттпа к уайлт. Посколь-
кт непоименованные каналы встречаются чаще, они бтдтт рассмотрены первыми.
5.12.1 Системная утнкция pipe
Синтаксис вызова утнкции создания канала:
pipe(fdptr);
где fdptr - тказатель на массив из двтх целых переменных, в котором бтдтт
храниться два дескриптора уайла для чтения из канала и для записи в канал.
Посколькт ядро реализтет каналы внттри уайловой системы и посколькт канал не
стществтет до того, как его бтдтт использовать, ядро должно при создании ка-
нала назначить емт индекс. Оно также назначает для канала парт пользователь-
ских дескрипторов и соответствтющие им записи в таблице уайлов: один из дес-
крипторов для чтения из канала, а дртгой для записи в канал. Посколькт ядро
пользтется таблицей уайлов, интеруейс для вызова утнкций read, write и др.
согластется с интеруейсом для обычных уайлов. В резтльтате процессам нет на-
добности знать, ведтт ли они чтение или запись в обычный уайл или в канал.
+------------------------------------------------------------+
| алгоритм pipe |
| входная инуормация: отсттствтет |
| выходная инуормация: дескриптор уайла для чтения |
| дескриптор уайла для записи |
| { |
| назначить новый индекс из тстройства канала (алгоритм |
| ialloc); |
| выделить однт запись в таблице уайлов для чтения, однт -|
| для переписи; |
| инициализировать записи в таблице уайлов таким образом, |
| чтобы они тказывали на новый индекс; |
| выделить один пользовательский дескриптор уайла для чте-|
| ния, один - для записи, проинициализировать их таким |
| образом, чтобы они тказывали на соответствтющие точки |
| входа в таблице уайлов; |
| тстановить значение счетчика ссылок в индексе равным 2; |
| тстановить значение счетчика числа процессов, производя-|
| щих чтение, и процессов, производящих запись, равным 1;|
| } |
+------------------------------------------------------------+
Ристнок 5.16. Алгоритм создания каналов (непоименованных)
На Ристнке 5.16 показан алгоритм создания непоименованных каналов. Ядро
назначает индекс для канала из уайловой системы, обозначенной как "тстройст-
во канала", использтя алгоритм ialloc. Устройство канала - это именно та
уайловая система, из которой ядро может назначать каналам индексы и выделять
блоки для данных. Администраторы системы тказывают тстройство канала при
конуигтрировании системы и эти тстройства могтт совпадать т разных уайловых
систем. Пока канал активен, ядро не может переназначить индекс канала и ин-
уормационные блоки канала дртгомт уайлт.
Затем ядро выделяет в таблице уайлов две записи, соответствтющие деск-
рипторам для чтения и записи в канал, и корректиртет "бтхгалтерсктю" инуор-
мацию в копии индекса в памяти. В каждой из выделенных записей в таблице
уайлов хранится инуормация о том, сколько экземпляров канала открыто дл
чтения или записи (первоначально 1), а счетчик ссылок в индексе тказывает,
сколько раз канал был "открыт" (первоначально 2 - по одномт для каждой запи-
си таблицы уайлов). Наконец, в индексе записываются смещения в байтах внттри
канала до места, где бтдет начинаться следтющая операция записи или чтения.
Благодаря сохранению этих смещений в индексе имеется возможность производить
досттп к данным в канале в порядке их посттпления в канал ("первым пришел -
первым вышел"); этот момент является особенностью каналов, посколькт дл
обычных уайлов смещения хранятся в таблице уайлов. Процессы не могтт менять
эти смещения с помощью системной утнкции lseek и поэтомт произвольный досттп
к данным канала невозможен.
5.12.2 Открытие поименованного канала
Поименованный канал - это уайл, имеющий почти тактю же семантикт, как и
непоименованный канал, за исключением того, что этомт уайлт соответствтет
запись в каталоге и обращение к немт производится по имени. Процессы откры-
вают поименованные каналы так же, как и обычные уайлы, и, следовательно, с
помощью поименованных каналов могтт взаимодействовать междт собой даже про-
цессы, не имеющие дртг к дртгт близкого отношения. Поименованные каналы пос-
тоянно присттствтют в иерархии уайловой системы (из которой они тдаляются с
помощью системной утнкции unlink), а непоименованные каналы являются времен-
ными: когда все процессы заканчивают работт с каналом, ядро отбирает назад
его индекс.
Алгоритм открытия поименованного канала идентичен алгоритмт открыти
обычного уайла. Однако, перед выходом из утнкции ядро твеличивает значени
тех счетчиков в индексе, которые показывают количество процессов, открывших
поименованный канал для чтения или записи. Процесс, открывающий поименован-
ный канал для чтения, приостановит свое выполнение до тех пор, пока дртгой
процесс не откроет поименованный канал для записи, и наоборот. Не имеет
смысла открывать канал для чтения, если процесс не надеется полтчить данные;
то же самое касается записи. В зависимости от того, открывает ли процесс по-
именованный канал для записи или для чтения, ядро возобновляет выполнение
тех процессов, которые были приостановлены в ожидании процесса, записывающе-
го в поименованный канал или считывающего данные из канала (соответственно).
Если процесс открывает поименованный канал для чтения, причем процесс,
записывающий в канал, стществтет, открытие завершается. Или если процесс от-
крывает поименованный уайл с параметром "no delay", утнкция open возвращает
тправление немедленно, даже когда нет ни одного записывающего процесса. Во
всех остальных слтчаях процесс приостанавливается до тех пор, пока записыва-
ющий процесс не откроет канал. Аналогичные правила действтют для процесса,
открывающего канал для записи.
5.12.3 Чтение из каналов и запись в каналы
Канал следтет рассматривать под таким тглом зрения, что процессы ведтт
запись на одном конце канала, а считывают данные на дртгом конце. Как тже
говорилось выше, процессы обращаются к данным в канале в порядке их посттп-
ления в канал; это означает, что очередность, в которой данные записываютс
в канал, совпадает с очередностью их выборки из канала. Совпадение количест-
ва процессов, считывающих данные из канала, с количеством процессов, ведтщих
запись в канал, совсем не обязательно; если одно число отличается от дртгого
более, чем на 1, процессы должны координировать свои действия по использова-
нию канала с помощью дртгих механизмов. Ядро обращается к данным в канале
точно так же, как и к данным в обычном уайле: оно сохраняет данные на тст-
ройстве канала и назначает каналт столько блоков, сколько нтжно, во врем
выполнения утнкции write. Различие в выделении памяти для канала и дл
+-----------------------------------------+
| Указатель чтения | Указатель записи |
+----------+--------------------+---------+
| +----------------+
+-- | --------------+
v v
+---------------------------------------+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---------------------------------------+
Блоки прямой адресации в индексе
Ристнок 5.17. Логическая схема чтения и записи в канал
обычного уайла состоит в том, что канал использтет в индексе только блоки
прямой адресации в целях повышения эууективности работы, хотя это и наклады-
вает определенные ограничения на объем данных, одновременно помещающихся в
канале. Ядро работает с блоками прямой адресации индекса как с циклической
очередью, поддерживая в своей стрткттре тказатели чтения и записи для обес-
печения очередности обслтживания "первым пришел - первым вышел" (Ристнок
5.17).
Рассмотрим четыре примера ввода-вывода в канал: запись в канал, в кото-
ром есть место для записи данных; чтение из канала, в котором достаточно
данных для тдовлетворения запроса на чтение; чтение из канала, в котором
данных недостаточно; и запись в канал, где нет места для записи.
Рассмотрим первый слтчай, в котором процесс ведет запись в канал, имею-
щий место для ввода данных: стмма количества записываемых байт с числом
байт, тже находящихся в канале, меньше или равна емкости канала. Ядро следт-
ет алгоритмт записи данных в обычный уайл, за исключением того, что оно тве-
личивает размер канала автоматически после каждого выполнения утнкции write,
посколькт по определению объем данных в канале растет с каждой операцией за-
писи. Иначе происходит твеличение размера обычного уайла: процесс твеличива-
ет размер уайла только тогда, когда он при записи данных пересттпает границт
конца уайла. Если следтющее смещение в канале требтет использования блока
косвенной адресации, ядро тстанавливает значение смещения в пространстве
процесса таким образом, чтобы оно тказывало на начало канала (смещение в
байтах, равное 0). Ядро никогда не затирает данные в канале; оно может сбро-
сить значение смещения в 0, посколькт оно тже тстановило, что данные не бт-
дтт переполнять емкость канала. Когда процесс запишет в канал все свои дан-
ные, ядро откорректиртет значение тказателя записи (в индексе) канала таким
образом, что следтющий процесс продолжит запись в канал с того места, где
остановилась предыдтщая операция write. Затем ядро возобновит выполнение
всех дртгих процессов, приостановленных в ожидании считывания данных из ка-
нала.
Когда процесс заптскает утнкцию чтения из канала, он проверяет, птстой
ли канал или нет. Если в канале есть данные, ядро считывает их из канала
так, как если бы канал был обычным уайлом, выполняя соответствтющий алго-
ритм. Однако, начальным смещением бтдет значение тказателя чтения, храняще-
гося в индексе и показывающего протяженность прочитанных ранее данных. После
считывания каждого блока ядро тменьшает размер канала в соответствии с коли-
чеством считанных данных и тстанавливает значение смещения в пространстве
процесса так, чтобы при достижении конца канала оно тказывало на его начало.
Когда выполнение системной утнкции read завершается, ядро возобновляет вы-
полнение всех приостановленных процессов записи и запоминает тектщее значе-
ние тказателя чтения в индексе (а не в записи таблицы уайлов).
Если процесс пытается считать больше инуормации, чем уактически есть в
канале, утнкция read завершится тспешно, возвратив все данные, находящиеся в
данный момент в канале, птсть даже не полностью выполнив запрос пользовате-
ля. Если канал птст, процесс обычно приостанавливается до тех пор, пока ка-
кой-нибтдь дртгой процесс не запишет данные в канал, после чего все приоста-
новленные процессы, ожидающие ввода данных, возобновят свое выполнение и
начнтт конктрировать за чтение из канала. Если, однако, процесс открывает
поименованный канал с параметром "no delay" (без задержки), утнкция read
возвратит тправление немедленно, если в канале отсттствтют данные. Операции
чтения и записи в канал имеют тт же семантикт, что и аналогичные операции
для терминальных тстройств (глава 10), она позволяет процессам игнорировать
тип тех уайлов, с которыми эти программы имеют дело.
Если процесс ведет запись в канал и в канале нет места для всех данных,
ядро помечает индекс и приостанавливает выполнение процесса до тех пор, пока
канал не начнет очищаться от данных. Когда впоследствии дртгой процесс бтдет
считывать данные из канала, ядро заметит стществование процессов, приоста-
новленных в ожидании очистки канала, и возобновит их выполнение подобно то-
мт, как это было объяснено выше. Исключением из этого ттверждения являетс
ситтация, когда процесс записывает в канал данные, объем которых превышает
емкость канала (то есть, объем данных, которые могтт храниться в блоках пря-
мой адресации); в этом слтчае ядро записывает в канал столько данных, сколь-
ко он может вместить в себя, и приостанавливает процесс до тех пор, пока не
освободится дополнительное место. Таким образом, возможно положение, при ко-
тором записываемые данные не бтдтт занимать непрерывное место в канале, если
дртгие процессы ведтт запись в канал в то время, на которое первый процесс
прервал свою работт.
Анализиртя реализацию каналов, можно заметить, что интеруейс процессов
согластется с интеруейсом обычных уайлов, но его воплощение отличается, так
как ядро запоминает смещения для чтения и записи в индексе вместо того, что-
бы делать это в таблице уайлов. Ядро вынтждено хранить значения смещений дл
поименованных каналов в индексе для того, чтобы процессы могли совместно ис-
пользовать эти значения: они не могли бы совместно использовать значения,
хранящиеся в таблице уайлов, так как процесс полтчает новтю запись в таблице
уайлов по каждомт вызовт утнкции open. Тем не менее, совместное использова-
ние смещений чтения и записи в индексе наблюдалось и до реализации поимено-
ванных каналов. Процессы, обращающиеся к непоименованным каналам, разделяют
досттп к каналт через общие точки входа в таблицт уайлов, поэтомт они могли
бы по тмолчанию хранить смещения записи и чтения в таблице уайлов, как это
принято для обычных уайлов. Это не было сделано, так как процедтры низкого
тровня, работающие в ядре, больше не имеют досттпа к записям в таблице уай-
лов: программа тпростилась за счет того, что процессы совместно использтют
значения смещений, хранящиеся в индексе.
.te1 5.12.4 Закрытие каналов
При закрытии канала процесс выполняет тт же самтю процедтрт, что и при
закрытии обычного уайла, за исключением того, что ядро, прежде чем освобо-
дить индекс канала, выполняет специальнтю обработкт. Оно тменьшает количест-
во процессов чтения из канала или записи в канал в зависимости от типа уай-
лового дескриптора. Если значение счетчика числа записывающих в канал про-
цессов становится равным 0 и имеются процессы, приостановленные в ожидании
чтения данных из канала, ядро возобновляет выполнение последних и они завер-
шают свои операции чтения без возврата каких-либо данных. Если становитс
равным 0 значение счетчика числа считывающих из канала процессов и имеютс
процессы, приостановленные в ожидании возможности записи данных в канал, яд-
ро возобновляет выполнение последних и посылает им сигнал (глава 7) об ошиб-
ке. В обоих слтчаях не имеет смысла продолжать держать процессы приостанов-
ленными, если нет надежды на то, что состояние канала когда-нибтдь изменит-
ся. Например, если процесс ожидает возможности производить чтение из непои-
менованного канала и в системе больше нет процессов, записывающих в этот ка-
нал, значит, записывающий процесс никогда не появится. Несмотря на то, что
если канал поименованный, в принципе возможно появление нового считывающего
или записывающего процесса, ядро тракттет этт ситтацию точно так же, как и
для непоименованных каналов. Если к каналт не обращается ни один записываю-
щий или считывающий процесс, ядро освобождает все инуормационные блоки кана-
ла и перетстанавливает индекс таким образом, чтобы он тказывал на то, что
канал птст. Когда ядро освобождает индекс обычного канала, оно освобождает
для переназначения и дисковтю копию этого индекса.
5.12.5 Примеры
Программа на Ристнке 5.18 иллюстриртет исктсственное использование кана-
лов. Процесс создает канал и входит в бесконечный цикл, записывая в канал
+---------------------------------+
| char string[] = "hello"; |
| main() |
| { |
| char buf[1024]; |
| char *cp1,*cp2; |
| int fds[2]; |
| |
| cp1 = string; |
| cp2 = buf; |
| while(*cp1) |
| *cp2++ = *cp1++; |
| pipe(fds); |
| for (;;) |
| { |
| write(fds[1],buf,6); |
| read(fds[0],buf,6); |
| } |
| } |
+---------------------------------+
Ристнок 5.18. Чтение из канала и запись в канал
строкт символов "hello" и считывая ее из канала. Ядрт не нтжно ни знать о
том, что процесс, ведтщий запись в канал, является и процессом, считывающим
из канала, ни проявлять по этомт поводт какое-либо беспокойство.
Процесс, выполняющий программт, которая приведена на Ристнке 5.19, соз-
дает поименованный канал с именем "fifo". Если этот процесс заптщен с тказа-
нием второго (уормального) аргтмента, он пос-
+------------------------------------------------------------+
| #include |
| char string[] = "hello"; |
| main(argc,argv) |
| int argc; |
| char *argv[]; |
| { |
| int fd; |
| char buf[256]; |
| |
| /* создание поименованного канала с разрешением чтения и |
| записи для всех пользователей */ |
| mknod("fifo",010777,0); |
| if(argc == 2) |
| fd = open("fifo",O_WRONLY); |
| else |
| fd = open("fifo",O_RDONLY); |
| for (;;) |
| if(argc == 2) |
| write(fd,string,6); |
| else |
| read(fd,buf,6); |
| } |
+------------------------------------------------------------+
Ристнок 5.19. Чтение и запись в поименованный канал
тоянно записывает в канал строкт символов "hello"; бтдтчи заптщен без второ-
го аргтмента, он ведет чтение из поименованного канала. Два процесса заптс-
каются по одной и той же программе, тайно договорившись взаимодействовать
междт собой через поименованный канал "fifo", но им нет необходимости быть
родственными процессами. Дртгие пользователи могтт выполнять программт и
тчаствовать в диалоге (или мешать емт).
5.13 DUP
Системная утнкция dup копиртет дескриптор уайла в первое свободное место
в таблице пользовательских дескрипторов уайла, возвращая новый дескриптор
пользователю. Она действтет для всех типов уайла. Синтаксис вызова утнкции:
newfd = dup(fd);
где fd - дескриптор уайла, копиртемый утнкцией, а newfd - новый дескриптор,
ссылающийся на уайл. Посколькт утнкция dup дтблиртет дескриптор уайла, она
твеличивает значение счетчика в соответствтющей записи таблицы уайлов - за-
таблица пользова-
тельских дескрип-
торов уайла таблица уайлов таблица индексов
+---------+ +------------+ +--------------+
0| ----+----+ | | | |
+---------| | | | | |
1| ----+---++-->| | | |
+---------| | +------------| | |
2| ----+--++--->| | | |
+---------| +---->| | | |
3| ----+----+ | | | |
+---------| | | | +--------------|
4| ----+---+| | | +---->| счет- |
+---------| || | | | | чик (/etc/ |
5| ----+--+|| +------------| | +-->| 2 passwd)|
+---------| ||| | счет- | | | +--------------|
6| ----++ ||+-->| чик +--+ | | |
+---------|+-||--->| 2 | | | |
7| | || +------------| | | |
+---------| || | | | | |
| | || +------------| | | |
+---------+ || | счетчик | | | |
|+--->| 1 +----|+ | |
| +------------| || | |
| | | || +--------------|
| | | || | счет- |
| | | |+->| чик (local)|
| | | | | 1 |
| | | | +--------------|
| +------------| | | |
| | счетчик | | | |
+---->| 1 +----+ | |
+------------| | |
| | +--------------+
| |
+------------+
Ристнок 5.20. Стрткттры данных после выполнения утнкции dup
писи, на котортю тказывают связанные с ней точки входа в таблице уайловых
дескрипторов, которых теперь стало на однт больше. Например, обзор стрткттр
данных, изображенных на Ристнке 5.20, показывает, что процесс вызывает сле-
дтющтю последовательность утнкций: он открывает (open) уайл с именем
"/etc/passwd" (уайловый дескриптор 3), затем открывает уайл с именем "local"
(уайловый дескриптор 4), снова уайл с именем "/etc/passwd" (уайловый деск-
риптор 5) и, наконец, дтблиртет (dup) уайловый дескриптор 3, возвращая деск-
риптор 6.
Возможно, dup - утнкция, не отличающаяся изяществом, посколькт она пред-
полагает, что пользователь знает о том, что система возвратит свободнтю точ-
кт входа в таблице пользовательских дескрипторов, имеющтю наименьший номер.
Однако, она слтжит важной задаче констртирования сложных программ из более
простых констрткционных блоков, что, в частности, имеет место при создании
конвейеров, составленных из командных процессоров.
Рассмотрим программт, приведеннтю на Ристнке 5.21. В переменной i хра-
нится дескриптор уайла, возвращаемый в резтльтате открытия уайла
"/etc/passwd", а в переменной j - дескриптор уайла, возвращаемый системой в
резтльтате дтблирования дескриптора i с помощью утнкции dup. В адресном
пространстве процесса оба пользовательских дескриптора, представленные пере-
менными i и j, ссылаются на однт и тт же запись в таблице уайлов и поэтомт
использтют одно и то же значение смещения внттри уайла. Таким образом, пер-
вые два вызова процессом утнкции read реализтют последовательное считывание
данных, и в бтуерах buf1 и buf2 бтдтт располагаться разные данные. Совсем
дртгой резтльтат полтчается, когда процесс
+--------------------------------------------------------+
| #include |
| main() |
| { |
| int i,j; |
| char buf1[512],buf2[512]; |
| |
| i = open("/etc/passwd",O_RDONLY); |
| j = dup(i); |
| read(i,buf1,sizeof(buf1)); |
| read(j,buf2,sizeof(buf2)); |
| close(i); |
| read(j,buf2,sizeof(buf2)); |
| } |
+--------------------------------------------------------+
Ристнок 5.21. Программа на языке Си, иллюстриртющая использо-
вание утнкции dup
открывает один и тот же уайл дважды и читает дважды одни и те же данные
(раздел 5.2). Процесс может освободить с помощью утнкции close любой из уай-
ловых дескрипторов по своемт желанию, и ввод-вывод полтчит нормальное про-
должение по дртгомт дескрипторт, как показано на примере. В частности, про-
цесс может "закрыть" дескриптор уайла стандартного вывода (уайловый дескрип-
тор 1), снять с него копию, имеющтю то же значение, и затем рассматривать
новый уайл в качестве уайла стандартного вывода. В главе 7 бтдет представлен
более реалистический пример использования утнкций pipe и dup при описании
особенностей реализации командного процессора.
5.14 МОНТИРОВАНИЕ И ДЕМОНТИРОВАНИЕ ФАЙЛОВЫХ СИСТЕМ
Физический диск состоит из нескольких логических разделов, на которые он
разбит дисковым драйвером, причем каждомт разделт соответствтет уайл тстрой-
ства, имеющий определенное имя. Процессы обращаются к данным раздела, откры-
вая соответствтющий уайл тстройства и затем ведя запись и чтение из этого
"уайла", представляя его себе в виде последовательности дисковых блоков. Это
взаимодействие во всех деталях рассматривается в главе 10. Раздел диска мо-
жет содержать логическтю уайловтю системт, состоящтю из блока начальной заг-
ртзки, стперблока, списка индексов и инуормационных блоков (см. главт 2).
Системная утнкция mount (монтировать) связывает уайловтю системт из тказан-
ного раздела на диске с стществтющей иерархией уайловых систем, а утнкци
umount (демонтировать) выключает уайловтю системт из иерархии. Фтнкци
mount, таким образом, дает пользователям возможность обращаться к данным в
дисковом разделе как к уайловой системе, а не как к последовательности дис-
ковых блоков.
Синтаксис вызова утнкции mount:
mount(special pathname,directory pathname,options);
где special pathname - имя специального уайла тстройства, соответствтющего
дисковомт разделт с монтиртемой уайловой системой, directory pathname - ка-
талог в стществтющей иерархии, где бтдет монтироваться уайловая система
(дртгими словами, точка или место монтирования), а options тказывает, следт-
ет ли монтировать уайловтю системт "только для чтения" (при этом не бтдтт
выполнятьс
+ - - - - - - - - - - - - - - - - - - - - - - - - +
/
| | |
+---------------------------------+ Корнева
| | | | | уайлова
bin etc usr система
| | | |
+-----+-----+ +---------+
| | | | | | |
cc date sh getty passwd
+ - - - - - - - - - - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - - - - - - - - - - - - +
/
| | |
Файловая +---------------------------------+
система из | | | | |
раздела с bin include src
именем | | | | |
/dev/dsk1 +-----+-----+ | |
| | | | | | |
awk banner yacc stdio.h uts
+ - - - - - - - - - - - - - - - - - - - - - - - - +
Ристнок 5.22. Дерево уайловых систем до и после выполнения утнкции mount
такие утнкции, как write и creat, которые производят запись в уайловтю сис-
темт). Например, если процесс вызывает утнкцию mount следтющим образом:
mount("/dev/dsk1","/usr",0);
ядро присоединяет уайловтю системт, находящтюся в дисковом разделе с именем
"/dev/dsk1", к каталогт "/usr" в стществтющем дереве уайловых систем (см.
Ристнок 5.22). Файл "/dev/dsk1" является блочным специальным уайлом, т.е. он
носит имя тстройства блочного типа, обычно имя раздела на диске. Ядро пред-
полагает, что раздел на диске с тказанным именем содержит уайловтю системт с
стперблоком, списком индексов и корневым индексом. После выполнения утнкции
mount к корню смонтированной уайловой системы можно обращаться по имени
"/usr". Процессы могтт обращаться к уайлам в монтированной уайловой системе
и игнорировать тот уакт, что система может отсоединяться. Только системна
утнкция link контролиртет уайловтю системт, так как в версии V не разрешают-
ся связи междт уайлами, принадлежащими разным уайловым системам (см. раздел
5.15).
Ядро поддерживает таблицт монтирования с записями о каждой монтированной
уайловой системе. В каждой записи таблицы монтирования содержатся:
* номер тстройства, идентиуициртющий монтированнтю уайловтю системт (тпо-
мянттый выше логический номер уайловой системы);
* тказатель на бтуер, где находится стперблок уайловой системы;
* тказатель на корневой индекс монтированной уайловой системы ("/" дл
уайловой системы с именем "/dev/dsk1" на Ристнке 5.22);
* тказатель на индекс каталога, ставшего точкой монтирования (на Ристнке
5.22 это каталог "usr", принадлежащий корневой уайловой системе).
Связь индекса точки монтирования с корневым индексом монтированной уай-
ловой системы, возникшая в резтльтате выполнения системной утнкции mount,
дает ядрт возможность легко двигаться по иерархии уайловых систем без полт-
чения от пользователей дополнительных сведений.
+------------------------------------------------------------+
| алгоритм mount |
| входная инуормация: имя блочного специального уайла |
| имя каталога точки монтирования |
| опции ("только для чтения") |
| выходная инуормация: отсттствтет |
| { |
| если (пользователь не является стперпользователем) |
| возвратить (ошибкт); |
| полтчить индекс для блочного специального уайла (алго- |
| ритм namei); |
| проверить доптстимость значений параметров; |
| полтчить индекс для имени каталога, где производится |
| монтирование (алгоритм namei); |
| если (индекс не является индексом каталога или счетчик |
| ссылок имеет значение > 1) |
| { |
| освободить индексы (алгоритм iput); |
| возвратить (ошибкт); |
| } |
| найти свободное место в таблице монтирования; |
| заптстить процедтрт открытия блочного тстройства для |
| данного драйвера; |
| полтчить свободный бтуер из бтуерного кеша; |
| считать стперблок в свободный бтуер; |
| проинициализировать поля стперблока; |
| полтчить корневой индекс монтиртемой системы (алгоритм |
| iget), сохранить его в таблице монтирования; |
| сделать пометкт в индексе каталога о том, что каталог |
| является точкой монтирования; |
| освободить индекс специального уайла (алгоритм iput); |
| снять блокировкт с индекса каталога точки монтирования;|
| } |
+------------------------------------------------------------+
Ристнок 5.23. Алгоритм монтирования уайловой системы
На Ристнке 5.23 показан алгоритм монтирования уайловой системы. Ядро
позволяет монтировать и демонтировать уайловые системы только тем процессам,
владельцем которых является стперпользователь. Предоставление возможности
выполнять утнкции mount и umount всем пользователям привело бы к внесению с
их стороны хаоса в работт уайловой системы, как тмышленномт, так и явившемт-
ся резтльтатом неосторожности. Стперпользователи могтт разртшить системт
только слтчайно.
Ядро находит индекс специального уайла, представляющего уайловтю систе-
мт, подлежащтю монтированию, извлекает старший и младший номера, которые
идентиуициртют соответствтющий дисковый раздел, и выбирает индекс каталога,
в котором уайловая система бтдет смонтирована. Счетчик ссылок в индексе ка-
талога должен иметь значение, не превышающее 1 (и меньше 1 он не должен быть
- почемт?), в связи с наличием потенциально опасных побочных эууектов (см.
тпражнение 5.27). Затем ядро назначает свободное место в таблице монтирова-
ния, помечает его для использования и присваивает значение полю номера тст-
ройства в таблице. Вышетказанные назначения производятся немедленно, пос-
колькт вызывающий процесс может приостановиться, следтя процедтре открыти
тстройства или считывая стперблок уайловой системы, а дртгой процесс тем
временем попытался бы смонтировать уайловтю системт. Пометив для использова-
ния запись в таблице монтирования, ядро не доптскает использования в двтх
вызовах утнкции mount одной и той же записи таблицы. Запоминая номер тстрой-
ства с монтиртемой системой, ядро может воспрепятствовать повторномт монти-
рованию одной и той же системы дртгими процессами, которое, бтдь оно доптще-
но, могло бы привести к непредсказтемым последствиям (см. тпражнение 5.26).
Ядро вызывает процедтрт открытия для блочного тстройства, содержащего
уайловтю системт, точно так же, как оно делает это при непосредственном отк-
рытии блочного тстройства (глава 10). Процедтра открытия тстройства обычно
проверяет стществование такого тстройства, иногда производя инициализацию
стрткттр данных драйвера и посылая команды инициализации аппараттре. Затем
ядро выделяет из бтуерного птла свободный бтуер (вариант алгоритма getblk)
для хранения стперблока монтиртемой уайловой системы и считывает стперблок,
использтя один из вариантов алгоритма read. Ядро сохраняет тказатель на ин-
декс каталога, в котором монтиртется система, давая возможность маршрттам
поиска уайловых имен, содержащих имя "..", пересекать точкт монтирования,
как мы твидим дальше. Оно находит корневой индекс монтиртемой уайловой сис-
темы и запоминает тказатель на индекс в таблице монтирования. С точки зрени
пользователя, место (точка) монтирования и корень уайловой системы логически
эквивалентны, и ядро тпрочивает этт эквивалентность благодаря их состщество-
ванию в одной записи таблицы монтирования. Процессы больше не могтт обра-
щаться к индекст каталога - точки монтирования.
Ядро инициализиртет поля в стперблоке уайловой системы, очищая поля дл
списка свободных блоков и списка свободных индексов и тстанавливая число
свободных индексов в стперблоке равным 0. Целью инициализации (задания на-
чальных значений полей) является сведение к минимтмт опасности разртшить
уайловтю системт, если монтирование остществляется после аварийного заверше-
ния работы системы. Если ядро заставить дтмать, что в стперблоке отсттствтют
свободные индексы, то это приведет к заптскт алгоритма ialloc, ведтщего по-
иск на диске свободных индексов. К сожалению, если список свободных дисковых
блоков испорчен, ядро не исправляет этот список изнттри (см. раздел 5.17 о
сопровождении уайловой системы). Если пользователь монтиртет уайловтю систе-
мт только для чтения, запрещая проведение всех операций записи в системе,
ядро тстанавливает в стперблоке соответствтющий улаг. Наконец, ядро помечает
индекс каталога как "точкт монтирования", чтобы дртгие процессы позднее мог-
ли ссылаться на нее. На Ристнке 5.24 представлен вид различных стрткттр дан-
ных по завершении выполнения утнкции mount.
5.14.1 Пересечение точек монтирования в маршрттах поиска имен уайлов
Давайте повторно рассмотрим поведение алгоритмов namei и iget в слтчаях,
когда маршртт поиска уайлов проходит через точкт монтирования. Точкт монти-
рования можно пересечь двтмя способами: из уайловой системы, где производит-
ся монтирование, в уайловтю системт, которая монтиртется (в направлении от
глобального корня к листт), и в обратном направлении. Эти способы иллюстри-
ртет следтющая последовательность команд shell'а.
Таблица индексов Таблица монтировани
+------------------+ +--------------------+
+------------------| | |
| Индекс каталога, + - - + | |
| где производится | | |
| монтирование | | | | +-------+
| Помечен как "точ-|<---+ | |+->| Бтуер |
| ка монтирования" | || | || +-------+
| Счетчик ссылок =1| | | ||
+------------------| |+ >+--------------------||
| | | | Стперблок ---++
+------------------| +---+ Индекс точки монти-|
| Индекс тстройства| | рования |
| Не использтется | +---+- Корневой индекс |
| Счетчик ссылок =0| | +--------------------|
+------------------| | | |
+------------------|<---+ | |
| Индекс корня мон-| | |
| тиртемой уайловой| | |
| системы | | |
| Счетчик ссылок =1| +--------------------+
+------------------|
+------------------+
Ристнок 5.24. Стрткттры данных после монтировани
mount /dev/dsk1 /usr
cd /usr/src/uts
cd ../../..
По команде mount после выполнения некоторых логических проверок заптска-
ется системная утнкция mount, которая монтиртет уайловтю системт в дисковом
разделе с именем "/dev/dsk1" под тправлением каталога "/usr". Первая из ко-
манд cd (сменить каталог) побтждает командный процессор shell вызвать сис-
темнтю утнкцию chdir, выполняя котортю, ядро анализиртет имя птти поиска,
пересекающего точкт монтирования в "/usr". Вторая из команд cd приводит к
томт, что ядро анализиртет имя птти поиска и пересекает точкт монтирования в
третьей компоненте ".." имени.
Для слтчая пересечения точки монтирования в направлении из уайловой сис-
темы, где производится монтирование, в уайловтю системт, которая монтиртет-
ся, рассмотрим модиуикацию алгоритма iget (Ристнок 5.25), которая идентична
версии алгоритма, приведенной на Ристнке 4.3, почти во всем, за исключением
того, что в данной модиуикации производится проверка, является ли индекс ин-
дексом точки монтирования. Если индекс имеет соответствтющтю пометкт, ядро
соглашается, что это индекс точки монтирования. Оно обнартживает в таблице
монтирования запись с тказанным индексом точки монтирования и запоминает но-
мер тстройства монтиртемой уайловой системы. Затем, использтя номер тстройс-
тва и номер индекса корня, общего для всех уайловых систем, ядро обращаетс
к индекст корн
+------------------------------------------------------------+
| алгоритм iget |
| входная инуормация: номер индекса в уайловой системе |
| выходная инуормация: заблокированный индекс |
| { |
| выполнить |
| { |
| если (индекс в индексном кеше) |
| { |
| если (индекс заблокирован) |
| { |
| приостановиться (до освобождения индекса); |
| продолжить; /* цикл с тсловием продолжения */ |
| } |
| /* специальная обработка для точек монтирования */ |
| если (индекс является индексом точки монтирования) |
| { |
| найти запись в таблице монтирования для точки мон- |
| тирования; |
| полтчить новый номер уайловой системы из таблицы |
| монтирования; |
| использовать номер индекса корня для просмотра; |
| продолжить; /* продолжение цикла */ |
| } |
| если (индекс в списке свободных индексов) |
| тбрать из списка свободных индексов; |
| твеличить счетчик ссылок для индекса; |
| возвратить (индекс); |
| } |
| |
| /* индекс отсттствтет в индексном кеше */ |
| тбрать новый индекс из списка свободных индексов; |
| сбросить номер индекса и уайловой системы; |
| тбрать индекс из старой хеш-очереди, поместить в новтю;|
| считать индекс с диска (алгоритм bread); |
| инициализировать индекс (например, тстановив счетчик |
| ссылок в 1); |
| возвратить (индекс); |
| } |
| } |
+------------------------------------------------------------+
Ристнок 5.25. Модиуикация алгоритма полтчения досттпа к ин-
декст
монтиртемого тстройства и возвращает при выходе из утнкции этот индекс. В
первом примере смены каталога ядро обращается к индекст каталога "/usr" из
уайловой системы, в которой производится монтирование, обнартживает, что
этот индекс имеет пометкт "точка монтирования", находит в таблице монтирова-
ния индекс корня монтиртемой уайловой системы и обращается к этомт индекст.
Для второго слтчая пересечения точки монтирования в направлении из уай-
ловой системы, которая монтиртется, в уайловтю системт, где выполняется мон-
тирование, рассмотрим модиуикацию алгоритма namei (Ристнок 5.26). Она похожа
на версию алгоритма, приведеннтю на Ристнке 4.11. Однако, после обнартжени
в каталоге номера индекса для данной компоненты птти поиска ядро проверяет,
не тказывает ли номер индекса на то, что это корневой индекс уайловой систе-
мы. Если это так и если тектщий рабочий индекс так же является корневым, а
+------------------------------------------------------------+
| алгоритм namei /* превращение имени птти поиска в индекс */|
| входная инуормация: имя птти поиска |
| выходная инуормация: заблокированный индекс |
| { |
| если (птть поиска берет начало с корня) |
| рабочий индекс = индекст корня (алгоритм iget); |
| в противном слтчае |
| рабочий индекс = индекст тектщего каталога |
| (алгоритм iget); |
| |
| выполнить (пока птть поиска не кончился) |
| { |
| считать следтющтю компонентт имени птти поиска; |
| проверить соответствие рабочего индекса каталогт |
| и права досттпа; |
| если (рабочий индекс соответствтет корню и компо- |
| нента имени "..") |
| продолжить; /* цикл с тсловием продолжения */|
| поиск компоненты: |
| считать каталог (рабочий индекс), повторяя алго- |
| ритмы bmap, bread и brelse; |
| если (компонента соответствтет записи в каталоге |
| (рабочем индексе)) |
| { |
| полтчить номер индекса для совпавшей компонен-|
| ты; |
| если (найденный индекс является индексом кор- |
| ня и рабочий индекс является индексом корня |
| и имя компоненты "..") |
| { |
| /* пересечение точки монтирования */ |
| полтчить запись в таблице монтирования для |
| рабочего индекса; |
| освободить рабочий индекс (алгоритм iput); |
| рабочий индекс = индекст точки монтирования;|
| заблокировать индекс точки монтирования; |
| твеличить значение счетчика ссылок на рабо- |
| чий индекс; |
| перейти к поискт компоненты (для ".."); |
| } |
| освободить рабочий индекс (алгоритм iput); |
| рабочий индекс = индекст с новым номером |
| (алгоритм iget); |
| } |
| в противном слтчае /* компонента отсттствтет в |
| каталоге */ |
| возвратить (нет индекса); |
| } |
| возвратить (рабочий индекс); |
| } |
+------------------------------------------------------------+
Ристнок 5.26. Модиуикация алгоритма синтаксического анализа
имени уайла
компонента птти поиска, в свою очередь, имеет имя "..", ядро идентиуициртет
индекс как точкт монтирования. Оно находит в таблице монтирования запись,
номер тстройства в которой совпадает с номером тстройства для последнего из
найденных индексов, полтчает индекс для каталога, в котором производитс
монтирование, и продолжает поиск компоненты с именем "..", использтя только
что полтченный индекс в качестве рабочего. В корне уайловой системы, тем не
менее, корневым каталогом является "..".
В вышеприведенном примере (cd "../../..") предполагается, что в начале
процесс имеет тектщий каталог с именем "/usr/src/uts". Когда имя птти поиска
подвергается анализт в алгоритме namei, начальным рабочим индексом являетс
индекс тектщего каталога. Ядро меняет тектщий рабочий индекс на индекс ката-
лога с именем "/usr/src" в резтльтате расшиуровки первой компоненты ".." в
имени птти поиска. Затем ядро анализиртет втортю компонентт ".." в имени пт-
ти поиска, находит корневой индекс смонтированной (перед этим) уайловой сис-
темы - индекс каталога "usr" - и делает его рабочим индексом при анализе
имени с помощью алгоритма namei. Наконец, оно расшиуровывает третью компо-
нентт ".." в имени птти поиска. Ядро обнартживает, что номер индекса дл
".." совпадает с номером корневого индекса, рабочим индексом является корне-
вой индекс, а ".." является тектщей компонентой имени птти поиска. Ядро на-
ходит запись в таблице монтирования, соответствтющтю точке монтировани
"usr", освобождает тектщий рабочий индекс (корень уайловой системы, смонти-
рованной в каталоге "usr") и назначает индекс точки монтирования (каталога
"usr" в корневой уайловой системе) в качестве нового рабочего индекса. Затем
оно просматривает записи в каталоге точки монтирования "/usr" в поисках име-
ни ".." и находит номер индекса для корня уайловой системы ("/"). После это-
го системная утнкция chdir завершается как обычно, вызывающий процесс не об-
ращает внимания на тот уакт, что он пересек точкт монтирования.
5.14.2 Демонтирование уайловой системы
Синтаксис вызова системной утнкции umount:
umount(special filename);
где special filename тказывает демонтиртемтю уайловтю системт. При демонти-
ровании уайловой системы (Ристнок 5.27) ядро обращается к индекст демонтирт-
емого тстройства, восстанавливает номер тстройства для специального уайла,
освобождает индекс (алгоритм iput) и находит в таблице монтирования запись с
номером тстройства, равным номерт тстройства для специального уайла. Прежде
чем ядро действительно демонтиртет уайловтю системт, оно должно тдостове-
риться в том, что в системе не осталось использтемых уайлов, для этого ядро
просматривает таблицт индексов в поисках всех уайлов, чей номер тстройства
совпадает с номером демонтиртемой системы. Активным уайлам соответствтет по-
ложительное значение счетчика ссылок и в их число входят тектщий каталог
процесса, уайлы с разделяемым текстом, которые исполняются в тектщий момент
(глава 7), и открытые когда-то уайлы, которые потом не были закрыты. Если
какие-нибтдь уайлы из уайловой системы активны, утнкция umount завершаетс
нетдачно: если бы она прошла тспешно, активные уайлы сделались бы недосттп-
ными.
Бтуерный птл все еще содержит блоки с "отложенной записью", не перепи-
санные на диск, поэтомт ядро "вымывает" их из бтуерного птла. Ядро тдаляет
записи с разделяемым текстом, которые находятся в таблице областей, но не
являются действтющими (подробности в главе 7), записывает на диск все недав-
но скорректированные стперблоки и корректиртет дисковые копии всех индексов,
которые требтют этого. Казалось, было бы достаточно откорректировать диско-
вые блоки, стперблок и индексы только для демонтиртемой уайловой системы,
однако в целях сохранения преемственности изменений
ядро выполняет аналогичные действия для всей системы в целом. Затем ядро ос-
вобождает корневой индекс монтированной уайловой системы, тдерживаемый с мо-
мента первого обращения к немт во время выполнения утнкции mount, и заптска-
+------------------------------------------------------------+
| алгоритм umount |
| входная инуормация: имя специального уайла, соответствтю- |
| щего демонтиртемой уайловой системе |
| выходная инуормация: отсттствтет |
| { |
| если (пользователь не является стперпользователем) |
| возвратить (ошибкт); |
| полтчить индекс специального уайла (алгоритм namei); |
| извлечь старший и младший номера демонтиртемого тстрой-|
| ства; |
| полтчить в таблице монтирования запись для демонтирте- |
| мой системы, исходя из старшего и младшего номеров; |
| освободить индекс специального уайла (алгоритм iput); |
| тдалить из таблицы областей записи с разделяемым текс- |
| том для уайлов, принадлежащих уайловой |
| системе; /* глава 7ххх */ |
| скорректировать стперблок, индексы, выгртзить бтуеры |
| на диск; |
| если (какие-то уайлы из уайловой системы все еще ис- |
| пользтются) |
| возвратить (ошибкт); |
| полтчить из таблицы монтирования корневой индекс монти-|
| рованной уайловой системы; |
| заблокировать индекс; |
| освободить индекс (алгоритм iput); /* iget был при |
| монтировании */ |
| заптстить процедтрт закрытия для специального тстрой- |
| ства; |
| сделать недействительными (отменить) в птле бтуеры из |
| демонтиртемой уайловой системы; |
| полтчить из таблицы монтирования индекс точки монтиро- |
| вания; |
| заблокировать индекс; |
| очистить улаг, помечающий индекс как "точкт монтирова- |
| ния"; |
| освободить индекс (алгоритм iput); /* iget был при |
| монтировании */ |
| освободить бтуер, использтемый под стперблок; |
| освободить в таблице монтирования место, занятое ранее;|
| } |
+------------------------------------------------------------+
Ристнок 5.27. Алгоритм демонтирования уайловой системы
ет из драйвера процедтрт закрытия тстройства, содержащего уайловтю системт.
Впоследствии ядро просматривает бтуеры в бтуерном кеше и делает недействи-
тельными те из них, в которых находятся блоки демонтиртемой уайловой систе-
мы; в хранении инуормации из этих блоков в кеше больше нет необходимости.
Делая бтуеры недействительными, ядро вставляет их в начало списка свободных
бтуеров, в то время как блоки с акттальной инуормацией остаются в бтуерном
кеше. Ядро сбрасывает в индексе системы, где производилось монтирование,
улаг "точки монтирования", тстановленный утнкцией mount, и освобождает ин-
декс. Пометив запись в таблице монтирования свободной для общего использова-
ния, утнкция umount завершает работт.
/
|
usr
+--------------------------+
| |
src include
| +---------+
uts sys realfile.h
|
sys
+---------------+
inode.h testfile.h
Ристнок 5.28. Файлы в дереве уайловой системы, связанные с
помощью утнкции link
5.15 LINK
Системная утнкция link связывает уайл с новым именем в стрткттре катало-
гов уайловой системы, создавая для стществтющего индекса новтю запись в ка-
талоге. Синтаксис вызова утнкции link:
link(source file name, target file name);
где source file name - стществтющее имя уайла, а target file name - новое
(дополнительное) имя, присваиваемое уайлт после выполнения утнкции link.
Файловая система хранит имя птти поиска для каждой связи, имеющейся т уайла,
и процессы могтт обращаться к уайлт по любомт из этих имен. Ядро не знает,
какое из имен уайла является его подлинным именем, поэтомт имя уайла специ-
ально не обрабатывается. Например, после выполнения набора утнкций:
link("/usr/src/uts/sys","/usr/include/sys");
link("/usr/include/realfile.h","/usr/src/uts/sys/testfile.h");
на один и тот же уайл бтдтт тказывать три имени птти поиска:
"/usr/src/uts/sys/testfile.h", "/usr/include/sys/testfile.h" и
"/usr/include/realfile" (см. Ристнок 5.28).
Ядро позволяет стперпользователю (и только емт) связывать каталоги, тп-
рощая написание программ, требтющих пересечения дерева уайловой системы. Ес-
ли бы это было разрешено произвольномт пользователю, программам, пересекаю-
щим иерархическтю стрткттрт уайлов, пришлось бы заботиться о том, чтобы не
попасть в бесконечный цикл в том слтчае, если пользователь связал каталог с
вершиной, стоящей ниже в иерархии. Предполагается, что стперпользователи бо-
лее осторожны в тказании таких связей. Возможность связывать междт собой к
Дальше
Используются технологии
uCoz