Чтение онлайн

ЖАНРЫ

Шрифт:

UNIX позволяет обеспечить блокирование заданного диапазона байтов файла или записи файла. Для этого служат базовый системный вызов управления файлом fcntl(2) и библиотечная функция lockf(3C), предназначенная специально для управления блокированием. При этом перед фактической файловой операцией (чтения или записи) процесс устанавливает блокирование соответствующего типа (для чтения или для записи). Если блокирование завершилось успешно, это означает, что требуемая файловая операция не создаст конфликта или нарушения целостности данных, например, при одновременной записи в файл несколькими процессами.

По умолчанию блокирование является рекомендательным (advisory lock). Это означает, что кооперативно работающие процессы могут руководствоваться созданными блокировками, однако ядро не запрещает чтение или запись в заблокированный участок файла. При

работе с рекомендательными блокировками процесс должен явно проверять их наличие с помощью тех же функций fcntl(2) и lockf(3C).

Мы уже встречались с использованием системного вызова fnctl(2) для блокирования записей файла в главе 2. Там же была упомянута структура

flock
, служащая для описания блокирования. Поля этой структуры описаны в табл. 4.8.

Таблица 4.8. Поля структуры flock

Поле Описание
short l_type
Тип блокирования:
F_RDLCK
обозначает блокирование для чтения (read lock),
F_WRLCK
— блокирование для записи (write lock),
F_UNLCK
обозначает снятие блокирования.
short l_whence
Точка отсчета смещения записи в файле. Может принимать значения, аналогичные рассмотренным при разговоре о функции lseek(2) в главе 2:
SEEK_SET
,
SEEK_CUR
,
SEEK_END
.
off_t l_start
Смещение блокируемой записи относительно точки отсчета, указанной полем
l_whence
.
off_t l_len
Длина блокируемой записи. Нулевое значение
l_len
указывает, что запись всегда распространяется до конца файла, независимо от возможного изменения его размера.
pid_t l_pid
Идентификатор процесса, установившего блокирование, возвращаемый при вызове команды
GETLK
.

Как следует из описания поля

l_type
структуры
flock
, существуют два типа блокирования записи: для чтения (
F_RDLCK
) и для записи (
F_WRLCK
). Правила блокирования таковы, что может быть установлено несколько блокирований для чтения на конкретный байт файла, при этом в установке блокирования для записи на этот байт будет отказано. Напротив, блокирование для записи на конкретный байт должно быть единственным, при этом в установке блокирования для чтения будет отказано.

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

...

struct flock lock;

...

/* Заполним описание lock с целью блокирования всего файла

для записи */

lock.l_type = FWRLCK;

lock.l_start = 0;

lock.whence = SEEK_SET;

lock.len = 0;

/* Заблокируем файл. Если блокирования, препятствующие

данной операции, уже существуют — ждем их снятия */

fcntl(fd, SETLKW, &lock);

/* Запишем данные в файл - нам никто не помешает */

write(fd, record, sizeof(record));

/* Снимем блокирование */

lock.l_type = F_UNLK;

fcntl(fd, SETLKW, &lock);

В отличие от рекомендательного в UNIX существует обязательное блокирование (mandatory lock), при котором ограничение на доступ к записям файла накладывается самим ядром. Реализация обязательных блокировок может быть различной. Например, в SCO UNIX (SVR3) снятие бита x для группы и установка бита SGID для группы приводит к тому, что блокировки, установленные fcntl(2) или lockf(3C), станут обязательными. UNIX SVR4 поддерживает установку блокирования отдельно для записи и для чтения, обеспечивая тем самым доступ для чтения

многим, а для записи — только одному процессу. Эти установки также осуществляются с помощью системного вызова fcntl(2). Следует иметь в виду, что использование обязательного блокирования таит потенциальную опасность. Например, если процесс блокирует доступ к жизненно важному системному файлу и по каким-либо причинам теряет контроль, это может привести к аварийному останову операционной системы.

Буферный кэш

Во введении отмечалось, что работа файловой подсистемы тесно связана с обменом данными с периферийными устройствами. Для обычных файлов и каталогов — это устройство, на котором размещается соответствующая файловая система, для специальных файлов устройств — это принтер, терминал, или сетевой адаптер. Не вдаваясь в подробности подсистемы ввода/вывода, рассмотрим, как во многих версиях UNIX организован обмен данными с дисковыми устройствами — традиционным местом хранения подавляющего большинства файлов [48] .

48

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

Не секрет, что операции дискового ввода/вывода являются медленными по сравнению, например, с доступом к оперативной или сверхоперативной памяти. Время чтения данных с диска и копирования тех же данных в памяти может различаться в несколько тысяч раз. Поскольку основные данные хранятся на дисковых накопителях, дисковый ввод/вывод является узким местом операционной системы. Для повышения производительности дискового ввода/вывода и, соответственно, всей системы в целом, в UNIX используется кэширование дисковых блоков в памяти.

Для этого используется выделенная область оперативной памяти, где кэшируются дисковые блоки файлов, к которым наиболее часто осуществляется доступ. Эта область памяти и связанный с ней процедурный интерфейс носят название буферного кэша, и через него проходит большинство операций файлового ввода/вывода. Схема взаимодействия различных подсистем ядра с буферным кэшем приведена на рис. 4.13.

Рис. 4.13. Роль буферного кэша

Внутренняя структура буферного кэша

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

buf
, с помощью которого ядро производит управление кэшем, включая идентификацию и поиск буферов, а также синхронизацию доступа. Заголовок также используется при обмене данными с драйвером устройства для выполнения фактической операции ввода/вывода. Когда возникает необходимость чтения или записи буфера на диск, ядро заносит параметры операции ввода/вывода в заголовок и передает его функции драйвера устройства. После завершения операции ввода/вывода заголовок содержит информацию о ее результатах.

Основные поля структуры

buf
приведены в табл. 4.9.

Таблица 4.9. Поля структуры buf

Поле Описание
b_flags
Флаги. Определяют состояние буфера в каждый момент времени (например,
B_BUSY
— буфер занят или
B_DONE
— закончена операция ввода/вывода с буфером) и направление передачи данных (
B_READ
,
B_WRITE
,
B_PHYS
)
av_forw
,
av_back
Указатели двухсвязного рабочего списка буферов, ожидающих обработки драйвером
b_bcount
Число байтов, которое требуется передать
b_un.b_addr
Виртуальный адрес буфера
b_blkno
Номер блока начала данных на устройстве
b_dev
Старший и младший номера устройства
Поделиться с друзьями: