Linux Kernel (Ядро линукса) (часть 3)


Опережающее чтение файлов

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

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

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

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

Ядро считает обращение к файлу последовательным по отношению к предыдущему, если первая запрошенная страница непосредственно следует за последней запрошенной страницей в предыдущем обращении.

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

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

Опережающее окно состоит из страниц (следующих за страницами текущего окна), которые в настоящий момент читаются ядром впрок”. Ни одна из страниц в этом окне еще не запрошена процессом, но ядро предполагает, что рано или поздно они процессу потребуются.

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

Основная структура данных, используемая алгоритмом опережающего чтения, — это дескриптор file ra state, У каждого файлового объекта есть такой дескриптор в поле f ra.

Когда файл открывается, все поля соответствующего дескриптора filerastate Приравнены К нулю, за исключением полей prev_page и ra_pages.
В поле prev page хранится индекс последней страницы, запрошенной процессом в предыдущей операции чтения; изначально это поле содержит -1.
Поле ra pages представляет максимальный размер текущего окна в страницах, т. е. устанавливает предел опережающего чтения для данного файла. Значение по умолчанию для этого поля хранится в дескрипторе backing dev info блочного устройства, на котором хранится файл . Приложение может настроить алгоритм опережающего чтения для конкретного открытого файла, модифицируя поле ra pages. Для этого оно должно сделать системный вызов posix_fadvise , передав ему команды posix_fadv_normal (установить максимальный размер опережающего чтения по умолчанию, обычно 32 страницы), posix fadv sequential (установить максимальный размер опережающего чтения в два раза больше значения по умолчанию) или posix fadv random (установить максимальный размер опережающего чтения равным нулю, тем самым отключив опережающее чтение).

Поле flags содержит два флага, ra_flag_miss и ra_flag_incache, играющих важную роль в опережающем чтении. Первый флаг устанавливается, если страница, прочитанная заранее, не найдена в кэше (вероятнее всего, из-за утилизации ее ядром в целях освобождения памяти; см. главу 17). В этом случае размер следующего создаваемого опережающего окна будет несколько уменьшен. Второй флаг устанавливается, когда ядро выясняет, что последние 256 страниц, запрошенных процессом, все были найдены в кэше страниц (количество последовательных успешных обращений к кэшу хранится в поле ra->cache_hit). В этом случае опережающее чтение отключается, поскольку ядро предполагает, что все страницы, затребованные процессом, уже находятся в кэше.

Когда выполняется алгоритм опережающего чтения? В следующих случаях:
- когда ядре-обрабатывает запрос на чтение страниц из файла от процесса, работающего в режиме пользователя; это событие приводит к вызову функции page_cache_readahead ;
- когда ядро выделяет страницу для отображения файла в память (функция
filemap nopage далее в этой главе тоже вызывает функцию
page_cache_readahead);
- когда приложение, работающее в режиме пользователя, выполняет системный вызов readahead , который явным образом запускает опережающее чтение по дескриптору файла;
- когда приложение, работающее в режиме пользователя, выполняет системный ВЫЗОВ posix_fadvise С Командами POSIX_FADV_NOREUSE ИЛИ
posix fadv willneed, которые информируют ядро, что указанный диапазон страниц файла будет запрошен в ближайшем будущем;
- когда приложение, работающее в режиме пользователя, выполняет системный ВЫЗОВ madvi.se С КОМаНДОЙ MADV WILLNEED, КОТОрая информирует ядро, что указанный диапазон страниц в области отображения файла в память будет запрошен в ближайшем будущем

Предыдущая страница | 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 | Следующая страница




Возможно, Вас также заинтересует:

ОС Knoppix - это Linux без про...

ВведениеЕсли вы цените свое время, умеете считать деньги и знаете стоимость информации, то эта книга...

Linux Kernel (Ядро линукса) (ч...

Спин-блокировкаСпин-блокировка необходима в многопроцессорной системе, потому что могут возникнуть...

Linux Kernel (Ядро линукса) (ч...

Копирование при записи В системах Unix первых поколений создание процесса было реализовано довольно...

Linux Kernel (Ядро линукса) (ч...

Буферы блоков и головы буферовУ каждого буфера есть дескриптор голова буфера, имеющий тип buffer...