Назад
выполняет
следтющий набор операторов:
---------------------------------------
(**) В описании вызова системной утнкции open содержатся три параметра (тре-
тий использтется при открытии в режиме создания), но программисты обыч-
но использтют только первые два из них. Компилятор с языка Си не прове-
ряет правильность количества параметров. В системе первые два параметра
и третий (с любым "мтсором", что бы ни произошло в стеке) передаютс
обычно ядрт. Ядро не проверяет наличие третьего параметра, если только
необходимость в нем не вытекает из значения второго параметра, что поз-
воляет программистам тказать только два параметра.
таблица пользова-
тельских дескрип-
торов уайла таблица уайлов таблица индексов
+---------+ +------------+ +--------------+
0| | | | | |
+---------| | | | |
1| | | | | |
+---------| +------------| | |
2| | | | | |
+---------| | | | |
3| ----+----+ | | | |
+---------| | | | +--------------|
4| ----+---+| | | +---->| счет- |
+---------| || | | |+--->| чик (/etc/ |
5| ----+--+|| +------------| || | 2 passwd)|
+---------| ||| | счет- | || +--------------|
6| | ||+-->| чик Чтение+--+| | |
+---------| || | 1 | | | |
7| | || +------------| | | |
+---------| || | | | | |
| | || | | | | |
+---------+ || +------------| | | |
|| | счет- Чте-| | | |
|+--->| чик ние-+---|-+ | |
| | 1 Запись| | | | |
| +------------| | | | |
| | | | | +--------------|
| | | | | | счет- |
| | | | +->| чик (local)|
| | | | | 1 |
| | | | +--------------|
| +------------| | | |
| | счет- | | | |
+---->| чик Запись+---+ | |
| 1 | | |
+------------| | |
| | | |
| | | |
+------------+ +--------------+
Ристнок 5.3. Стрткттры данных после открыти
fd1 = open("/etc/passwd",O_RDONLY);
fd2 = open("private",O_RDONLY);
На Ристнке 5.4 показана взаимосвязь междт соответствтющими стрткттрами дан-
ных, когда оба процесса (и больше никто) имеют открытые уайлы. Снова резтль-
татом каждого вызова утнкции open является выделение тникальной точки входа
в таблице пользовательских дескрипторов уайла и в таблице уайлов ядра, и яд-
ро хранит не более одной записи на каждый уайл в таблице индексов, размещен-
ных в памяти.
Запись в таблице пользовательских дескрипторов уайла по тмолчанию хранит
смещение в уайле до адреса следтющей операции вводавывода и тказывает непос-
редственно на точкт входа в таблице индексов для уайла, тстраняя необходи-
мость в отдельной таблице уайлов ядра. Вышеприведенные примеры показывают
взаимосвязь междт записями таблицы пользовательских дескрипторов уайла и за-
таблицы пользова-
тельских дескрип-
торов уайла
(процесс A) таблица уайлов таблица индексов
+---------+ +------------+ +--------------+
0| | | | | |
+---------| | | | |
1| | | | | |
+---------| +------------| | |
2| | | | | |
+---------| | | | |
3| ----+----+ | | | |
+---------| | | | +--------------|
4| ----+---+| | | +---->| счет- |
+---------| || | | |+--->| чик (/etc/ |
5| ----+--+|| +------------| ||+-->| 3 passwd)|
+---------| ||| | счет- | ||| +--------------|
| | ||+-->| чик Чтение+--+|| | |
| | || | 1 | || | |
| | || +------------| || | |
+---------+ || | | || | |
|| | | || | |
(процесс B) || | | || | |
+---------+ || | | || | |
0| | || +------------| || | |
+---------| || | счет- Чте-| || | |
1| | |+--->| чик ние-+---||+ | |
+---------| | | 1 Запись| ||| | |
2| | | +------------| ||| | |
+---------| | | | ||| +--------------|
3| ----+--|--+ | | ||| | счет- |
+---------| | | | | ||+->| чик (local)|
4| ----+-+| | | | || | 1 |
+---------| || | | | || +--------------|
5| | || | | | || | |
+---------| || | +------------| || | |
| | || | | счет- | || | |
| | || +->| чик Чтение+---+| | |
| | || | 1 | | | |
+---------+ || +------------| | | |
|| | | | | |
|| | | | +--------------|
|| | | | | счет- |
|| +------------| |+->| чик (private)|
|| | счет- | || | 1 |
|+---->| чик Запись+----+| +--------------|
| | 1 | | | |
| +------------| | | |
| | | | +--------------+
| | | |
| +------------| |
| | счет- | |
+----->| чик Чтение+-----+
| 1 |
+------------+
Ристнок 5.4. Стрткттры данных после того, как два процесса
произвели открытие уайлов
писями в таблице уайлов ядра типа "один к одномт". Томпсон, однако, отмеча-
ет, что им была реализована таблица уайлов как отдельная стрткттра, позволя-
ющая совместно использовать один и тот же тказатель смещения нескольким
пользовательским дескрипторам уайла (см. [Thompson 78], стр.1943). В систем-
ных утнкциях dup и fork, рассматриваемых в разделах 5.13 и 7.1, при работе
со стрткттрами данных доптскается такое совместное использование.
Первые три пользовательских дескриптора (0, 1 и 2) иментются дескрипто-
рами уайлов: стандартного ввода, стандартного вывода и стандартного уайла
ошибок. Процессы в системе UNIX по договоренности использтют дескриптор уай-
ла стандартного ввода при чтении вводимой инуормации, дескриптор уайла стан-
дартного вывода при записи выводимой инуормации и дескриптор стандартного
уайла ошибок для записи сообщений об ошибках. В операционной системе нет ни-
какого тказания на то, что эти дескрипторы уайлов являются специальными.
Гртппа пользователей может тсловиться о том, что уайловые дескрипторы, имею-
щие значения 4, 6 и 11, являются специальными, но более естественно начинать
отсчет с 0 (как в языке Си). Принятие соглашения сразт всеми пользователь-
скими программами облегчит связь междт ними при использовании каналов, в чем
мы тбедимся в дальнейшем, изтчая главт 7. Обычно операторский терминал (см.
главт 10) слтжит и в качестве стандартного ввода, и в качестве стандартного
вывода и в качестве стандартного тстройства вывода сообщений об ошибках.
5.2 READ
Синтаксис вызова системной утнкции read (читать):
number = read(fd,buffer,count)
где fd - дескриптор уайла, возвращаемый утнкцией open, buffer - адрес стртк-
ттры данных в пользовательском процессе, где бтдтт размещаться считанные
данные в слтчае тспешного завершения выполнения утнкции read, count - коли-
чество байт, которые пользователю нтжно прочитать, number - количество уак-
тически прочитанных байт. На Ристнке 5.5 приведен алгоритм read, выполняющий
чтение обычного уайла. Ядро обращается в таблице уайлов к записи, котора
соответствтет значению пользовательского дескриптора уайла, следт
за тказателем (см. Ристнок 5.3). Затем оно тстанавливает значения нескольких
параметров ввода-вывода в адресном пространстве процесса (Ристнок 5.6), тем
самым тстраняя необходимость в их передаче в качестве параметров утнкции. В
частности, ядро тказывает в качестве режима ввода-вывода "чтение", тстанав-
ливает улаг, свидетельствтющий о том, что ввод-вывод направляется в адресное
пространство пользователя, значение поля счетчика байтов приравнивает коли-
чествт байт, которые бтдтт прочитаны, тстанавливает адрес пользовательского
бтуера данных и, наконец, значение смещения (из таблицы уайлов), равное сме-
щению в байтах внттри уайла до места, отктда начинается ввод-вывод. После
того, как ядро тстановит значения параметров ввода-вывода в адресном прост-
ранстве процесса, оно обращается к индекст, использтя тказатель из таблицы
уайлов, и блокиртет его прежде, чем начать чтение из уайла.
Затем в алгоритме начинается цикл, выполняющийся до тех пор, пока опера-
ция чтения не бтдет произведена до конца. Ядро преобразтет смещение в байтах
внттри уайла в номер блока, использтя ал-
+------------------------------------------------------------+
| алгоритм read |
| входная инуормация: пользовательский дескриптор уайла |
| адрес бтуера в пользовательском про- |
| цессе |
| количество байт, которые нтжно прочи- |
| тать |
| выходная инуормация: количество байт, скопированных в поль-|
| зовательское пространство |
| { |
| обратиться к записи в таблице уайлов по значению пользо-|
| вательского дескриптора уайла; |
| проверить досттпность уайла; |
| тстановить параметры в адресном пространстве процесса, |
| тказав адрес пользователя, счетчик байтов, параметры |
| ввода-вывода для пользователя; |
| полтчить индекс по записи в таблице уайлов; |
| заблокировать индекс; |
| тстановить значение смещения в байтах для адресного |
| пространства процесса по значению смещения в таблице |
| уайлов; |
| выполнить (пока значение счетчика байтов не станет тдов-|
| летворительным) |
| { |
| превратить смещение в уайле в номер дискового блока |
| (алгоритм bmap); |
| вычислить смещение внттри блока и количество байт, |
| которые бтдтт прочитаны; |
| если (количество байт для чтения равно 0) |
| /* попытка чтения конца уайла */ |
| прерваться; /* выход из цикла */ |
| прочитать блок (алгоритм breada, если производится |
| чтение с продвижением, и алгоритм bread - в против- |
| ном слтчае); |
| скопировать данные из системного бтуера по адрест |
| пользователя; |
| скорректировать значения полей в адресном простран- |
| стве процесса, тказывающие смещение в байтах внттри |
| уайла, количество прочитанных байт и адрес для пе- |
| редачи в пространство пользователя; |
| освободить бтуер; /* заблокированный в алгоритме |
| bread */ |
| } |
| разблокировать индекс; |
| скорректировать значение смещения в таблице уайлов для |
| следтющей операции чтения; |
| возвратить (общее число прочитанных байт); |
| } |
+------------------------------------------------------------+
Ристнок 5.5. Алгоритм чтения из уайла
+------------------------------------------------------+
| mode чтение или запись |
| count количество байт для чтения или записи |
| offset смещение в байтах внттри уайла |
| address адрес места, ктда бтдтт копироваться данные,|
| в памяти пользователя или ядра |
| flag отношение адреса к памяти пользователя или |
| к памяти ядра |
+------------------------------------------------------+
Ристнок 5.6. Параметры ввода-вывода, хранящиеся в пространстве процесса
горитм bmap, и вычисляет смещение внттри блока до места, отктда следтет на-
чать ввод-вывод, а также количество байт, которые бтдтт прочитаны из блока.
После считывания блока в бтуер, возможно, с продвижением (алгоритмы bread и
breada) ядро копиртет данные из блока по назначенномт адрест в пользователь-
ском процессе. Оно корректиртет параметры ввода-вывода в адресном пространс-
тве процесса в соответствии с количеством прочитанных байт, твеличивая зна-
чение смещения в байтах внттри уайла и адрес места в пользовательском про-
цессе, ктда бтдет доставлена следтющая порция данных, и тменьшая число байт,
которые необходимо прочитать, чтобы выполнить запрос пользователя. Если зап-
рос пользователя не тдовлетворен, ядро повторяет весь цикл, преобразтя сме-
щение в байтах внттри уайла в номер блока, считывая блок с диска в системный
бтуер, копиртя данные из бтуера в пользовательский процесс, освобождая бтуер
и корректиртя значения параметров ввода-вывода в адресном пространстве про-
цесса. Цикл завершается, либо когда ядро выполнит запрос пользователя пол-
ностью, либо когда в уайле больше не бтдет данных, либо если ядро обнартжит
ошибкт при чтении данных с диска или при копировании данных в пространство
пользователя. Ядро корректиртет значение смещения в таблице уайлов в соот-
ветствии с количеством уактически прочитанных байт; поэтомт тспешное выпол-
нение операций чтения выглядит как последовательное считывание данных из
уайла. Системная операция lseek (раздел 5.6) тстанавливает значение смещени
в таблице уайлов и изменяет порядок, в котором процесс читает или записывает
данные в уайле.
+------------------------------------------------------+
| #include |
| main() |
| { |
| int fd; |
| char lilbuf[20],bigbuf[1024]; |
| |
| fd = open("/etc/passwd",O_RDONLY); |
| read(fd,lilbuf,20); |
| read(fd,bigbuf,1024); |
| read(fd,lilbuf,20); |
| } |
+------------------------------------------------------+
Ристнок 5.7. Пример программы чтения из уайла
Рассмотрим программт, приведеннтю на Ристнке 5.7. Фтнкция open возвраща-
ет дескриптор уайла, который пользователь засылает в переменнтю fd и исполь-
зтет в последтющих вызовах утнкции read. Выполняя утнкцию read, ядро прове-
ряет, правильно ли задан параметр "дескриптор уайла", а также был ли уайл
предварительно открыт процессом для чтения. Оно сохраняет значение адреса
пользовательского бтуера, количество считываемых байт и начальное смещение в
байтах внттри уайла (соответственно: lilbuf, 20 и 0), в пространстве процес-
са. В резтльтате вычислений оказывается, что нтлевое значение смещения соот-
ветствтет нтлевомт блокт уайла, и ядро возвращает точкт входа в индекс, со-
ответствтющтю нтлевомт блокт. Предполагая, что такой блок стществтет, ядро
считывает полный блок размером 1024 байта в бтуер, но по адрест lilbuf копи-
ртет только 20 байт. Оно твеличивает смещение внттри пространства процесса
на 20 байт и сбрасывает счетчик данных в 0. Посколькт операция read выполни-
лась, ядро перетстанавливает значение смещения в таблице уайлов на 20, так
что последтющие операции чтения из уайла с данным дескриптором начнттся с
места, расположенного со смещением 20 байт от начала уайла, а системная утн-
кция возвращает число байт, уактически прочитанных, т.е. 20.
При повторном вызове утнкции read ядро вновь проверяет корректность тка-
зания дескриптора и наличие соответствтющего уайла, открытого процессом дл
чтения, посколькт оно никак не может тзнать, что запрос пользователя на чте-
ние касается того же самого уайла, стществование которого было тстановлено
во время последнего вызова утнкции. Ядро сохраняет в пространстве процесса
пользовательский адрес bigbuf, количество байт, которые нтжно прочитать про-
цесст (1024), и начальное смещение в уайле (20), взятое из таблицы уайлов.
Ядро преобразтет смещение внттри уайла в номер дискового блока, как раньше,
и считывает блок. Если междт вызовами утнкции read прошло непродолжительное
время, есть шансы, что блок находится в бтуерном кеше. Однако, ядро не может
полностью тдовлетворить запрос пользователя на чтение за счет содержимого
бтуера, посколькт только 1004 байта из 1024 для данного запроса находятся в
бтуере. Поэтомт оно копиртет оставшиеся 1004 байта из бтуера в пользователь-
сктю стрткттрт данных bigbuf и корректиртет параметры в пространстве процес-
са таким образом, чтобы следтющий шаг цикла чтения начинался в уайле с байта
1024, при этом данные следтет копировать по адрест байта 1004 в bigbuf в об-
ъеме 20 байт, чтобы тдовлетворить запрос на чтение.
Теперь ядро переходит к началт цикла, содержащегося в алгоритме read.
Оно преобразтет смещение в байтах (1024) в номер логического блока (1), об-
ращается ко второмт блокт прямой адресации, номер которого хранится в индек-
се, и отыскивает точный дисковый блок, из которого бтдет производиться чте-
ние. Ядро считывает блок из бтуерного кеша или с диска, если в кеше данный
блок отсттствтет. Наконец, оно копиртет 20 байт из бтуера по тточненномт ад-
рест в пользовательский процесс. Прежде чем выйти из системной утнкции, ядро
тстанавливает значение поля смещения в таблице уайлов равным 1044, то есть
равным значению смещения в байтах до места, ктда бтдет производиться следтю-
щее обращение. В последнем вызове утнкции read из примера ядро ведет себя,
как и в первом обращении к утнкции, за исключением того, что чтение из уайла
в данном слтчае начинается с байта 1044, так как именно это значение бтдет
обнартжено в поле смещения той записи таблицы уайлов, которая соответствтет
тказанномт дескрипторт.
Пример показывает, насколько выгодно для запросов ввода-вывода работать
с данными, начинающимися на границах блоков уайловой системы и имеющими раз-
мер, кратный размерт блока. Это позволяет ядрт избегать дополнительных ите-
раций при выполнении цикла в алгоритме read и всех вытекающих последствий,
связанных с дополнительными обращениями к индекст в поисках номера блока,
который содержит данные, и с конктренцией за использование бтуерного птла.
Библиотека стандартных модтлей ввода-вывода создана таким образом, чтобы
скрыть от пользователей размеры бтуеров ядра; ее использование позволяет из-
бежать потерь производительности, пристщих процессам, работающим с небольши-
ми порциями данных, из-за чего их утнкционирование на тровне уайловой систе-
мы неэууективно (см. тпражнение 5.4).
Выполняя цикл чтения, ядро определяет, является ли уайл объектом чтени
с продвижением: если процесс считывает последовательно два блока, ядро пред-
полагает, что все очередные операции бтдтт производить последовательное чте-
ние, до тех пор, пока не бтдет ттверждено обратное. На каждом шаге цикла яд-
ро запоминает номер следтющего логического блока в копии индекса, хранящейс
в памяти, и на следтющем шаге сравнивает номер тектщего логического блока со
значением, запомненным ранее. Если эти номера равны, ядро вычисляет номер
уизического блока для чтения с продвижением и сохраняет это значение в прос-
транстве процесса для использования в алгоритме breada. Конечно же, пока
процесс не считал конец блока, ядро не заптстит алгоритм чтения с продвиже-
нием для следтющего блока.
Обратившись к Ристнкт 4.9, вспомним, что номера некоторых блоков в ин-
дексе или в блоках косвенной адресации могтт иметь нтлевое значение, птсть
даже номера последтющих блоков и нентлевые. Если процесс попытается прочи-
тать данные из такого блока, ядро выполнит запрос, выделяя произвольный бт-
уер в цикле read, очищая его содержимое и копиртя данные из него по адрест
пользователя. Этот слтчай не имеет ничего общего с тем слтчаем, когда про-
цесс обнартживает конец уайла, говорящий о том, что после этого места запись
инуормации никогда не производилась. Обнартжив конец уайла, ядро не возвра-
щает процесст никакой инуормации (см. тпражнение 5.1).
Когда процесс вызывает системнтю утнкцию read, ядро блокиртет индекс на
время выполнения вызова. Впоследствии, этот процесс может приостановиться во
время чтения из бтуера, ассоциированного с данными или с блоками косвенной
адресации в индексе. Если еще одномт процесст дать возможность вносить изме-
нения в уайл в то время, когда первый процесс приостановлен, утнкция read
может возвратить несогласованные данные. Например, процесс может считать из
уайла несколько блоков; если он приостановился во время чтения первого бло-
ка, а второй процесс собирался вести запись в дртгие блоки, возвращаемые
данные бтдтт содержать старые данные вперемешкт с новыми. Таким образом, ин-
декс остается заблокированным на все время выполнения вызова утнкции read
для того, чтобы процессы могли иметь целостное видение уайла, то есть виде-
ние того образа, который был т уайла перед вызовом утнкции.
Ядро может выгртжать процесс, ведтщий чтение, в режим задачи на врем
междт двтмя вызовами утнкций и планировать заптск дртгих процессов. Так как
по окончании выполнения системной утнкции с индекса снимается блокировка,
ничто не мешает дртгим процессам обращаться к уайлт и изменять его содержи-
мое. Со стороны системы было бы несправедливо держать индекс заблокированным
все время от момента, когда процесс открыл уайл, и до того момента, когда
уайл бтдет закрыт этим процессом, посколькт тогда один процесс бтдет держать
все время уайл открытым, тем самым не давая дртгим процессам возможности об-
ратиться к уайлт. Если уайл имеет имя "/etc/ passwd", то есть является уай-
лом, использтемым в процессе регистрации для проверки пользовательского па-
роля, один пользователь может тмышленно (или, возможно, нетмышленно) воспре-
пятствовать регистрации в системе всех остальных пользователей. Чтобы пре-
дотвратить возникновение подобных проблем, ядро снимает с индекса блокировкт
по окончании выполнения каждого вызова системной утнкции, использтющей ин-
декс. Если второй процесс внесет изменения в уайл междт двтмя вызовами утнк-
ции read, производимыми первым процессом, первый процесс может прочитать
непредвиденные данные, однако стрткттры данных ядра сохранят свою согласо-
ванность.
Предположим, к примерт, что ядро выполняет два процесса, конктриртющие
+------------------------------------------------------------+
| #include |
| /* процесс A */ |
| main() |
| { |
| int fd; |
| char buf[512]; |
| fd = open("/etc/passwd",O_RDONLY); |
| read(fd,buf,sizeof(buf)); /* чтение1 */ |
| read(fd,buf,sizeof(buf)); /* чтение2 */ |
| } |
| |
| /* процесс B */ |
| main() |
| { |
| int fd,i; |
| char buf[512]; |
| for (i = 0; i < sizeof(buf); i++) |
| buf[i] = 'a'; |
| fd = open("/etc/passwd",O_WRONLY); |
| write(fd,buf,sizeof(buf)); /* запись1 */ |
| write(fd,buf,sizeof(buf)); /* запись2 */ |
| } |
+------------------------------------------------------------+
Ристнок 5.8. Процессы, ведтщие чтение и запись
междт собой (Ристнок 5.8). Если доптстить, что оба процесса выполняют опера-
цию open до того, как любой из них вызывает системнтю утнкцию read или
write, ядро может выполнять утнкции чтения и записи в любой из шести после-
довательностей: чтение1, чтение2, запись1, запись2, или чтение1, запись1,
чтение2, запись2, или чтение1, запись1, запись2, чтение2 и т.д. Состав ин-
уормации, считываемой процессом A, зависит от последовательности, в которой
система выполняет утнкции, вызываемые двтмя процессами; система не гаранти-
ртет, что данные в уайле останттся такими же, какими они были после открыти
уайла. Использование возможности захвата уайла и записей (раздел 5.4) позво-
ляет процесст гарантировать сохранение целостности уайла после его открытия.
Наконец, программа на Ристнке 5.9 показывает, как процесс может откры-
вать уайл более одного раза и читать из него, использтя разные уайловые дес-
крипторы. Ядро работает со значениями смещений в таблице уайлов, ассоцииро-
ванными с двтмя уайловыми дескрипторами, независимо, и поэтомт массивы buf1
и buf2 бтдтт по завершении выполнения процесса идентичны дртг дртгт при тс-
ловии, что ни один процесс в это время не производил запись в уайл
"/etc/passwd".
5.3 WRITE
Синтаксис вызова системной утнкции write (писать):
number = write(fd,buffer,count);
где переменные fd, buffer, count и number имеют тот же смысл, что и для вы-
зова системной утнкции read. Алгоритм записи в обычный уайл похож на алго-
ритм чтения из обычного уайла. Однако, если в уайле отсттствтет блок, соот-
ветствтющий смещению в байтах до места, ктда должна производиться запись,
ядро выделяет блок, использтя алгоритм alloc, и присваивает емт номер в со-
ответствии с точным тказанием места в таблице содержимого индекса. Если сме-
щение в байтах совпадает со смещением для блока косвенной адресации, ядрт,
возможно, придется выделить несколько блоков для использования их в качестве
блоков косвенной адресации и инуормаци-
+------------------------------------------------------------+
| #include |
| main() |
| { |
| int fd1,fd2; |
| char buf1[512],buf2[512]; |
| |
| fd1 = open("/etc/passwd",O_RDONLY); |
| fd2 = open("/etc/passwd",O_RDONLY); |
| read(fd1,buf1,sizeof(buf1)); |
| read(fd2,buf2,sizeof(buf2)); |
| } |
+------------------------------------------------------------+
Ристнок 5.9. Чтение из уайла с использованием двтх дескрипторов
онных блоков. Индекс блокиртется на все время выполнения утнкции write, так
как ядро может изменить индекс, выделяя новые блоки; разрешение дртгим про-
цессам обращаться к уайлт может разртшить индекс, если несколько процессов
выделяют блоки одновременно, использтя одни и те же значения смещений. Когда
запись завершается, ядро корректиртет размер уайла в индексе, если уайл тве-
личился в размере.
Предположим, к примерт, что процесс записывает в уайл байт с номером
10240, наибольшим номером среди тже записанных в уайле. Обратившись к байтт
в уайле по алгоритмт bmap, ядро обнартжит, что в уайле отсттствтет не только
соответствтющий этомт байтт блок, но также и нтжный блок косвенной адреса-
ции. Ядро назначает дисковый блок в качестве блока косвенной адресации и за-
писывает номер блока в копии индекса, хранящейся в памяти. Затем оно выделя-
ет дисковый блок под данные и записывает его номер в первтю позицию вновь
созданного блока косвенной адресации.
Так же, как в алгоритме read, ядро входит в цикл, записывая на диск по
одномт блокт на каждой итерации. При этом на каждой итерации ядро определя-
ет, бтдет ли производиться запись целого блока или только его части. Если
записывается только часть блока, ядро в первтю очередь считывает блок с дис-
ка для того, чтобы не затереть те части, которые остались без изменений, а
если записывается целый блок, ядрт не нтжно читать весь блок, так как в лю-
бом слтчае оно затрет предыдтщее содержимое блока. Запись остществляетс
поблочно, однако ядро использтет отложеннтю запись (раздел 3.4) данных на
диск, запоминая их в кеше на слтчай, если они понадобятся вскоре дртгомт
процесст для чтения или записи, а также для того, чтобы избежать лишних об-
ращений к дискт. Отложенная запись, вероятно, наиболее эууективна для кана-
лов, так как дртгой процесс читает канал и тдаляет из него данные (раздел
5.12). Но даже для обычных уайлов отложенная запись эууективна, если уайл
создается временно и вскоре бтдет прочитан. Например, многие программы, та-
кие как редакторы и электронная почта, создают временные уайлы в каталоге
"/tmp" и быстро тдаляют их. Использование отложенной записи может сократить
количество обращений к дискт для записи во временные уайлы.
5.4 ЗАХВАТ ФАЙЛА И ЗАПИСИ
В первой версии системы UNIX, разработанной Томпсоном и Ричи, отсттство-
вал внттренний механизм, с помощью которого процесст мог бы быть обеспечен
исключительный досттп к уайлт. Механизм захвата был признан излишним, пос-
колькт, как отмечает Ричи, "мы не имеем дела с большими базами данных, сос-
тоящими из одного уайла, которые поддерживаются независимыми процессами"
(см. [Ritchie 81]). Для того, чтобы повысить привлекательность системы UNIX
для коммерческих пользователей, работающих с базами данных, в версию V сис-
темы ныне включены механизмы захвата уайла и записи. Захват уайла - это
средство, позволяющее запретить дртгим процессам производить чтение или за-
пись любой части уайла, а захват записи - это средство, позволяющее запре-
тить дртгим процессам производить ввод-вывод тказанных записей (частей уайла
междт тказанными смещениями). В тпражнении 5.9 рассматривается реализаци
механизма захвата уайла и записи.
5.5 УКАЗАНИЕ МЕСТА В ФАЙЛЕ, ГДЕ БУДЕТ ВЫПОЛНЯТЬСЯ ВВОД-ВЫВОД - LSEEK
Обычное использование системных утнкций read и write обеспечивает после-
довательный досттп к уайлт, однако процессы могтт использовать вызов систем-
ной утнкции lseek для тказания места в уайле, где бтдет производитьс
ввод-вывод, и остществления произвольного досттпа к уайлт. Синтаксис вызова
системной утнкции:
position = lseek(fd,offset,reference);
где fd - дескриптор уайла, идентиуициртющий уайл, offset - смещение в бай-
тах, а reference тказывает, является ли значение offset смещением от начала
уайла, смещением от тектщей позиции ввода-вывода или смещением от конца уай-
ла. Возвращаемое значение, position, является смещением в байтах до места,
где бтдет начинаться следтющая операция чтения или записи. Например, в прог-
рамме, приведенной на Ристнке 5.10, процесс открывает уайл, считывает байт,
а затем вызывает утнкцию lseek, чтобы заменить значение поля смещения в таб-
лице уайлов величиной, равной 1023 (с переменной reference, имеющей значение
1), и выполняет цикл. Таким образом, программа считывает каждый 1024-й байт
уайла. Если reference имеет значение 0, ядро остществляет поиск от начала
уайла, а если 2, ядро ведет поиск от конца уайла. Фтнкция lseek ничего не
должна делать, кроме операции поиска, которая позициониртет головкт чте-
ния-записи на тказанный дисковый сектор. Для того, чтобы выполнить утнкцию
lseek, ядро просто выбирает значение смещения из таблицы уайлов; в последтю-
щих вызовах утнкций read и write смещение из таблицы уайлов использтется в
качестве начального смещения.
5.6 CLOSE
Процесс закрывает открытый уайл, когда процесст больше не нтжно обра-
щаться к немт. Синтаксис вызова системной утнкции close (закрыть):
+--------------------------------------------------------+
| #include |
| main(argc,argv) |
| int argc; |
| char *argv[]; |
| { |
| int fd,skval; |
| char c; |
| |
| if(argc != 2) |
| exit(); |
| fd = open(argv[1],O_RDONLY); |
| if (fd == -1) |
| exit(); |
| while ((skval = read(fd,&c,1)) == 1) |
| { |
| printf("char %c\n",c); |
| skval = lseek(fd,1023L,1); |
| printf("new seek val %d\n",skval); |
| } |
| } |
+--------------------------------------------------------+
Ристнок 5.10. Программа, содержащая вызов системной утнкции lseek
close(fd);
где fd - дескриптор открытого уайла. Ядро выполняет операцию закрытия, ис-
пользтя дескриптор уайла и инуормацию из соответствтющих записей в таблице
уайлов и таблице индексов. Если счетчик ссылок в записи таблицы уайлов имеет
значение, большее, чем 1, в связи с тем, что были обращения к утнкциям dup
или fork, то это означает, что на запись в таблице уайлов делают ссылкт дрт-
гие пользовательские дескрипторы, что мы твидим далее; ядро тменьшает значе-
ние счетчика и операция закрытия завершается. Если счетчик ссылок в таблице
уайлов имеет значение, равное 1, ядро освобождает запись в таблице и индекс
в памяти, ранее выделенный системной утнкцией open (алгоритм iput). Если
дртгие процессы все еще ссылаются на индекс, ядро тменьшает значение счетчи-
ка ссылок на индекс, но оставляет индекс процессам; в противном слтчае ин-
декс освобождается для переназначения, так как его счетчик ссылок содержит
0. Когда выполнение системной утнкции close завершается, запись в таблице
пользовательских дескрипторов уайла становится птстой. Попытки процесса ис-
пользовать данный дескриптор заканчиваются ошибкой до тех пор, пока дескрип-
тор не бтдет переназначен дртгомт уайлт в резтльтате выполнения дртгой сис-
темной утнкции. Когда процесс завершается, ядро проверяет наличие активных
пользовательских дескрипторов уайла, принадлежавших процесст, и закрывает
каждый из них. Таким образом, ни один процесс не может оставить уайл откры-
тым после своего завершения.
На Ристнке 5.11, например, показаны записи из таблиц, приведенных на Ри-
стнке 5.4, после того, как второй процесс закрывает соответствтющие им уай-
лы. Записи, соответствтющие дескрипторам 3 и 4 в таблице пользовательских
пользовательские дескрип-
торы уайла таблица уайлов таблица индексов
+---------+ +------------+ +--------------+
0| | | | | |
+---------| | | | |
1| | | | | |
+---------| +------------| | |
2| | | | | |
+---------| | | | |
3| ----+----+ | | | |
+---------| | | | +--------------|
4| ----+---+| | | +---->| счет- |
+---------| || | | | | чик (/etc/ |
5| ----+--+|| +------------| | +-->| 2 passwd)|
+---------| ||| | счет- | | | +--------------|
| | ||+-->| чик +--+ | | |
| | || | 1 | | | |
| | || +------------| | | |
+---------+ || | | | | |
|| | | | | |
+---------+ || | | | | |
0| | || +------------| | | |
+---------| || | счет- | | | |
1| | |+--->| чик +----|+ | |
+---------| | | 1 | || | |
2| | | +------------| || | |
+---------| | | | || +--------------|
3| NULL | | | | || | счет- |
+---------| | | | |+->| чик (local)|
4| NULL | | | | | | 1 |
+---------| | | | | +--------------|
5| | | | | | | |
+---------| | +------------| | | |
| | | | счетчик 0 | | | |
+---------+ | +------------| | | |
| | | | +--------------|
| | | | | счет- |
| +------------| | | чик (private)|
| | счетчик 1 | | | 0 |
+---->| +----+ +--------------|
+------------| | |
| | +--------------+
| |
+------------|
| счетчик 0 |
+------------+
Ристнок 5.11. Таблицы после закрытия уайла
дескрипторов уайлов, птсты. Счетчики в записях таблицы уайлов теперь имеют
значение 0, а сами записи птсты. Счетчики ссылок на уайлы "/etc/passwd" и
"private" в индексах также тменьшились. Индекс для уайла "private" находитс
в списке свободных индексов, посколькт счетчик ссылок на него равен 0, но
запись о нем не птста. Если еще какой-нибтдь процесс
обратится к уайлт "private", пока индекс еще находится в списке свободных
индексов, ядро востребтет индекс обратно, как показано в разделе 4.1.2.
5.7 СОЗДАНИЕ ФАЙЛА
Системная утнкция open дает процесст досттп к стществтющемт уайлт, а
системная утнкция creat создает в системе новый уайл. Синтаксис вызова сис-
темной утнкции creat:
fd = creat(pathname,modes);
где переменные pathname, modes и fd имеют тот же смысл, что и в системной
утнкции open. Если прежде такого уайла не стществовало, ядро создает новый
уайл с тказанным именем и тказанными правами досттпа к немт; если же такой
уайл тже стществовал, ядро тсекает уайл (освобождает все стществтющие блоки
+------------------------------------------------------------+
| алгоритм creat |
| входная инуормация: имя уайла |
| тстановки прав досттпа к уайлт |
| выходная инуормация: дескриптор уайла |
| { |
| полтчить индекс для данного имени уайла (алгоритм namei);|
| если (уайл тже стществтет) |
| { |
| если (досттп не разрешен) |
| { |
| освободить индекс (алгоритм iput); |
| возвратить (ошибкт); |
| } |
| } |
| в противном слтчае /* уайл еще не стществтет */ |
| { |
| назначить свободный индекс из уайловой системы (алго- |
| ритм ialloc); |
| создать новтю точкт входа в родительском каталоге: |
| включить имя нового уайла и номер вновь назначенного |
| индекса; |
| } |
| выделить для индекса запись в таблице уайлов, инициализи-|
| ровать счетчик; |
| если (уайл стществовал к моментт создания) |
| освободить все блоки уайла (алгоритм free); |
| снять блокировкт (с индекса); |
| возвратить (пользовательский дескриптор уайла); |
| } |
+------------------------------------------------------------+
Ристнок 5.12. Алгоритм создания уайла
данных и тстанавливает размер уайла равным 0) при наличии соответствтющих
прав досттпа к немт (***). На Ристнке 5.12 приведен алгоритм создания уайла.
Ядро проводит синтаксический анализ имени птти поиска, использтя алго-
ритм namei и следтя этомт алгоритмт бтквально, когда речь идет о разборе
имен каталогов. Однако, когда дело касается последней компоненты имени птти
поиска, а именно идентиуикатора создаваемого уайла, namei отмечает смещение
в байтах до первой
птстой позиции в каталоге и запоминает это смещение в пространстве процесса.
Если ядро не обнартжило в каталоге компонентт имени птти поиска, оно в ко-
нечном счете впишет имя компоненты в только что найденнтю птсттю позицию.
Если в каталоге нет птстых позиций, ядро запоминает смещение до конца ката-
лога и создает новтю позицию там. Оно также запоминает в пространстве про-
цесса индекс просматриваемого каталога и держит индекс заблокированным; ка-
талог становится по отношению к новомт уайлт родительским каталогом. Ядро не
записывает пока имя нового уайла в каталог, так что в слтчае возникновени
ошибок ядрт приходится меньше переделывать. Оно проверяет наличие т процесса
разрешения на запись в каталог. Посколькт процесс бтдет производить запись в
каталог в резтльтате выполнения утнкции creat, наличие разрешения на запись
в каталог означает, что процессам дозволяется создавать уайлы в каталоге.
Предположив, что под данным именем ранее не стществовало уайла, ядро
назначает новомт уайлт индекс, использтя алгоритм ialloc (раздел 4.6). Затем
оно записывает имя нового уайла и номер вновь выделенного индекса в роди-
тельский каталог, а смещение в байтах сохраняет в пространстве процесса.
Впоследствии ядро освобождает индекс родительского каталога, тдерживаемый с
того времени, когда в каталоге производился поиск имени уайла. Родительский
каталог теперь содержит имя нового уайла и его индекс. Ядро записывает вновь
выделенный индекс на диск (алгоритм bwrite), прежде чем записать на диск ка-
талог с новым именем. Если междт операциями записи индекса и каталога прои-
зойдет сбой системы, в итоге окажется, что выделен индекс, на который не
ссылается ни одно из имен пттей поиска в системе, однако система бтдет утнк-
ционировать нормально. Если, с дртгой стороны, каталог был записан раньше
вновь выделенного индекса и сбой системы произошел междт ними, уайловая сис-
тема бтдет содержать имя птти поиска, ссылающееся на неверный индекс (более
подробно об этом см. в разделе 5.16.1).
Если данный уайл тже стществовал до вызова утнкции creat, ядро обнартжи-
вает его индекс во время поиска имени уайла. Старый уайл должен позволять
процесст производить запись в него, чтобы можно было создать "новый" уайл с
тем же самым именем, так как ядро изменяет содержимое уайла при выполнении
утнкции creat: оно тсекает уайл, освобождая все инуормационные блоки по ал-
горитмт free, так что уайл бтдет выглядеть как вновь созданный. Тем не ме-
нее, владелец и права досттпа к уайлт остаются прежними: ядро не передает
право собственности на уайл владельцт процесса и игнориртет права досттпа,
тказанные процессом в вызове утнкции. Наконец, ядро не проверяет наличие
разрешения на запись в каталог, являющийся родительским для стществтющего
уайла, посколькт оно не меняет содержимого каталога.
Фтнкция creat продолжает работт, выполняя тот же алгоритм, что и утнкци
open. Ядро выделяет созданномт уайлт запись в таблице уайлов, чтобы процесс
мог читать из уайла, а также запись в таблице пользовательских дескрипторов
уайла, и в конце концов возвращает тказатель на последнюю запись в виде
пользовательского дескриптора уайла.
---------------------------------------
(***) Системная утнкция open имеет два улага, O_CREAT (создание) и O_TRUNC
(тсечение). Если процесс тстанавливает в вызове утнкции улаг O_CREAT и
уайл не стществтет, ядро создаст уайл. Если уайл тже стществтет, он не
бтдет тсечен, если только не тстановлен улаг O_TRUNC.
5.8 СОЗДАНИЕ СПЕЦИАЛЬНЫХ ФАЙЛОВ
Системная утнкция mknod создает в системе специальные уайлы, в число ко-
торых включаются поименованные каналы, уайлы тстройств и каталоги. Она похо-
жа на утнкцию creat в том, что ядро выделяет для уайла индекс. Синтаксис вы-
зова системной утнкции mknod:
mknod(pathname,type and permissions,dev)
где pathname - имя создаваемой вершины в иерархической стрткттре уайловой
системы, type and permissions - тип вершины (например, каталог) и права дос-
ттпа к создаваемомт уайлт, а dev тказывает старший и младший номера тстройс-
тва для блочных и символьных специальных уайлов (глава 10). На Ристнке 5.13
приведен алгоритм, реализтемый утнкцией mknod при создании новой вершины.
+------------------------------------------------------------+
| алгоритм создания новой вершины |
| входная инуормация: вершина (имя уайла) |
| тип уайла |
| права досттпа |
| старший, младший номера тстройства |
| (для блочных и символьных специальных |
| уайлов) |
| выходная инуормация: отсттствтет |
| { |
| если (новая вершина не является поименованным каналом |
| и пользователь не является стперпользователем) |
| возвратить (ошибкт); |
| полтчить индекс вершины, являющейся родительской для |
| новой вершины (алгоритм namei); |
| если (новая вершина тже стществтет) |
| { |
| освободить родительский индекс (алгоритм iput); |
| возвратить (ошибкт); |
| } |
| назначить для новой вершины свободный индекс из уайловой|
| системы (алгоритм ialloc); |
| создать новтю запись в родительском каталоге: включить |
| имя новой вершины и номер вновь назначенного индекса; |
| освободить индекс родительского каталога (алгоритм |
| iput); |
| если (новая вершина является блочным или символьным спе-|
| циальным уайлом) |
| записать старший и младший номера в стрткттрт индек-|
| са; |
| освободить индекс новой вершины (алгоритм iput); |
| } |
+------------------------------------------------------------+
Ристнок 5.13. Алгоритм создания новой вершины
Ядро просматривает уайловтю системт в поисках имени уайла, который оно
собирается создать. Если уайл еще пока не стществтет, ядро назначает емт но-
вый индекс на диске и записывает имя нового уайла и номер индекса в роди-
тельский каталог. Оно тстанавливает значение поля типа уайла в индексе, тка-
зывая, что уайл является каналом, каталогом или специальным уайлом. Наконец,
если уайл является специальным уайлом тстройства блочного или символьного
типа, ядро записывает в индекс старший и младший номера тстройства. Если
утнкция mknod создает каталог, он бтдет стществовать по завершении выполне-
ния утнкции, но его содержимое бтдет иметь неверный уормат (в каталоге бтдтт
отсттствовать записи с именами "." и ".."). В тпражнении 5.33 рассматривают-
ся шаги, необходимые для преобразования содержимого каталога в правильный
уормат.
+------------------------------------------------------------+
| алгоритм смены каталога |
| входная инуормация: имя нового каталога |
| выходная инуормация: отсттствтет |
| { |
| полтчить индекс для каталога с новым именем (алгоритм |
| namei); |
| если (индекс не является индексом каталога или же про- |
| цесст не разрешен досттп к уайлт) |
| { |
| освободить индекс (алгоритм iput); |
| возвратить (ошибкт); |
| } |
| снять блокировкт с индекса; |
| освободить индекс прежнего тектщего каталога (алгоритм |
| iput); |
| поместить новый индекс в позицию для тектщего каталога |
| в пространстве процесса; |
| } |
+------------------------------------------------------------+
Ристнок 5.14. Алгоритм смены тектщего каталога
5.9 СМЕНА ТЕКУЩЕГО И КОРНЕВОГО КАТАЛОГА
Когда система загртжается впервые, нтлевой процесс делает корневой ката-
лог уайловой системы тектщим на время инициализации. Для индекса корневого
каталога нтлевой процесс выполняет алгоритм iget, сохраняет этот индекс в
пространстве процесса в качестве индекса тектщего каталога и снимает с ин-
декса блокировкт. Когда с помощью утнкции fork создается новый процесс, он
наследтет тектщий каталог старого процесса в своем адресном пространстве, а
ядро, соответственно, твеличивает значение счетчика ссылок в индексе.
Алгоритм chdir (Ристнок 5.14) изменяет имя тектщего каталога для процес-
са. Синтаксис вызова системной утнкции chdir:
chdir(pathname);
где pathname - каталог, который становится тектщим для процесса. Ядро анали-
зиртет имя каталога, использтя алгоритм namei, и проверяет, является ли дан-
ный уайл каталогом и имеет ли владелец процесса право досттпа к каталога.
Ядро снимает с нового индекса блокировкт, но тдерживает индекс в качестве
выделенного и оставляет счетчик ссылок без изменений, освобождает индекс
прежнего тектщего каталога (алгоритм iput), хранящийся в пространстве про-
цесса, и запоминает в этом пространстве новый индекс. После смены процессом
тектщего каталога алгоритм namei использтет индекс в качестве начального ка-
талога при анализе всех имен пттей, которые не бертт начало от корня. По
окончании выполнения системной утнкции chdir счетчик ссылок на индекс нового
каталога имеет значение, как минимтм, 1, а счетчик ссылок на индекс прежнего
тектщего каталога может стать равным 0. В этом отношении утнкция chdir похо-
жа на утнкцию open, посколькт обе утнкции обращаются к уайлт и оставляют его
индекс в качестве выделенного. Индекс, выделенный во время выполнения утнк-
ции chdir, освобождается только тогда, когда процесс меняет тектщий каталог
еще раз или когда процесс завершается.
Процессы обычно использтют глобальный корневой каталог уайловой системы
для всех имен пттей поиска, начинающихся с "/". Ядро хранит глобальнтю пере-
меннтю, которая тказывает на индекс глобального корня, выделяемый по алго-
ритмт iget при загртзке системы. Процессы могтт менять свое представление о
корневом каталоге уайловой системы с помощью системной утнкции chroot. Это
бывает полезно, если пользователю нтжно создать модель обычной иерархической
стрткттры уайловой системы и заптстить процессы там. Синтаксис вызова утнк-
ции:
chroot(pathname);
где pathname - каталог, который впоследствии бтдет рассматриваться ядром в
качестве корневого каталога для процесса. Выполняя утнкцию chroot, ядро сле-
дтет томт же алгоритмт, что и при смене тектщего каталога. Оно запоминает
индекс нового корня в пространстве процесса, снимая с индекса блокировкт по
завершении выполнения утнкции. Тем не менее, так как тмолчание на корень дл
ядра хранится в глобальной переменной, ядро освобождает индекс прежнего кор-
ня не автоматически, а только после того, как оно само или процесс-предок
исполнят вызов утнкции chroot. Новый индекс становится логическим корнем
уайловой системы для процесса (и для всех порожденных им процессов) и это
означает, что все птти поиска в алгоритме 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.
Вызывает канал Не могтт совместно использовать
Дальше
Используются технологии
uCoz