Библиотека сайта rus-linux.net
Руководство по "продвинутым" файловым системам, часть 6
Первоисточник : http://www-106.ibm.com/developerworks/library/l-fs6.html
Переход на devfs (с использованием init wrapper)
Daniel Robbins (drobbins@gentoo.org)
President/CEO, Gentoo Technologies, Inc.
October 2001
С выходом релиза 2.4 Linux появилась возможность использования filesystem с новыми свойствами, таких как Reiserfs, XFS, GFS и других. Эти filesystems еще не достаточно опробованы и имеются вопросы, что именно они могут делать, насколько они хороши и насколько оправдано их использование в промышленной Linux среде. В этой статье Daniel демонстрирует использование init wrapper и конвертацию системы на использование "devfs mode".
А все ли у вас подготовлено?
Эта статья завершает описание конвертации Linux под использование devfs или Device Filesystem. Для тех, кто только что начал чтение, будут полезны ссылки. В части 4 этой серии статей объяснялось, как devfs решает проблему регистрации устройств на уровне ядра. В части 5 я описал все шаги, необходимые для придания вашей Linux системе свойства devfs-совместимости (без этого переход на devfs невозможен).
Если вы не читали часть 5, очень важно сделать это теперь, прежде чем выполнить описанные ниже команды. Если не сделать предварительную подготовку, есть гарантия, что init wrapper, который будет инсталлирован, правильно работать не сможет. В таком случае вы останетесь с системой, которая не может загружаться и требует больших усилий для своей "реанимации". Однако если вы прочли и сделали все, о чем писалось в части 5, можно двигаться дальше.
The init wrapper
Первые строки.
Часть 5 этой серии статей завершилась описанием концепции init wrapper с объяснением, почему именно таким способом удобно решать проблему инициализации devfs. Без углубления в детали, все же по шагам "пройдемся" по полной версии init wrapper и "вникнем" в ее части. Начнем с вершины:
The init wrapper, top portion
#!/bin/bash
# Copyright 2001 Daniel Robbins <drobbins@gentoo.org>, Gentoo Technologies, Inc.
# Distributed under the GNU General Public License, version 2.0 or later.
trap ":" INT QUIT TSTP
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
umask 022
if [ $$ -ne 1 ]
then
exec /sbin/init.system $*
fi
Как можно заметить, init wrapper - "правильный" bash script
, что следует из "магической" первой строки
#!/bin/bash
. Самое время заметить, что этот init wrapper требует версию bash 2.0 или современней. Введите
/bin/bash --version
, чтобы узнать, какая версия инсталлирована на вашей системе. В системе может быть установлено
несколько версий интерпретатора. При этом может иметься отдельный исполняемый файл /bin/bash2
. В таком случае
скорректируйте первую строку сценария.
Теперь смотрим ниже. Команда trap
защищает сценарий от прерывания пользователем (например, комбинации CRTL-C в
процессе начальной загрузки), если его выполнение уже началось. Далее, экспортируется path по умолчанию и устанавливается (по
умолчанию) umask 022. Явное назначение umask для использования в процессе начальной загрузки не помешает, тем более что на
ранних ядрах серии 2.4 имелся дефект, приводивший к установке по умолчанию umask 0.
Ниже - первая условная инструкция, if [ $$ -ne 1 ]
. Интерпретатор разворачивает $$
в process ID
(текущего процесса). Инструкция читается как "отличается ли ИДЕНТИФИКАТОР текущего процесса от 1?" Что за этим скрывается?
Первый процесс, запущенный ядром при загрузке, всегда получает PID 1. Именно этот номер зарезервирован для процесса
init
. Если PID не равен 1, можно уверенно сказать, что сценарий запущен из командной строки уже после загрузки
системы. Заметим, второе вполне нормально, так как команда /sbin/init
имеет двойное назначение и при запуске
суперпользователем на загруженной машине используется для смены runlevel. Во втором случае сценарий просто запустит через
exec
оригинальный /sbin/init
, на нашей системе переименованный в /sbin/init.system
.
Используя переменную $*
можно любой из параметров, введенный в командной строке, передать программе init.system.
Kernel boot options
В случае, когда wrapper запускается ядром при начальной загрузке, сценарий получает PID равный 1. Первая условная
конструкция будет пропущена, а bash
продолжит выполнение wrapper. Просмотрим следующие строки:
More of the init wrapper
mount -n /proc
devfs="yes"
for copt in `cat /proc/cmdline`
do
if [ "${copt%=*}" = "wrapper" ]
then
parms=${copt##*=}
#parse wrapper option
if [ "${parms/nodevfs//}" != "${parms}" ]
then
devfs="no"
fi
fi
done
Если отрабатывается эта часть кода, значит, идет процесс начальной загрузки. Первое действие - монтирование /proc к файловой
системе root, которая все еще установлена в режиме read-only. После этого следует большой и сложный кусок bash
кода, который использует выгоды одной очень удобной особенности Linux. Вы могли этого и не знать, но ядро позволяет увидеть,
какие опции были ему переданы через LILO или GRUB по содержимому /proc/cmdline
. На моем development box содержимое
/proc/cmdline следующее:
Contents of /proc/cmdline
# cat /proc/cmdline
root=/dev/hda6 hda=89355,16,63 mem=524224K
Мы воспользуемся преимуществом существования /proc/cmdline
, сканируя этот файл для поиска опции начальной
загрузки с именем wrapper
. Если среди kernel boot options появится wrapper=nodevfs
, то сценарий не
станет делать enabled devfs. В случае если такая переменная отсутствует в /proc/cmdline
, то wrapper bash
script
продолжит devfs инициализацию. Мораль этой истории - вы можете легко отключать инициализацию devfs, передав ядру
в паузе LILO или GRUB параметр wrapper=nodevfs
. Если сделано так, сценарий переопределит значение переменной devfs
в no
, иначе - оставит yes
.
Wrapping как он есть.
Теперь оставшаяся часть wrapper:
Rest of the init wrapper
if [ "$devfs" = "yes" ]
then
if [ -e /dev/.devfsd ]
then
clear
echo
echo "The init wrapper has detected that /dev has been automatically mounted by"
echo "the kernel. This will prevent devfs from automatically saving and"
echo "restoring device permissions. While not optimal, your system will still"
echo "be able to boot, but any perm/ownership changes or creation of new compat."
echo "device nodes will not be persistent across reboots until you fix this"
echo "problem."
echo
echo "Fortunately, the fix for this problem is quite simple; all you need to"
echo "do is pass the \"devfs=nomount\" boot option to the kernel (via GRUB"
echo "or LILO) the next time you boot. Then /dev will not be auto-mounted."
echo "The next time you compile your kernel, be sure that you do not"
echo "enable the \"Automatically mount filesystem at boot\" devfs kernel"
echo "configuration option. Then the \"devfs=nomount\" hack will no longer be"
echo "needed."
echo
read -t 15 -p "(hit Enter to continue or wait 15 seconds...)"
else
mount -n /dev /dev-state -o bind
mount -n -t devfs none /dev
if [ -d /dev-state/compat ]
then
echo Copying devices from /dev-state/compat to /dev
cp -ax /dev-state/compat/* /dev
fi
fi
/sbin/devfsd /dev >/dev/null 2>&1;
fi
exec /sbin/init.system $*
Перед нами большая условная инструкция, которая выполняется только тогда, когда переменная devfs установлена в yes
.
Иначе, инициализация devfs пропускается полностью, а devfs не пытается монтироваться. В таком случае произойдет традиционная
non-devfs boot.
Однако если выбрана установка devfs, сценарий входит внутрь условного выражения. В этой инструкции выясняется, была ли devfs
уже смонтирована ядром. Делается это через простой "визуальный" контроль наличия символьного устройства /dev/.devfsd
.
При монтировании devfs ядром это устройство создается автоматически, а будущий devfsd процесс будет использовать его для связи с
ядром. Если devfs уже смонтирована (пользователь выбрал опцию "Automatically mount devfs at boot" перед компиляцией ядра),
происходит распечатка информационного сообщения. В сообщении говорится, что невозможно повторно установить features of devfs,
так как сделать это можно через wrapper только тогда, когда это еще не сделано самим ядром.
Device persistence
Если все OK, мы выполняем установку devfs, которая была описана в конце прошлой статьи. При этом /dev is bind-mounted к
/dev-state, а файловая система devfs монтируется к /dev. Теперь о том, что было пропущено в прошлой статье. Мы проверяем
существование каталога /dev-state/compat
и рекурсивно копируем его содержимое в /dev. Такая процедура может
показаться избыточной (мы ведь собираемся пользоваться преимуществами devfsd, не так ли?), но как выясняется необходимая и
полезная. Причина, по которой необходим каталог compat в том, что devfsd's persistence features работают только с
devfs-enabled drivers.
Что случиться, если в системе используется non-devfs kernel module? Казалось бы, достаточно создать device node в /dev
вручную. Проблема в том, что созданный вручную new device node будет проигнорирован devfsd
, а это означает, что
после очередной перезагрузки он исчезнет и его придется "пересоздавать". Решение проблемы в том, чтобы иметь каталог
/dev-state/compat
. Если имеется non-devfs module, просто создаете old-style device nodes в /dev-state/compat
,
и они будут добавлены к devfs при начальной загрузке. Все это благодаря "внимательности" нашего init wrapper.
Осталось запустить devfsd
и выйти из условного выражения. Как последний штрих, через exec
запускается реальный init
, или, как он теперь называется, /sbin/init.system
. Происходит стандартный
процесс загрузки системы. Ну, не совсем стандартный. Мы теперь имеем devfs-enabled system!
Инсталляция init wrapper.
Теперь приступим к инсталляции init wrapper. Сначала напишите исходный wrapper.sh и сохраните его где-нибудь в вашей системе. Сделайте следующее:
Installing the init wrapper
# cd /sbin
# cp init init.system
# cp /path/to/wrapper.sh init
# chmod +x init
Теперь init wrapper на месте.
Tweaking umount
Используя init wrapper, мы уходим от использования сложного компилированного initscript. Однако полностью избежать всех
проблем нам не удастся. Если не предпринять дополнительных мер, то rc scripts
будут очень много времени тратить на
размонтирование корневой файловой системы (после инсталляции devfs). Имеется простое решение проблемы. Профильтруйте через
grep
ваши rc scripts
на наличие команд umount
, например, cd /etc/rc.d; grep -r
umount *
, или cd /etc/init.d; grep -r umount *
(в зависимости, где инсталлированы ваши rc scripts). Далее,
в каждом сценарии, где имеется команда umount
, убедитесь, что она вызывается с ключом -r
.
Ключ -r
для umount
выполняет remount в режим read-only (при повторной попытке), если unmounting
завершился неудачей. Это достаточно для приведения корневой файловой системы в непротиворечивое состояние, после чего
перезагрузка произойдет без проблем. "Чистое" размонтирование может не произойти из-за существующего монтирования к /dev, а
/dev, в свою очередь, может оказаться не размонтированным, если открыт какой-либо device node.
Теперь перезагрузка практически подготовлена. Но, прежде, посмотрим на devfsd
и /etc/devfsd.conf
с позиции, чтобы compatibility devices и device persistence были enabled.
Не волнуйтесь, мы всего лишь в одном шаге от завершения перехода на devfs.
devfsd.conf
Загрузите /etc/devfsd.conf
в ваш любимый редактор. Посмотрите на первые четыре строки рекомендованного мною devfsd.conf:
devfsd.conf, top portion
REGISTER .* MKOLDCOMPAT
UNREGISTER .* RMOLDCOMPAT
REGISTER .* MKNEWCOMPAT
UNREGISTER .* RMNEWCOMPAT
Каждая из этих четырех строк состоит из event (REGISTER
или UNREGISTER
), регулярного
выражения (.*
) и action (строки *COMPAT
). Что они означают? Первая строка указывает devfsd исполнять
MKOLDCOMPAT
action, когда любое устройство регистрируется в ядре (регулярное выражение .*
match любому
устройству). MKOLDCOMPAT
action встроенная функция devfsd
и означает "создавать любые old compatibility
devices, соответствующие регулярному выражению, при его регистрации через devfs". Как несложно догадаться, RM*COMPAT
actions работают при device unregistration, заставляя эти special compatibility devices волшебно исчезать. В целом, эти четыре
строки указывают devfsd
создавать compatibility devices (if any) когда устройство регистрируется и удалять
compatibility devices при unregistered. Благодаря таким строкам, когда регистрируется драйвер IDE device как
/dev/ide/host0/bus0/target0/lun0/disc
(в devfs-style) самой системой, то devfs автоматически создает
соответствующее /dev/hda
compatibility-style device. Это очень полезно для таких команд, как mount
и
fsck
, которые могут читать /etc/fstab, содержащий имена old-style device. Вообще, создание compatibility devices
делает переход на devfs "бес проблемным". Следующая строка из моего devfsd.conf:
Module auto-loading
devfsd.conf, continued
LOOKUP .* MODLOAD
Эта запись сообщает devfsd
выполнять MODLOAD
action всякий раз, когда любое устройство (смотри на
регулярное выражение) "looked up". Такое случается, когда приложение ищет конкретное device node. MODLOAD
action
заставит выполнить команду modprobe /dev/mydev
(/dev/mydev - имя устройства, которое внешний процесс пытается
найти). Благодаря этой feature (в сочетании с правильно конфигурированным /etc/modules.conf), становится возможной автозагрузка
драйверов по требованию, например, звуковой карточки, и другие подобные вещи.
Device persistence
Продолжим разбор строк из devfsd.conf:
devfsd.conf, continued
REGISTER ^pt[sy]/.* IGNORE
CHANGE ^pt[sy]/.* IGNORE
REGISTER .* COPY /dev-state/$devname $devpath
CHANGE .* COPY $devpath /dev-state/$devname
CREATE .* COPY $devpath /dev-state/$devname
В этих нескольких строках предписывается devfsd
использование /dev-state как архива для любых device permission
или ownership изменений, а также любых new compatibility devices, которые пользователь может создать. В двух первых строках
явно сообщается devfsd
не выполнять специальных действий при регистрации или смены атрибутов для любых
pseudo-terminal devices. Если такие строки не использовать, то permissions и ownership таких pseudo-terminals сохранялись бы
после перезагрузок. Это не оптимально, так как мы всегда должны устанавливать новые значения perms для pseudo-terminal devices
после загрузки системы.
Следующие три строки включают /dev-state persistence для всех остальных устройств. Восстановятся любые атрибуты из
/dev-state, когда устройство регистрируется или стартует сам devfsd
(а также копирование атрибутов для любых
совместимых устройств). Кроме этого, будут немедленно копироваться любые изменения в атрибутах, а также любые записи для
только что созданных совместимых устройств к /dev-state.
CFUNCTION and symlinks
Заканчивается мой devfsd.conf следующими строками:
devfsd.conf, end
REGISTER ^cdrom/cdrom0$ CFUNCTION GLOBAL symlink cdroms/cdrom0 cdrom
UNREGISTER ^cdrom/cdrom0$ CFUNCTION GLOBAL unlink cdrom
REGISTER ^misc/psaux$ CFUNCTION GLOBAL symlink misc/psaux mouse
UNREGISTER ^misc/psaux$ CFUNCTION GLOBAL unlink mouse
Эти четыре строки необязательные, но они достойны упоминания. В то время как /dev-state persistence работает чудесно для
device nodes, к символическим ссылкам это не относится. Возникает вопрос: как обеспечить, чтобы символические ссылки, например
/dev/mouse или /dev/cdrom, не просто существовали, но и автоматически "воссоздавались" после перезагрузок? В конфигурациях
devfsd
такая возможность предусмотрена. Последние четыре строки (или подобные, все зависит от специфики
настраиваемой системы) решают проблему. Первые две указывают devfsd
создавать символическую ссылку /dev/cdrom,
когда регистрируется устройство /dev/cdrom/cdrom0. Реализовано это через dynamic call функций libc, которые в данном случае
специфицированы как symlink()
и unlink()
. Последние две строки конфигурационного файла используют
аналогичный подход для создания символических ссылок /dev/mouse, когда устройство /dev/misc/psaux (в этом примере PS/2 мышь)
регистрируется в devfs. "Подгоните" строки к вашей системе и сохраните конфигурационный файл devfsd.conf.
Предупреждение перед перезагрузкой.
Перед перезагрузкой можно посмотреть Richard Gooch's devfs FAQ. По этой ссылке можно найти информацию о devfs naming scheme, особенно полезную, когда вы только знакомитесь с именами устройств нового стиля (смотри Resources ниже). Я также рекомендую распечатать на принтере из части 5 порядок действий при "emergency bash rescue", если это для вас новость. Помните, если по каким то причинам init wrapper script создаст проблему, вы всегда можете удалить его следующей последовательностью спасательных команд. Загрузитесь "emergency bash rescue", выполните remounting корневой файловой системы в режим read-write и последовательно сделайте:
Откат к статусу pre-wrapper, если необходимо.
# cd /sbin
# mv init wrapper.sh
# mv init.system init
Выполнив эти команды, перемонтируйте файловую систему в режим read-only и перезагрузите систему. Система откатится в pre-wrapper state. Исправьте ошибки, снова перезагрузитесь и наслаждайтесь devfs!
Resources
- Читайте другие статьи этой серии от Daniel Robbins:
- Презентация ReiserFS. (Часть 1)
- Использование ReiserFS в Linux 2.4. (Часть 2)
- Презентация tmpfs и bind mounts. (Часть 3)
- Презентация devfs. (Часть 4)
- Установка devfs. (Часть 5)
- Переход на devfs (с использованием init wrapper). (Часть 6)
- Презентация ext3. (Часть 7)
- Неожиданности от ext3. (Часть 8)
- Презентация XFS. (Часть 9)
- Развертывание XFS. (Часть 10)
- Совершенствование файловых систем. (Часть 11)
- Download the most recent version of the devfsd tarball, currently version 1.3.16.
- O'Reilly's Linux Device Drivers, 2nd Edition is an excellent book and a great resource for learning more about device registration, and Linux device driver programming in general.
- Be sure to read the Linux Devfs FAQ by Richard Gooch, the creator of Linux devfs. You may find the information about the devfs naming scheme particularly helpful. You may also want to visit Richard Gooch's main page; it contains devfs as well as other neat things.
- Subscribe to the devfs mailing list by sending an e-mail to majordomo@oss.sgi.com with the word subscribe in the body of the message. View the devfs list archives.
- Find out more about GRUB at the GNU GRUB project page. Even better, check out Daniel's developerWorks tutorial on installing and using GRUB.
- Are you a LILO user? It's OK; we still love you. Grab the most recent version of LILO.
- Linux Weekly News is a great resource for keeping up with the latest kernel developments.
- Browse more Linux resources on developerWorks.
- Browse more Open source resources on developerWorks.
About the author
Residing in Albuquerque, New Mexico, Daniel Robbins is the President/CEO of Gentoo Technologies, Inc., and the creator of Gentoo Linux, an advanced Linux for the PC, and the Portage system, a next-generation ports system for Linux. He has also served as a contributing author for the Macmillan books Caldera OpenLinux Unleashed, SuSE Linux Unleashed, and Samba Unleashed. Daniel has been involved with computers in some fashion since the second grade, when he was first exposed to the Logo programming language as well as a potentially dangerous dose of Pac Man. This probably explains why he has since served as a Lead Graphic Artist at SONY Electronic Publishing/Psygnosis. Daniel enjoys spending time with his wife, Mary, and his new baby daughter, Hadassah. You can contact Daniel at drobbins@gentoo.org.Перевод: Владимир Холманов