8.9.2. Ошибки из-за отсутствия страниц

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

Малые ошибки

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

Большие ошибки

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

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

Отслеживание ошибок из-за отсутствия страниц

Можно отследить ошибки страниц для отдельных процессов с помощью команд ps, top и time. Следующая команда показывает простой пример того, как команда time отображает ошибки страниц. Результаты работы команды cal не имеют значения, поэтому мы их отключили, перенаправив в устройство /dev/null.

$ /usr/bin/time cal > /dev/null

0.00user 0.00system 0:00.06elapsed 0%CPU (0avgtext+0avgdata 3328maxresident)k

648inputs+0outputs (2major+254minor)pagefaults 0swaps

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

Если вы хотите увидеть ошибки для работающих процессов, воспользуйтесь командой top или ps. При запуске команды top используйте флаг f, чтобы изменить отображаемые поля, и флаг u, чтобы отобразить количество больших ошибок. Результаты будут показаны в новом столбце, nFLT. Количество малых ошибок вы не увидите.

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

$ ps -o pid,min_flt,maj_flt 20365

  PID  MINFL  MAJFL

20365 834182     23

Столбцы MINFL и MAJFL показывают число малых и больших ошибок из-за отсутствия страниц. Конечно, можно сочетать все это с любыми другими параметрами выбора процессов, как рассказано на странице руководства ps(1).

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

8.10. Отслеживание производительности процессора и памяти с помощью команды vmstat

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

Хитрость в овладении мощью команды vmstat состоит в понимании ее отчета. Вот, например, результаты работы команды vmstat 2, которая сообщает статистику каждые две секунды:

$ vmstat 2

procs —————-memory————— —-swap— ——-io—— -system— ——cpu——

r  b   swpd   free   buff    cache  si  so   bi   bo  in  cs us sy id wa

2  0 320416 3027696 198636 1072568   0   0    1    1   2   0 15  2 83  0

2  0 320416 3027288 198636 1072564   0   0    0 1182 407 636  1  0 99  0

1  0 320416 3026792 198640 1072572   0   0    0   58 281 537  1  0 99  0

0  0 320416 3024932 198648 1074924   0   0    0  308 318 541  0  0 99  1

0  0 320416 3024932 198648 1074968   0   0    0    0 208 416  0  0 99  0

0  0 320416 3026800 198648 1072616   0   0    0    0 207 389  0  0 100 0

Этот вывод распределяется по таким категориям: procs — для процессов, memory — для использования памяти, swap — для страниц, которые перемещаются в область подкачки и из нее, io — для использования диска, system — для количества переключений ядра на его код и cpu — для количества времени, затраченного различными частями системы.

Приведенный выше пример типичен для систем, которые не выполняют много работы. Обычно следует начинать просмотр со второй строки — в первой содержатся средние значения за все время работы системы. Например, в данном случае система переместила на диск (swpd) 320 416 Кбайт памяти, при этом свободно около 3 025 000 Кбайт (3 Гбайт) реальной памяти. Хотя некоторая часть области подкачки использована, нулевые значения в столбцах si (swap-in, «входящая» подкачка) и so (swap-out, «выходящая» подкачка) говорят о том, что в данный момент ядро не занято никаким из видов подкачки с диска. Столбец buff сообщает объем памяти, который ядро использует для дисковых буферов (см. подраздел 4.2.5).

В правом столбце с заголовком CPU можно увидеть распределение процессорного времени (столбцы us, sy, id и wa). Они сообщают соответственно процентное соотношение времени, которое процессор тратит на задачи пользователя, системные задачи (задачи ядра), бездействие и ожидание ввода/вывода. В приведенном примере запущено не так много пользовательских процессов (они используют не более 1 % процессорного времени); ядро не делает практически ничего, в то время как процессор находится в бездействии 99 % всего времени.

Теперь взгляните, что происходит, если через некоторое время запускается большая команда (первые две строки появились перед самым запуском программы) (пример 8.3).

Пример 8.3. Активность памяти

procs —————-memory————— —-swap— ——-io—— -system— ——cpu——

r b     swpd   free   buff     cache  si  so    bi   bo   in   cs us sy id wa

1 0  320412  2861252  198920 1106804   0   0     0    0 2477 4481 25 2 72 0

Внутреннее устройство Linux - _58.jpg

1 0  320412  2861748  198924 1105624   0   0     0   40 2206 3966 26 2 72 0

1 0  320412  2860508  199320 1106504   0   0   210   18 2201 3904 26 2 71 1

1 1  320412  2817860  199332 1146052   0   0 19912    0 2446 4223 26 3 63 8

2 2  320284  2791608  200612 1157752 202   0  4960  854 3371 5714 27 3 51 18

Внутреннее устройство Linux - _59.jpg

1 1  320252  2772076  201076 1166656  10   0  2142 1190 4188 7537 30 3 53 14

0 3  320244  2727632  202104 1175420  20   0  1890  216 4631 8706 36 4 46 14

Как следует из примера 8.3 (маркер

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