netlib.narod.ru | < Назад | Оглавление | Далее > |
Функция parse_options выполняет разбор параметров, передаваемых во время выполнения загрузчиком ядра, воздействуя на некоторые из них самостоятельно, а другие передавая в процесс init. Параметры могут храниться в конфигурационном файле либо вводиться пользователем во время загрузки ядра.
19707: Параметры собираются в одну длинную командную строку, указатель на начало которой передается ядру. Загрузчик ядра сохраняет эту строку по известному адресу.
19718: Выбор следующего параметра с сохранением его для следующей итерации цикла. Следует отметить, что параметры должны отделяться друг от друга пробелами. Любой найденный разделительный пробел в следующей строке кода заменяется нулевым байтом, что позволяет рассматривать строку как нормальную строку в стиле С, которая хранит один параметр. Если не найдено ни одного пробела, это значит, что достигнут последний параметр в line и цикл завершается на следующей итерации.
Обратите внимание, что код не обеспечивает пропуск множества пробелов. Пусть line хранит следующее значение (содержащее 2 пробела):
rw debug
В результате распознавания выделяются три параметра: "rw", "" (пустая строка) и "debug". Поскольку пустая строка не является допустимым параметром ядра, она передается в init (как вскоре будет показано) — совершенно не то, что хотел пользователь! Следовательно, за удаление лишних пробелов отвечает загрузчик ядра. LILO делает это с завидной элегантностью.
19721: Попытка интерпретации параметра. Первых два параметра, ro и rw, уведомляют ядро о необходимости смонтировать корневую файловую систему, где находится каталог /, в режимах, соответственно, только для чтения и для чтения/записи.
19729: Третий параметр, debug, увеличивает объем отладочной информации, выводимой по вызову do_syslog (строка 25724).
19733: Первые несколько параметров представляют собой стандартные флаги и не имеют аргументов. Кроме них, ядро распознает параметры, задаваемые в виде параметр=значенне. Например, строка init=/some/other/program задает команду, которая будет выполняться загрузчиком ядра вместо init. Код отбрасывает часть init=, а оставшуюся часть сохраняет в execute_command для дальнейшего использования в init (строка 20044). В отличие от обработки остальных параметров, обработка данного параметра не может делаться полностью в функции checksetup (строка 19612), поскольку выполняется модификация ее локальных переменных. Ниже будет показано, по какой причине три рассмотренных выше параметра обрабатываются здесь, а не в checksetup.
19745: Изрядная часть параметров ядра распознается в функции checksetup. В случае удачной обработки параметра эта функция возвращает истину и цикл продолжается.
19750: В противном случае line не содержит допустимый параметр ядра. В такой ситуации содержимое line трактуется как параметр или переменная окружения для процесса init. Переменная окружения должна задаваться в виде переменная=значение. До тех пор пока имеется свободное место в массивах argv_init и envp_init (соответственно, строки 19057 и 19059), параметры и переменные окружения будут в них сохраняться для дальнейшей передачи в функцию init.
Комментарий в строке 19736 сообщает, что строка auto не является префиксом для какого-либо параметра, поэтому в большинстве случаев будет нормальным ее присутствие в массиве argv_init, так как auto — допустимый параметр для init. Однако, когда встречается параметр init=, он используется для запуска вместо init командного процессора и auto может его обескуражить. В этой связи parse_options игнорирует аргумент init.
Любопытно, что цикл завершается когда заполняется один из двух массивов argv_init и envp_init, поскольку заполнение argv_init не означает, что в line больше не осталось переменных окружения, предназначенных для init. Кроме того, там еще могут оставаться и необработанные параметры ядра. Это выглядит еще более странно, если учесть, что MAX_INIT_ARGS (строка 19029) и MAX_INIT_ENVS (строка 19030) определены со значениями 8 — очень маленький предел, который быстро подходит к концу. Если изменить операторы break в строках 19752 и 19756 на continue, цикл будет продолжать распознавать параметры без записи их в конец массивов argv_init и envp_init. Это может оказаться весьма полезным в случае, если command_line содержит опции ядра, не предназначенные для init.
19760: Обработаны все параметры ядра. Последний шаг заключается в добавлении символа NULL в конец массивов argv_init и envp_init, чтобы init имела возможность вовремя остановиться.
19612: Функция checksetup обеспечивает обработку большинства параметров ядра. Различают три категории параметров: использующие нормальные параметры ядра, разбираемые в части после знака =; разбираемые в части после знака = самостоятельно; разбираемые в части до и после знака =. На первую категорию ссылаются как на «обработанные» параметры, тогда как на вторую — как на «сырые». К последней категории относится только параметр IDE, имеющий отношение к драйверу IDE; этот случай проверяется первым в строке 19619.
19625: Далее checksetup сканирует массив raw_params (строка 19552) на предмет того, должен ли данный параметр оставаться необработанным. Элементы raw_params имеют тип struct kernel_param (строка 19223), ассоциирующий префикс параметра с функцией, которая вызывается, если такой параметр будет найден. Если атрибут str в каком-то элементе массива предваряется содержимым line, производится вызов соответствующей функции с передачей ей оставшейся части line и checksetup вернет ненулевое значение, которое говорит о том, что данный параметр обработан. Массив raw_params завершается двумя элементами NULL, поэтому цикл завершается также при встрече атрибута str, равного NULL. В таком случае цикл очевидно переходит в конец массива raw_params, не отыскав ни одного совпадения. Естественно, проверка атрибута setup_func функционирует в равной степени хорошо. Рассмотренный цикл иллюстрирует одно утверждение: в отличие от большей части ядра, инициализация не должна выполняться настолько быстро, насколько это возможно. Если даже ядро потратит на инициализацию на несколько микросекунд больше, чем планировалось, нет никаких оснований искать потери — пока еще никаких пользовательских приложений не запускалось, посему и терять-то нечего. В результате код выглядит необычно непроизводительным и требующим очевидной оптимизации. Например, длины строк в массиве raw_params можно было бы сохранить в том же массиве, а не вычислять их каждый раз (см. строку 19626). Для достижения еще больших результатов можно было отсортировать элементы массива raw_params в алфавитном порядке и предоставить возможность checksetup выполнять более быстрый двоичный поиск.
Нет ни малейших препятствий внести подобного рода усовершенствования в raw_params, однако, вряд ли игра будет стоить свеч, поскольку двоичный поиск дает существенную экономию только на больших массивах (точное значение признака «большой» зависит от конкретных обстоятельств). Коллега raw_params, массив cooked_params (строка 19228), определенно обладает большими возможностями для оптимизации, однако с ним связана одна проблема: сортировка по алфавиту может оказаться затруднительной, поскольку потребует разделения некоторых блоков #ifdef (см. строки с 19268 по 19272). Кроме того, из-за того, что алгоритм должен выполнять поиск по префиксам, он может оказаться чувствительным к порядку следования элементов. Все же упомянутые задачи не являются главенствующими и потенциальное возрастание производительности не стоит затраченного на это труда (в конце концов, дерево префиксов можно построить и статически). В данном случае предпочтение было отдано простоте.
Тем не менее, в аналогичном массиве root_dev_names (см. строку 19085), который отображает префиксы имен аппаратных устройств на их идентификаторы, разработчики несколько повысили производительность, поместив более часто используемые элементы (диски IDE и SCSI) перед менее часто используемыми (IDE-устройства чтения CD-ROM с интерфейсом подключения к порту принтера). Ничего подобного в случае raw_params и cooked_params не замечено.
Еще одно замечание. Уже сейчас можно предположить, почему параметры ro, rw и debug проверяются в parse_options, а не здесь — в parse_options выполняется полное сравнение, в то время как в checksetup проверяются только префиксы. В частности, параметр ro оказывается префиксом root= (строка 19553), а все должно работать корректно даже в случае присутствия в строке параметров и ro, и root=. Конечно, аргумент выглядит довольно-таки слабо. Посмотрите на параметр noinitrd (строка 19251). Он анализируется в cooked_params и, стало быть, подпадает под сравнение лишь префиксов; связанная с ним функция (no_initrd, строка 19902) игнорирует любые передаваемые ей параметры, т.е. ro, rw и debug, которые могут попасть в cooked_params.
19632: Этот цикл выполняет те же самые действия над массивом cooked_params, что и предыдущий цикл делал по отношению к raw_params. Единственное отличие между упомянутыми циклами (помимо массивов, конечно) состоит в том, что данный цикл имеет дело с частью line после знака =, вызывая get_options (строка 19062) перед обращением к функции установки. Функция get_options заполняет массив ints максимум десятью отрицательными числами. ints[0] хранит количество используемых элементов массива, т.е. сколько раз int get_options сохранялось в ints. Заполненный массив затем передается в функцию установки, которая соответствующим образом его интерпретирует.
19640: Возвращается 0, означающий, что параметр ядра в line не распознался.
19076: profile_setup является хорошим примером функции установки, вызываемой checksetup — она короткая, выполняет определенные действия над аргументом ints и у вас уже должно быть определенное понятие по поводу того, что она делает. Как упоминалось ранее, пользователь может устанавливать prof_shift во время загрузки. Сейчас рассмотрим, как это делается. profile_setup вызывается, если во время загрузки ядра указывается параметр profile=. Связь строки префикса и собственно функции выполняется в строке 19235. Следует заметить, что это находится в cooked_params, поэтому profile_setup получает обработанные параметры.
19079: Использует в качестве нового значения prof_shift первое число (если таковое присутствует), находящееся после profile=. Другие аргументы, которые могут указываться в параметре, полностью игнорируются.
19081: Если параметр profile= поступает без значений, prof_shift получает значение по умолчанию, равное 2. Такое значение по умолчанию может несколько обескуражить, поскольку, как уже должно быть известно, оно означает использование четверти доступной ядру памяти для профилирования — огромные накладные расходы. С другой стороны, такой объем позволит с высокой точностью определить узкие места, за исключением, быть может, нескольких операторов. В конечном счете, картина не столь страшна: ввиду того, что профилируется только код ядра, накладные расходы составят 25% от размера кода, а не от всей памяти ядра.
netlib.narod.ru | < Назад | Оглавление | Далее > |