Начиная c версии ядра 2.4, Linux позволяет приложению обходить буферную кэш-память при выполнении дискового ввода-вывода, перемещая данные непосредственно из пользовательского пространства памяти в файл или на дисковое устройство. Иногда этот режим называют
Приведенная здесь информация относится исключительно к Linux и не стандартизирована в SUSv3. Тем не менее некоторые варианты непосредственного доступа к вводу-выводу в отношении устройств или файлов предоставляются большинством реализаций UNIX.
Иногда непосредственный ввод-вывод неверно понимается в качестве средства достижения высокой производительности ввода-вывода. Но для большинства приложений использование непосредственного ввода-вывода может существенно снизить производительность. Дело в том, что ядро выполняет несколько оптимизаций для повышения производительности ввода-вывода за счет использования буферной кэш-памяти, включая последовательное упреждающее чтение данных, выполнение ввода-вывода в кластерах, состоящих из дисковых блоков, и позволение процессам, обращающимся к одному и тому же файлу, совместно задействовать буферы в кэш-памяти. Все эти виды оптимизации при использовании непосредственного ввода-вывода утрачиваются. Он предназначен только для приложений со специализированными требованиями к вводу-выводу, например для систем управления базами данных, выполняющих свое собственное кэширование и оптимизацию ввода-вывода, и которым не нужно, чтобы ядро тратило время центрального процессора и память на выполнение таких же задач.
Непосредственный ввод-вывод можно выполнять либо в отношении отдельно взятого файла, либо в отношении блочного устройства (например, диска). Для этого при открытии файла или устройства с помощью вызова open() указывается флаг O_DIRECT.
Флаг O_DIRECT работает, начиная с версии ядра 2.4.10. Использование этого флага поддерживается не всеми файловыми системами и версиями ядра Linux. Большинство базовых файловых систем поддерживают флаг O_DIRECT, но многие файловые системы, не относящиеся к UNIX (например, VFAT), — нет. Можно проверить поддержку этой возможности, протестировав выбранную файловую систему (если файловая система не поддерживает O_DIRECT, вызов open() даст сбой с выдачей ошибки EINVAL) или исследовав на этот предмет исходный код ядра.
Если один процесс открыл файл с флагом O_DIRECT, а другой — обычным образом (то есть с использованием буферной кэш-памяти), то согласованность между содержимым буферной кэш-памяти и данными, считанными или записанными через непосредственный ввод/вывод, отсутствует. Подобного развития событий следует избегать.
Сведения об устаревшем (ныне нерекомендуемом) методе получения необрабатываемого (raw) доступа к дисковому устройству можно найти на странице руководства raw(8).
Поскольку непосредственный ввод-вывод (как на дисковых устройствах, так и в отношении файлов) предполагает непосредственное обращение к диску, при выполнении ввода-вывода следует соблюдать некоторые ограничения.
• Переносимый буфер данных должен быть выровнен по границе памяти, кратной размеру блока.
• Смещение в файле или в устройстве, с которого начинаются переносимые данные, должно быть кратно размеру блока.
• Длина переносимых данных должна быть кратной размеру блока.
Несоблюдение любого из этих ограничений влечет за собой возникновение ошибки EINVAL. В показанном выше перечне под размером блока подразумевается размер физическего блока устройства (обычно это 512 байт).
При выполнении непосредственного ввода-вывода в Linux 2.4 накладывается больше ограничений, чем в Linux 2.6: выравнивание, длина и смещение должны быть кратны размеру логического блока используемой файловой системы. (Обычно размеры логических блоков в файловой системе равны 1024, 2048 или 4096 байт.)
В листинге 13.1 предоставляется простой пример использования O_DIRECT при открытии файла для чтения. Эта программа воспринимает до четырех аргументов командной строки, указывающих (в порядке следования) файл, из которого будут считываться данные, количество считываемых из файла байтов, смещение, к которому программа должна перейти, прежде чем начать считывание данных из файла, и выравнивание буфера данных, передаваемое read(). Последние два аргумента опциональны и по умолчанию настроены соответственно на значения нулевого смещения и 4096 байт.
Рассмотрим примеры того, что будет показано при запуске программы:
$ ./direct_read /test/x 512
Read 512 bytes
$ ./direct_read /test/x 256
ERROR [EINVAL Invalid argument] read