Ядро Linux в комментариях

       

Sys_reboot


Если и есть в ядре места, способные привести к паранойе, так вот sys_reboot— несомненный лидер в этом отношении. И причина, в общем-то, неплохая: как гласит имя функции, она должна использоваться для перезагрузки компьютера. Кроме того, в зависимости от передаваемых аргументов, эта функция может останавливать компьютер, выключать его питание и разрешать либо запрещать использование комбинации клавиш Ctrl+Alt+Del для перезагрузки. При написании кода, использующего sys_reboot следует соблюдать особую осторожность и принимать во внимание замечание, находящееся выше данной строки кода. Это замечание гласит, что перед обращением к sys_reboot следует обязательно синхронизировать жесткие диски, иначе данные, хранящиеся в дисковом кэше будут потеряны.

Принимая во внимание возможные неприятные последствия для системы, sys_reboot требует указания нескольких магических аргументов, которые будут рассматриваться немного позже.

В случае игнорирования характеристики CAP_SYS_BOOT (строка ) возвращается код ошибки EPERM. Характеристики более подробно обсуждаются в , а сейчас достаточно упомянуть, что с их помощью реализуется метод проверки допустимых привилегий для конкретного пользователя.

А здесь — паранойя во всей своей красе! sys_reboot сравнивает аргументы magic1 и magic2 с магическими числами, определенными в строках с по . Идея заключается в том, что если функция sys_reboot вызывается случайно, то весьма нежелательно так же случайно получить значения из аргументов magic1 и magic2. Следует заметить, что это не рассматривается как мера по увеличению степени безопасности, но только как защита от небрежного использования.

Между прочим, упоминаемые магические числа не выбирались случайным образом. Первое число относительно очевидно — этакий каламбур из «feel dead» (чувствовать себя мертвым). На следующие три числа для максимального эффекта лучше смотреть в шестнадцатиричном виде: 0x28121969, 0x5121996 и 0x16041998. Они выглядят как день рождения супруги Линуса (или, возможно, кого-то из его родственников) и дни рождения двух его дочерей. Соответственно, это наводит на мысль, что с увеличением семьи Линуса, вероятность случайного вызова функции перезагрузки будет стремиться к уменьшению. Однако, я думаю, что жена его остановит до того, как он исчерпает возможности 32-разрядного пространства.


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

В случае LINUX_REBOOT_CMD_RESTART функция sys_reboot обращается к списку функций в reboot_notifier_list, уведомляя их о том, что система перезагружается. В общем случае эти функции представляют собой часть модуля, для которого требуется выполнение определенных действий перед завершением работы системы. Кажется, что этот список не используется где-либо в ядре — по крайней мере, в рамках стандартного дистрибутива; возможно, его используют какие-то внешние модули. Во всяком случае, такой список существует и при необходимости им можно воспользоваться.

LINUX_REBOOT_CMD_RESTART и другие значения cmd определяются начиная со строки . Никакого скрытого смысла эти значения не несут; они установлены такими просто потому, что не должны возникать из-за всякого рода случайностей и не должны совпадать с другими значениями. (Самое интересное, что значение LINUX_REBOOT_CMD_CAD_OFF равно 0, а 0 относится как раз таким случайно возникающим значениям. Однако, поскольку LINUX_REBOOT_CMD_CAD_OFF просто запрещает использование комбинации клавиш Ctrl+Alt+Del для перезагрузки системы, случайное возникновение подобной ситуации считается «безопасным».)

После вывода предупредительного сообщения sys_reboot вызывает machine_restart (строка ) для перезапуска компьютера. Как можно заметить в строке , возврат из функции machine_restart никогда не происходит, тем не менее сразу за ее вызовом находится break.

Следует ли это рассматривать как классический стиль хорошего программирования? Пожалуй, да, и сейчас я кое-что добавлю. Файл kernel/sys.c относится к архитектурно-независимой части кода. Однако machine_restart, будучи явно архитектурно-зависимой, находится в архитектурно-зависимой части кода (файл arch/i386/kernel/process.c).



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

На такого рода платформах будет иметь место возврат из machine_restart, таким образом подобная возможность должна учитываться в коде.

По такому случаю в официальном дистрибутиве существует, по крайней мере, одна версия, допускающая возврат из machine_restart — версия для m68k. Код отличается для различных версий m68k-компьютеров. Поскольку книга ориентируется на х86-компьютеры, более подробный анализ всех особенностей упомянутого кода здесь не приводится.

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

Сейчас самое время немного прерваться. Оказывается, что чем бы ни считалось все рассмотренное — простой привычкой или паранойей — все оно необходимо для обеспечения переносимости ядра.

Следующие два случая разрешают или запрещают перезагрузку с использованием нечестной комбинации клавиш Ctrl+Alt+Del (известной в миру как «вулканический удар по нервам», «прах хакера», или, наиболее понравившаяся мне, «трехпальцевый привет»). Здесь просто выполняется установка состояния глобального флажка C_A_D, определенного в строке и проверяемого в строке .

Этот случай подобен случаю LINUX_REBOOT_CMD_RESTART, но он останавливает систему, а не перезагружает ее. Отличие состоит только в обращении не к machine_restart, а к machine_halt (строка ), которая не предпринимает никаких действий для платформы х86, однако выполняет ощутимую работу по завершению работы системы для других платформ. Кроме того, здесь имеется план перехода на аварийный режим для компьютеров, которые не останавливаются при помощи machine_halt — выполняется обращение к функции do_exit (строка ), уничтожающей само ядро.



Здесь можно заметить знакомый шаблон. sys_reboot обесточивает компьютер, что практически то же самое, что и останов системы, за исключением вызова machine_power_off (строка ) для тех систем, которые обладают возможностью программного отключения электропитания.

Случай LINUX_REBOOT_CMD_RESTART2 — это одна из вариаций на известную тему. Здесь принимается команда, передаваемая в ASCII-строке, которая выражает способ, по которому компьютер должен завершать работу. Эта строка интерпретируется не функцией sys_reboot, a machine_restart; следовательно, строка, если она вообще присутствует, имеет отношение к платформо-зависимому коду. (Стоит, однако, заметить, что обычно имеется только один способ перезагрузки компьютера, поэтому эта дополнительная информация функцией machine_restart игнорируется.)

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

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


Содержание раздела