The design of the unix operating system by Maurice J

Вид материалаРеферат
5.10 Смена владельца и режима доступа к файлу
5.11 Stat и fstat
Подобный материал:
1   ...   10   11   12   13   14   15   16   17   ...   55
для всех порожденных им процессов) и это означает, что все пути

поиска в алгоритме namei, начинающиеся с корня ("/"), возьмут на-

чало с данного индекса и что все попытки войти в каталог ".." над

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

новый корень. Процесс передает всем вновь порождаемым процессам

этот каталог в качестве корневого подобно тому, как передает свой

текущий каталог.


5.10 СМЕНА ВЛАДЕЛЬЦА И РЕЖИМА ДОСТУПА К ФАЙЛУ


Смена владельца или режима (прав) доступа к файлу является

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

вызова соответствующих системных функций:

chown(pathname,owner,group)

chmod(pathname,mode)

Для того, чтобы поменять владельца файла, ядро преобразует

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

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

ла (процесс не может распоряжаться тем, что не принадлежит ему).

Затем ядро назначает файлу нового владельца и нового группового

пользователя, сбрасывает флаги прежних установок (см. раздел 7.5)

и освобождает индекс по алгоритму iput. После этого прежний вла-

делец теряет право "собственности" на файл. Для того, чтобы поме-

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

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

жим доступа.


5.11 STAT И FSTAT


Системные функции stat и fstat позволяют процессам запраши-

вать информацию о статусе файла: типе файла, владельце файла,

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

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


stat(pathname,statbuffer);

fstat(fd,statbuffer);

где pathname - имя файла, fd - дескриптор файла, возвращаемый

функцией open, statbuffer - адрес структуры данных пользователь-

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

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

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

Рисунке 5.33 иллюстрирует использование функций stat и fstat.


Вызывает канал Не могут совместно использовать

канал


Процесс 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;│

│ } │

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


Рисунок 5.16. Алгоритм создания каналов (непоименованных)


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

налов. Ядро назначает индекс для канала из файловой системы,

обозначенной как "устройство канала", используя алгоритм ialloc.

Устройство канала - это именно та файловая система, из которой

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

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

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

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

декс канала и информационные блоки канала другому файлу.

Затем ядро выделяет в таблице файлов две записи, соответству-

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

"бухгалтерскую" информацию в копии индекса в памяти. В каждой из

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

сколько экземпляров канала открыто для чтения или записи (перво-

начально 1), а счетчик ссылок в индексе указывает, сколько раз

канал был "открыт" (первоначально 2 - по одному для каждой записи

таблицы файлов). Наконец, в индексе записываются смещения в бай-

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

ция записи или чтения. Благодаря сохранению этих смещений в ин-

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

порядке их поступления в канал ("первым пришел - первым вышел");

этот момент является особенностью каналов, поскольку для обычных

файлов смещения хранятся в таблице файлов. Процессы не могут ме-

нять эти смещения с помощью системной функции lseek и поэтому

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


5.12.2 Открытие поименованного канала


Поименованный канал - это файл, имеющий почти такую же семан-

тику, как и непоименованный канал, за исключением того, что этому

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

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

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

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

имеющие друг к другу близкого отношения. Поименованные каналы

постоянно присутствуют в иерархии файловой системы (из которой

они удаляются с помощью системной функции unlink), а непоимено-

ванные каналы являются временными: когда все процессы заканчивают

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

Алгоритм открытия поименованного канала идентичен алгоритму

открытия обычного файла. Однако, перед выходом из функции ядро

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

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

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

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

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

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

чить данные; то же самое касается записи. В зависимости от того,

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

ния, ядро возобновляет выполнение тех процессов, которые были

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

канал или считывающего данные из канала (соответственно).

Если процесс открывает поименованный канал для чтения, причем

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

Или если процесс открывает поименованный файл с параметром "no

delay", функция open возвращает управление немедленно, даже когда

нет ни одного записывающего процесса. Во всех остальных случаях

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

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

крывающего канал для записи.


5.12.3 Чтение из каналов и запись в каналы


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

цессы ведут запись на одном конце канала, а считывают данные на

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

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

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

очередностью их выборки из канала. Совпадение количества процес-

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

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

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

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

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

ным в обычном файле: оно сохраняет данные на устройстве канала и

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

ния функции write. Различие в выделении памяти для канала и для


---------------------T--------------------┐

│ Указатель чтения │ Указатель записи │

L----------+---------+----------+----------

│ │

│ ------------------

L-------------------┐

│ │

v v

----T---T---T---T---T---T---T---T---T---┐

│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │

L---+---+---+---+---+---+---+---+---+----

Блоки прямой адресации в индексе


Рисунок 5.17. Логическая схема чтения и записи в канал


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

только блоки прямой адресации в целях повышения эффективности ра-

боты, хотя это и накладывает определенные ограничения на объем

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

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

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

очередности обслуживания "первым пришел - первым вышел" (Рисунок

5.17).

Рассмотрим четыре примера ввода-вывода в канал: запись в ка-

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

котором достаточно данных для удовлетворения запроса на чтение;

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

нал, где нет места для записи.

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

канал, имеющий место для ввода данных: сумма количества записыва-

емых байт с числом байт, уже находящихся в канале, меньше или

равна емкости канала. Ядро следует алгоритму записи данных в

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

нала автоматически после каждого выполнения функции write, пос-

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

рацией записи. Иначе происходит увеличение размера обычного

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

при записи данных переступает границу конца файла. Если следующее

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

ции, ядро устанавливает значение смещения в пространстве процесса

таким образом, чтобы оно указывало на начало канала (смещение в

байтах, равное 0). Ядро никогда не затирает данные в канале; оно

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

ло, что данные не будут переполнять емкость канала. Когда процесс

запишет в канал все свои данные, ядро откорректирует значение

указателя записи (в индексе) канала таким образом, что следующий

процесс продолжит запись в канал с того места, где остановилась

предыдущая операция write. Затем ядро возобновит выполнение всех

других процессов, приостановленных в ожидании считывания данных

из канала.

Когда процесс запускает функцию чтения из канала, он проверя-

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

тывает их из канала так, как если бы канал был обычным файлом,

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

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

вающего протяженность прочитанных ранее данных. После считывания

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

чеством считанных данных и устанавливает значение смещения в

пространстве процесса так, чтобы при достижении конца канала оно

указывало на его начало. Когда выполнение системной функции read

завершается, ядро возобновляет выполнение всех приостановленных

процессов записи и запоминает текущее значение указателя чтения в

индексе (а не в записи таблицы файлов).

Если процесс пытается считать больше информации, чем факти-

чески есть в канале, функция read завершится успешно, возвратив

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

полностью выполнив запрос пользователя. Если канал пуст, процесс

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

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

ные процессы, ожидающие ввода данных, возобновят свое выполнение

и начнут конкурировать за чтение из канала. Если, однако, процесс

открывает поименованный канал с параметром "no delay" (без за-

держки), функция read возвратит управление немедленно, если в ка-

нале отсутствуют данные. Операции чтения и записи в канал имеют

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

ройств (глава 10), она позволяет процессам игнорировать тип тех

файлов, с которыми эти программы имеют дело.

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

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

процесса до тех пор, пока канал не начнет очищаться от данных.

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

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

дании очистки канала, и возобновит их выполнение подобно тому,

как это было объяснено выше. Исключением из этого утверждения яв-

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

которых превышает емкость канала (то есть, объем данных, которые

могут храниться в блоках прямой адресации); в этом случае ядро

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

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

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

тором записываемые данные не будут занимать непрерывное место в

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

которое первый процесс прервал свою работу.

Анализируя реализацию каналов, можно заметить, что интерфейс

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

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

записи в индексе вместо того, чтобы делать это в таблице файлов.

Ядро вынуждено хранить значения смещений для поименованных кана-

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

вать эти значения: они не могли бы совместно использовать значе-

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

запись в таблице файлов по каждому вызову функции open. Тем не

менее, совместное использование смещений чтения и записи в индек-

се наблюдалось и до реализации поименованных каналов. Процессы,

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

через общие точки входа в таблицу файлов, поэтому они могли бы по

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

это принято для обычных файлов. Это не было сделано, так как про-

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

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

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

индексе.


5.12.4 Закрытие каналов


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

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

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

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

записи в канал в зависимости от типа файлового дескриптора. Если

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

равным 0 и имеются процессы, приостановленные в ожидании чтения

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

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

Если становится равным 0 значение счетчика числа считывающих из

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

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

последних и посылает им сигнал (глава 7) об ошибке. В обоих слу-

чаях не имеет смысла продолжать держать процессы приостановленны-

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

менится. Например, если процесс ожидает возможности производить

чтение из непоименованного канала и в системе больше нет процес-

сов, записывающих в этот канал, значит, записывающий процесс ни-

когда не появится. Несмотря на то, что если канал поименованный,

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

щего процесса, ядро трактует эту ситуацию точно так же, как и для

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

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

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

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

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

ковую копию этого индекса.


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

│ char string[] = "hello"; │

│ main() │

│ { │

│ char buf[1024]; │

│ char *cp1,*cp2; │

│ int fds[2]; │

│ │

│ cp1 = string; │