Прямой ввод/вывод
Как мы уже знаем, в Linux 2.6 нет существенной разницы между обращением к обычному файлу через файловую систему, обращением к нему посредством блоков, доступных через соответствующий файл блочного устройства и даже обращением к файлу с использованием его отображения в память. Однако существуют чрезвычайно сложные программы (так называемые приложения с самокэшированием), которые стремятся иметь полный контроль над всем механизмом ввода/вывода данных. Приведем в качестве примера высокопроизводительные серверы баз данных. В большинстве из них реализованы собственные механизмы кэширования, которые используют особые свойства запросов к базам данных. Подобным программам кэш страниц, предоставляемый ядром, мало поможет. Наоборот, он будет вреден по следующим причинам:
- большое количество страничных кадров будет непроизводительно дублировать данные, уже находящиеся в оперативной памяти (в кэше памяти, созданном пользователем);
- работа системных вызовов read о и write о будет замедляться из-за избыточных инструкций, обрабатывающих кэш страниц и выполняющих опережающее чтение; то же самое справедливо в отношении страничных операций, связанных с отображением файлов в память;
- вместо того чтобы пересылать данные непосредственно между диском и пользовательской памятью, системные вызовы read () и write о делают две пересылки: между диском и буфером ядра и между буфером ядра и пользовательской памятью.
Поскольку аппаратные блочные устройства должны управляться с помощью прерываний и механизма DMA, а сделать это можно только в режиме ядра, приложениям с самокэшированием определенно требуется некоторая поддержка со стороны ядра.
Linux предлагает разработчикам простой способ обхода кэша страниц — прямой ввод/вывод. При прямом вводе/выводе ядро программирует контроллер диска так, чтобы он пересылал данные непосредственно на страницы (или от них), принадлежащие адресному пространству режима пользователя, которое выделено приложению с самокэшированием.
Как мы знаем, каждая пересылка данных протекает асинхронно. Когда она выполняется, ядро может переключиться на другой процесс; центральный процессор может возвратиться в режим пользователя; страницы процесса, запустившего пересылку данных, могут быть выгружены и т. д. Все работает хорошо при обычном вводе/выводе данных, поскольку в нем задействованы страницы кэшей диска. Кэшами диска владеет ядро, они не могут быть выгружены, и они видны всем процессам, работающим в режиме ядра.
Зато при прямом вводе/выводе данные должны перемещаться в пределах страниц, принадлежащих адресному пространству режима пользователя, которое представлено процессу. Ядро должно позаботиться о том, чтобы эти страницы были доступны каждому процессу, работающему в режиме ядра, и что они не будут выгружены, пока выполняется пересылка данных. Посмотрим, как это достигается.
Когда приложение с самокэшированием хочет обратиться к файлу напрямую, оно открывает его с флагом o direct При обслуживании системного вызова open о функция dentry open проверяет, реализован ли метод direct io для объекта address space открываемого файла и возвращает код ошибки, если метода нет. Флаг o direct может быть также установлен для файла, уже открытого с использованием команды f setfl системного вызова fcnti .
Вначале рассмотрим случай, когда приложение с самокэшированием делает системный вызов read с флагом o direct. Как было сказано в разд. "Чтение из файла" ранее в этой главе, метод read для файла обычно реализуется функцией generic file readO, КОТОраЯ инициализирует Дескрипторы iovec И
kiocb И вызывает функцию generic_file_aio_read. Последняя проверяет корректность буфера режима пользователя, описываемого дескриптором iovec, затем проверяет, установлен ли флаг o direct для файла. Вызванная системным вызовом reado, эта функция выполняет фрагмент кода, эквивалентный следующему:
if (filp->f_flags & 0_DIRECT) {
if (count == 0 || ppos > filp->f_mapping->host->i_size) return 0;
retval = generic_file_direct_IO(READ, iocb, iov, ppos, 1); if (retval > 0)
ppos += retval; file_accessed(filp); return retval;
}
Функция проверяет текущее положение файлового указателя, размер файла и количество запрошенных символов, а затем вызывает функцию generic_file_direct_IO, Передавая ей ТИП Операции READ, Дескриптор iocb, дескриптор iovec, текущее значение файлового указателя и количество буферов режима пользователя, заданных в дескрипторе io vec (один). Когда функция generic_file_direct_IOЗавершает работу, функция generic_
fiie_aio_read обновляет файловый указатель, ставит отметку времени на индексном дескрипторе файла и возвращает управление.
Аналогичные действия происходят, когда системный вызов write о делается для файла с установленным флагом o direct. Как было сказано в разд. "Запись в файл” ранее в этой главе, метод write файла, в конечном счете, вызывает функцию generic fiie aio write noiock. Она проверяет, установлен ли флаг ODIRECT, И, если установлен, вызывает функцию generic_file_ direct io (), задавая write в качестве типа операции.
Функция generic fiie direct ioo принимает следующие параметры:
- rw — тип операции, read или write;
- iocb — указатель на дескриптор kiocb
- iov — указатель на массив дескрипторов iovec;
- offset — смещение внутри файла;
- nr segs — количество дескрипторов iovec в массиве iov.
Функция generic fiie direct ioo ВЫПОЛНЯеТ Следующие ДеЙСТВИЯ:
1. Получает адрес file файлового объекта из поля ki fiip дескриптора kiocb И адрес mapping объекта address_space ИЗ ПОЛЯ f ile->f_mapping.
2. Если задан тип операции write, и один или несколько процессов создали отображение в память некоторой порции этого файла, функция вызывает unmap mapping range , чтобы отменить отображение всех страниц файла.
Эта функция также гарантирует, что если какая-нибудь запись в Таблице Страниц, у которой отменяется отображение, имеет установленный бит Dirty, то эта страница будет помечена как грязная в кэше страниц.
3. Если индексное дерево С корнем В поле mapping не пусто (mapping->nrpages больше нуля), функция вызывает функции filemap_fdatawrite() и fiiemap fdatawaitо, чтобы принудительно записать на диск все грязные страницы и подождать, пока операции записи не будут завершены Даже если приложение с самокэшированием обращается к файлу напрямую, в системе могут быть другие приложения, обращающиеся к нему через кэш страниц. Чтобы избежать потери данных, образ диска синхронизируется с кэшем страниц до начала операции прямого ввода/вывода.
4. Вызывает метод direct io адресного пространства mapping.
5. Если задан тип операции write, функция вызывает функцию invaiidate_inode_pages2о, чтобы перебрать все страницы в индексном дереве адресного пространства mapping и освободить их. Функция также очищает записи в Таблице Страниц режима пользователя, которые ссылаются на эти страницы.
В большинстве случаев метод direct io является интерфейсом к функции
biockdev direct io . Эта функция очень сложна и использует большое
количество структур и функций. Впрочем, она выполняет операции, уже описанные в этой главе: разбирает читаемые или записываемые данные на подходящие блоки, находит данные на диске и заполняет один или несколько дескрипторов bio, описывающих операции ввода/вывода, которые необходимо выполнить. Конечно, данные будут прочитаны или записаны прямо в буферы режима пользователя, заданные дескрипторами iovec из массива iov. Дескрипторы bio передаются общему слою работы с блочными устройствами, для чего вызывается функция submit bioo Как правило, функция biockdev direct ioo не возвращает управление, пока все операции прямого ввода/вывода не будут завершены. Следовательно, если системный вызов read или write возвратил управление, приложение с самокэшированием может безбоязненно обращаться к буферам, содержащим данные из файла.
Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | Следующая страница