ВУЗ: Не указан

Категория: Книга

Дисциплина: Операционные системы

Добавлен: 29.10.2018

Просмотров: 63395

Скачиваний: 231

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

10.6. Файловая система UNIX   

851

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

.

» и «

..

». Первая запись 

обозначает сам каталог. Вторая является ссылкой на родительский каталог, то есть 
каталог, в котором данный каталог числится как запись. Таким образом, из каталога 

/usr/fred

 к файлу Лизы 

x

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

../lisa/x

.

Кроме обычных файлов Linux поддерживает также символьные специальные файлы 
и блочные специальные файлы. Символьные специальные файлы используются для 
моделирования последовательных устройств ввода-вывода, таких как клавиатуры 
и принтеры. Если процесс откроет файл 

/dev/tty

 и прочитает из него, то он получит 

введенные с клавиатуры символы. Если открыть файл 

/dev/lp

 и записать в него дан-

ные, то эти данные будут распечатаны на принтере. Блочные специальные файлы 
(обычно с такими именами, как 

/dev/hd1

) могут использоваться для чтения и записи 

необработанных дисковых разделов, минуя файловую систему. При этом поиск байта 
номер k, за которым последует чтение, приведет к чтению k-го байта из соответству-
ющего дискового раздела, игнорируя i-узел и файловую структуру. Необработанные 
блочные устройства используются для страничной подкачки и свопинга программами 
установки файловой системы (например, mkfs), а также программами, исправляющими 
поврежденные файловые системы (например, fsck).

На многих компьютерах установлено по два и более жестких диска. Например, на 
мэйнфреймах в банках часто бывает необходимо иметь по 100 и более дисков (чтобы 
хранить огромные базы данных). Даже у персональных компьютеров часто имеется по 
меньшей мере два диска — жесткий диск и дисковод для оптических дисков (напри-
мер, DVD). При наличии у компьютера нескольких дисков возникает необходимость 
в управлении ими.

Одно из решений заключается в том, чтобы создать отдельную файловую систему 
на каждом диске и управлять ими по отдельности. Например, рассмотрим ситуацию, 
изображенную на рис. 10.15, а. Здесь показан жесткий диск, который мы будем на-
зывать 

C:

, а также DVD, который мы будем называть 

D:

. У каждого есть собственный 

корневой каталог и файлы. При таком решении пользователь должен помимо каталогов 
указывать также устройство (если оно отличается от используемого по умолчанию). 
Например, чтобы скопировать файл 

x

 в каталог 

d

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

бирается диск 

C:

), следует ввести команду

cp D:/x /a/d/x

Такой подход применяется в нескольких операционных системах, включая Windows 8, 
которая унаследовала его у MS-DOS, появившейся в прошлом веке.

Применяемое в операционной системе Linux решение заключается в том, чтобы по-
зволить смонтировать один диск в дерево файлов другого диска. В нашем примере 
мы можем смонтировать DVD в каталог 

/b

, получая в результате файловую систему, 

показанную на рис. 10.15, б. Теперь пользователь видит единое дерево файлов и уже 
не должен думать о том, какой файл на каком устройстве находится. В результате при-
веденная ранее команда примет вид

cp /b/x /a/d/x

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


background image

852  

 Глава 10. Изучение конкретных примеров: Unix, Linux и Android 

DVD

Рис. 10.15. Файловые системы: а — раздельные; б — после монтирования

Другое интересное свойство файловой системы Linux — блокировка (locking) . В не-
которых приложениях два и более процесса могут одновременно использовать один 
и тот же файл, что может привести к условиям гонки. Одно из решений данной про-
блемы заключается в том, чтобы создать в приложении критические области. Но если 
эти процессы принадлежат независимым пользователям, которые даже не знакомы 
друг с другом, то такой способ координации действий, как правило, очень неудобен.

Рассмотрим, например, базу данных, состоящую из многих файлов в одном или не-
скольких каталогах, доступ к которым получают никак не связанные между собой 
пользователи. С каждым каталогом или файлом можно связать семафор и достичь 
взаимного исключения, заставляя процессы выполнять операцию down на соответству-
ющем семафоре перед доступом к данным. Недостаток такого решения заключается 
в том, что при этом недоступным становится весь каталог или файл — даже если нужна 
всего одна запись.

По этой причине стандарт POSIX предоставляет гибкий и детальный механизм, по-
зволяющий процессам за одну неделимую операцию блокировать даже единственный 
байт файла (или целый файл). Механизм блокировки требует от вызывающей стороны 
указать блокируемый файл, начальный байт и количество байтов. Если операция за-
вершается успешно, то система создает запись в таблице, в которой указывается, что 
определенные байты файла (например, запись базы данных) заблокированы.

Стандартом определены два типа блокировки: блокировка с монополизацией (exclusive 
locks)   и  блокировка без монополизации (shared locks) . Если часть файла уже имеет 
блокировку без монополизации, то повторная попытка установки блокировки без моно-
полизации на это место файла разрешается, но попытка установить блокировку с моно-
полизацией будет отвергнута. Если же какая-либо область файла содержит блокировку 
с монополизацией, то любые попытки заблокировать любую часть этой области файла 
будут отвергаться, пока не будет снята блокировка. Для успешной установки блокировки 
необходимо, чтобы каждый байт в блокируемой области был доступен.

При установке блокировки процесс должен указать, будет ли он блокироваться в том 
случае, когда блокировку установить не удается. Если процесс выбрал блокирование, 
то он блокируется до тех пор, пока с запрашиваемой области файла не будет снята 
блокировка, после чего процесс активизируется и блокировка устанавливается. Если 
процесс решил не блокироваться при невозможности установить блокировку, то 


background image

10.6. Файловая система UNIX   

853

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

Заблокированные области могут перекрываться. На рис. 10.16, а мы видим, что про-
цесс A установил блокировку без монополизации на байты с 4-го по 7-й в некотором 
файле. Затем процесс B устанавливает блокировку без монополизации на байты с 6-го 
по 9-й (рис. 10.16, б). Наконец, процесс C блокирует байты со 2-го по 11-й. Пока это 
блокировки без монополизации, они могут существовать одновременно.

Теперь посмотрим, что произойдет, если процесс попытается получить блокировку 
с монополизацией на байт 9 (рис. 10.16, в), блокируясь при неудаче блокировки. Две 
предыдущие блокировки перекрываются с этой блокировкой. Поэтому вызывающая 
сторона будет заблокирована и останется заблокированной до тех пор, пока оба про-
цесса (B и C) не снимут свои блокировки.

Рис. 10.16. Файл: а — с одной блокировкой; б — добавление второй блокировки; 

в — третья блокировка

10.6.2. Вызовы файловой системы в Linux

Многие системные вызовы   имеют отношение к файлам и файловой системе. Сначала 
мы рассмотрим системные вызовы, работающие с отдельными файлами. Затем изучим 
те системные вызовы, которые оперируют каталогами или всей файловой системой 
в целом. Для создания нового файла можно использовать системный вызов creat
(Когда Кена Томпсона однажды спросили, что бы он поменял, если бы у него была воз-
можность во второй раз разработать операционную систему UNIX, он ответил, что на 


background image

854  

 Глава 10. Изучение конкретных примеров: Unix, Linux и Android 

этот раз вместо creat он назвал бы этот системный вызов create.) В качестве параметров 
этому системному вызову следует задать имя файла и режим защиты. Так, команда

fd = creat("abc", mode);

создает файл 

abc

 с режимом защиты, указанным в mode. Биты mode определяют круг 

пользователей, которые могут получить доступ к файлу, а также уровень предостав-
ляемого им доступа. Вопросы защиты файлов и доступа к ним будут рассмотрены 
в дальнейшем.

Системный вызов creat не только создает новый файл, но и открывает его для записи. 
Чтобы последующие системные вызовы могли получить доступ к файлу, успешный 
системный вызов creat возвращает небольшое неотрицательное целое число, называ-
емое дескриптором файла (file descriptor)  (fd в приведенном ранее примере). Если 
системный вызов выполняется с уже существующим файлом, то длина этого файла 
уменьшается до 0, а все его содержимое теряется. Файлы можно создавать также при 
помощи вызова open с соответствующими аргументами.

Теперь продолжим изучение основных вызовов файловых систем, перечисленных 
в табл. 10.9. Чтобы прочитать данные из существующего файла или записать данные 
в существующий файл, его нужно сначала открыть с помощью open или creat. Этому 
системному вызову следует указать имя файла, а также режим, в котором он должен 
быть открыт: для чтения, для записи либо и для того и для другого. Также можно указать 
различные дополнительные параметры. Как и creat, системный вызов open возвращает 
дескриптор файла, который может быть использован для чтения или записи. Затем файл 
может быть закрыт при помощи вызова close, после чего дескриптор файла можно ис-
пользовать повторно (для последующего creat или open). Системные вызовы creat и open 
всегда возвращают наименьший неиспользуемый в данный момент дескриптор файла.

Таблица 10.9. Некоторые системные вызовы для работы с файлами. В случае ошиб-
ки возвращаемое значение s равно –1, fd — дескриптор файла, position — смещение 
в файле. Параметры должны быть понятны без пояснений

Системный вызов

Описание

fd=creat(name, mode)

Один из способов создания нового файла

fd=open(file, how, ј

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

s=close(fd)

Закрыть открытый файл

n=read(fd, buffer, nbytes)

Прочитать данные из файла в буфер

n=write(fd, buffer, nbytes)

Записать данные из буфера в файл

position=lseek(fd, offset, whence)

Переместить указатель в файле

s=stat(name, &buf)

Получить информацию о состоянии файла

s=fstat(fd, &buf)

Получить информацию о состоянии файла

s=pipe(&fd[0])

Создать канал

s=fcntl(fd, cmd, ...)

Блокировка файла и другие операции

Когда программа начинает выполнение стандартным образом, файловые дескрипторы 
0, 1 и 2 уже открыты для стандартного ввода, стандартного вывода и стандартного пото-
ка сообщений об ошибках соответственно. Таким образом, фильтр (например, програм-


background image

10.6. Файловая система UNIX   

855

ма sort) может просто читать свои входные данные из файла с дескриптором 0, а писать 
выходные данные в файл с дескриптором 1, не заботясь о том, что это за файлы. Работа 
этого механизма обеспечивается оболочкой, которая проверяет, чтобы эти дескрипторы 
соответствовали нужным файлам (прежде чем программа начнет свою работу).

Чаще всего программы используют системные вызовы read и write. У обоих вызовов по 
три параметра: дескриптор файла (указывающий, с каким из открытых файлов будет 
производиться операция чтения или записи), адрес буфера (сообщающий, куда по-
ложить данные или откуда их взять), а также счетчик (указывающий, сколько байтов 
следует передать). Вот и все. Очень простая схема. Пример типичного вызова:

n = read(fd, buffer, nbytes)

Хотя большинство программ читают и записывают файлы последовательно, некоторым 
программам необходимо бывает иметь доступ к произвольной части файла. С каждым 
открытым файлом связан указатель, который обозначает текущую позицию в файле. 
При последовательном чтении (или записи) он указывает на следующий байт, который 
будет прочитан (или записан). Например, если перед чтением 1024 байтов указатель 
был установлен на 4096-й байт, то после успешного системного вызова read он будет 
автоматически перемещен на 5120-й байт. Указатель в файле можно переместить с по-
мощью системного вызова lseek, что позволяет при последующих системных вызовах 
read (или write) читать данные из файла (или писать их в файл) в произвольной пози-
ции файла и даже за концом файла. Этот системный вызов назван lseek, чтобы не путать 
его с теперь уже устаревшим, использовавшимся ранее на 16-разрядных компьютерах 
системным вызовом seek.

У системного вызова lseek три параметра: первый — дескриптор файла, второй — по-
зиция в файле, а третий сообщает, относительно чего эта позиция — начала файла, 
текущей позиции или конца файла. Возвращаемое системным вызовом lseek значение 
представляет собой абсолютную позицию в файле после того, как указатель был из-
менен. Забавно, что системный вызов lseek — это единственный системный вызов из 
относящихся к файловой системе, который никогда не вызывает перемещения блока 
головок диска, так как все, что он делает, — это обновление текущей позиции в файле, 
представляющей собой просто число в памяти.

Для каждого файла операционная система Linux хранит такие сведения, как тип 
(режим) файла (обычный, каталог, специальный файл), его размер, время последней 
модификации, и другую информацию. Программы могут получить эту информацию 
при помощи системного вызова stat. Первый параметр представляет собой имя файла. 
Второй является указателем на структуру, в которую следует поместить запрошенную 
информацию. Поля этой структуры перечислены в табл. 10.10. Системный вызов 
fstat — это то же самое, что и системный вызов stat, с той лишь разницей, что он ра-
ботает с уже открытым файлом (имя которого может быть неизвестно), а не с путем.

Таблица 10.10. Поля структуры, возвращаемой системным вызовом stat

Устройство, на котором располагается файл

Номер i-узла (идентифицирует файл на устройстве)

Режим файла (включая информацию о защите)

Количество ссылок файла