FreeBSD for Embedded Systems, раздел 2 - технология "OpenSpecial" - портирование FreeBSD 4.3 до уровня Embedded OS мягкого реального времени


Введение

Здесь изложен материал по построению embedded -   версии операционной системы FreeBSD 4.3. Основой для данной технологией послужило описание компактной версии под названием PicoBSD. PicoBSD входит в состав дистрибутива ОС FreeBSD, исходные тексты для построения компактной версии, размещаемой на флоппи, находятся в /usr/src/release/picobsd. Информация о системе расположена по адресу people.freebsd.org/~picobsd. В настоящее время этот проект не поддерживается.

На странице www.freebsd.org есть информация по построению системы через скрипт picobsd. Однако, на мой взгляд, он является достаточно сложным, содержит ошибки (по крайней мере содержал в версии 4.3) и к тому же предназначен для размещения на флоппи; здесь изложена процедура построения ОС на Disk-On-Chip поэтапно и с подробным комментарием.


Особенности аппаратной платформы

В качестве аппаратной платформы для нашего проекта выбран клон Intel - CPU686, форм - фактор MicroPC, шина ISA. Выбор был обусловлен тем, что компоненты под байтовую ISA существенно дешевле. Практически единственный недостаток такой шины - низкая реальная скорость передачи данных (около 1МБ в секунду). Правда, чипсет следующих версий CPU686 позволяет через BIOS увеличить эту скорость. Но эта опция в нашем проекте еще не тестировалась. В настоящее время планируется перенести технологию OpenSpecial на Intel - совместимые модули AmPro со стековой PCI шиной.

Аппаратура содержит следующие компоненты:

1) Плата процессора CPU686, Fastwel (cpu686.pdf):

2) Модуль реального времени своей разработки, обеспечивающий понижение скорости данных;

3) Мультипортовая ISA плата 5554, Octagon Systems (5554.txt), состоящая из четырех последовательных интерфейсов RS-232/RS-422/RS-485. К портам RS-422/RS-485 подключаются внешние устройства, к порту RS-232 - модем для удаленного обмена.

4) Программируемый модуль ввода - вывода UNIO48-5, Fastwel (UNIOXX-5.pdf), содержащий ПЛИС, запрограммированные как параллельные порты. Модуль поддерживает 48 каналов цифрового ввода - вывода. К этой ISA плате подключаются модуль РВ, цифровая клавиатура и символьный дисплей (консоль). Консоль используется для локального управления системой.

5) Объединительная пассивная байтовая ISA шина 5278 совместно с источником электропитания компании Octagon Systems. В эту ISA шину врубаются все вышеперечисленные платы.

Замечу, что перечисленное оборудование нашего проекта работает в климатическом диапазоне от минус 40 до +50С. Более подробную информацию по оборудованию можно найти по адресу ftp://ftp.prosoft.ru/pub/Hardware.

Особенности проекта:

Как отмечалось еще в Разделе 1, из особенностей накопителей DOC следует:

Уменьшение объема софта достигается совместной компоновкой программ в сжатом ядре с помощью утилиты crunchgen, а также минимально необходимой структурой каталогов. Свопинг исключается заданием соответствующих опций компиляции ядра, условие read-only - размещением файловой системы в памяти - Memory File System (MFS). Эти факторы являются ключевыми в технологии OpenSpecial.


Форматирование Disk-On-Chip

Итак, для построения FreeBSD Embedded подключаем к нашей плате процессора жесткий диск с предустановленной FreeBSD 4.3, клавиатуру и дисплей. Первым делом нужно перекомпилировать ядро для включения поддержки DOC. В предположении, что файл конфигурации - GENERIC, копируем его в PICOBSD и в файле PICOBSD добавляем строку

device    fla0 at isa?

После этого перекомпилируем ядро и перезагружаемся. (Драйвер fla компании M-Systems, вернее его главная часть - объектный модуль msysosak.o.uu входит в дистрибутив FreeBSD). После перезагрузки даем команду

dmesg | grep "fla"

и смотрим сообщения драйвера fla. Если Disk-On-Chip успешно обнаружен, драйвер рапортует о структуре устройства, аналогичной  структуре диска. Запомним эту  информацию, она будет нужна для разметки DOC.

Далее, нужно подготовить протофайл, описывающий  структуру DOC, для программы disklabel. Проще всего это сделать, взяв за основу протофайл жесткого диска, с которого мы загружались. Если загрузка проходила с /dev/ad0s1, переходим в свой рабочий каталог и даем команду

disklabel -r ad0s1 > label.fla8

Корректируем созданный файл label.fla8, изменяя значения соответствующих параметров диска на выданные драйвером. После внесенных изменений файл приобретает примерно следующий вид: label.fla8. Заметьте, что сделана корректировка разделов (partitions): отсутствует раздел свопинга, создан основной раздел fla0a и дополнительный раздел fla0e, который мы будем использовать для сохранения пользовательских данных (раздел fla0a read-only!). Зачем для пользовательских данных создавать отдельных раздел, когда можно смонтировать fla0a для записи и писАть туда? Если во время записи произойдет сбой (отключено электропитание), то файловая система, содержащая нашу ОС, будет испорчена. По этой причине запись производится в дополнительный раздел. Если после сбоя его содержимое становится некорректным, раздел можно просто восстановить командой newfs. Естественно, все пользовательские данные будут утеряны, но работоспособность системы сохранится.

Теперь, после того как создан протофайл label.fla8, на DOC можно создать метку диска и загрузочные записи. Этот процесс выполняется стандартным образом, за следующим исключением: второй загрузчик - boot2, располагаемый в /boot/boot2, во время старта вызывает /boot/loader, который нам не нужен по соображением компактности. Из загрузчика boot2 необходимо непосредственно запускать kernel. (Для справки по этапам загрузки смотри man boot). Последовательность команд, модифицирующая второй загрузчик и меняющая ссылку с loader на kernel:

cp /boot/boot2 ~
perl -pne 's/\/boot\/loader/\/kernel\0\0\0\0\0/' boot2 > b2

Новый загрузчик-2 находится в b2. Теперь все готово для записи метки на Disk-On-Chip. Запись выполняется командой

disklabel -BRw -b /boot/boot1 -s b2 label.fla8

Ключи программы disklabel означают следующее: -B - создать загрузочную запись, ключ -b ссылается на файл первого загрузчика, -s - на файл второго; -w - запись метки на диск; - R - для создания метки использовать информацию из протофайла. Создаем файловые системы в основном и дополнительном разделе (с оптимизацией по свободному пространству):

newfs -i 32768 -m 0 -p 0 -o space /dev/fla0a
newfs -i 32768 -m 0 -p 0 -o space /dev/fla0e

Электронный диск готов. Можно проверить, все ли мы сделали правильно:

disklabel -r fla0

На консоль выводятся данные, аналогичные содержащимся в label.fla8, только вместо звездочек мы видим вычисленные данные по размером разделов.

mount /dev/fla0a /mnt
cp /dev/null /mnt/file.txt
ls /mnt/file.txt
rm /mnt/file.txt
umount /mnt

Монтирование файловой системы, создание и удаление тестового файла file.txt должны пройти без ошибок.


Создание Memory File  System - MFS

Из ничего создаем файл mfs.bin - образ MFS:

dd if=/dev/zero of=mfs.bin count=6000 bs=1k
dd if=/boot/boot1 of=mfs.bin conv=notrunc

Размер файла mfs.bin, или размер создаваемой файловой системы в нашем случае - 6000К. В MFS должны поместиться дерево каталогов и системные программы, упакованные утилитой crunchgen. ВАЖНО: размер MFS должен соответствовать значению опции MD_ROOT_SIZE в файле конфигурации ядра (в нашем случае файл PICOBSD, см. ниже), поскольку MFS записывается непосредственно в ядро. Каким должен быть размер MFS? Первое ограничение - размер kernel, равный размеру собственно ядра (сжатого) без MFS плюс размер MFS - должно быть меньше размера файловой системы fla0a. Следует также учесть, что нужно обеспечить дополнительное пространство для пользовательских программ, динамически компонуемых библиотек, необходимых для работы, и собственных драйверов. Второе ограничение - размер оперативной памяти. Распакованное ядро и MFS располагаются в ОЗУ, где должно остаться место для пользовательских процессов (программ и динамически выделяемой памяти - и не забываем про отсутствие свопинга, поэтому у PicoBSD не должно возникнуть желания выгрузить что - нибудь в отсутствующий файл подкачки :-)). В проекте DF складывается следующая арифметика: сжатое ядро ~ 1,5МБ, плюс MFS ~ 6МБ, итого свободное место на fla0a ~??? МБ. После старта системы, команда df рапортует, что около 70% файловой системы MFS свободно (ОЗУ размером 32МБ).

Создаем виртуальный диск /dev/vn0 на основе образа MFS,  записываем метку диска, создаем файловую систему и монтируем ее:

vnconfig -c -s labels /dev/vn0 mfs.bin
disklabel -rw vn0 auto
newfs -i 32768 -m 0 -p 0 -o space /dev/rvn0c
mount /dev/vn0c /mnt

В смонтированной директории /mnt создаем структуру каталогов MFS. Структура создается программой mtree на основе файла mfs.mtree:

mtree -deU -f mfs.mtree -p /mnt

Модификацией файла mfs.mtree можно задать свою структуру каталогов. На следующем этапе, нижеприведенным скриптом создаются ссылки "стандартных" каталогов, в которых обычно размещаются системные утилиты, на каталог /stand. Это означает, что эти утилиты, размещаемые в каталогах /bin, /sbin, /usr/bin, /usr/sbin и им подобным, фактически будут находиться в каталоге /stand. В каталоге /dev создаются файлы устройств, которые нам понадобятся:

#!/bin/sh
STAND_LINKS="bin sbin usr/bin usr/sbin usr/libexec usr/local/bin"
MY_DEVS="std cuaa0 cuaa1 cuaa2 vty10 fd0 ad0 ad1 pty0 fla0"
for i in ${STAND_LINKS}; do
ln -s /stand /mnt/$i
done
ln -s /dev/null /mnt/var/run/log
ln -s /etc/termcap /mnt/usr/share/misc/termcap
(cd /mnt/dev ; ln -s /dev/MAKEDEV ;
./MAKEDEV ${MY_DEVS}; rm MAKEDEV)

Здесь еще раз подчеркиваю, что все создаваемые в /mnt ссылки и файлы фактически будут храниться в образе MFS - файле mfs.bin.


Упаковка системных утилит - crunchgen

Далее, с помощью утилиты crunchgen упаковываем основные системные программы в одну программу - crunch. Упаковка достигается за счет совместного использования стандартных библиотек типа libc, которые обычно компонуются статически с каждой системной утилитой. Вызов системных программ обеспечивается механизмом жестких ссылок на crunch, которая получает имя вызываемой программы из командной строки. Программа crunch опознает имя запукскаемой утилиты через argv[0]. Пример: в crunch упакована программа mount, mount - жесткая ссылка на crunch (имена всех системных утилит - жесткие ссылки на crunch). Тогда после запуска из командной строки mount /cdrom выполняется crunch с аргументом argv[0] = "mount /cdrom", и выполняется подпрограмма mount. Утилита crunchgen использует конфигурационный файл crunch.conf, в котором указываются включаемые программы, каталоги расположения их исходных текстов, ссылки и другая информация. Я дополнил этот список несколькими утилитами, которые были нужны в проекте DF. Корректировки исходного файла crunch.conf обозначены двойным комментарием - ##. Из стандартного файла конфигурации я исключил natd, routed, ipfw и все, что связано с PPP (поддержка PPP - в ядре, через pppd), и добавил клиента и сервер для FTP - ftp и ftpd, утилиты mv, tar, grep, поддержку PPP через pppd, редактор ee и утилиту ldconfig, которая задает путь для динамически компонуемых библиотек (используется во время старта системы). Заметьте, что ссылки на системные утилиты gzip, ps, dmesg, netstat указывают на программы minigzip, sps, msg, ns. Это - компактные версии соответствующих стандартных программ, расположенные в каталоге /usr/src/release/picobsd/tinyware.

Выполняем команду crunchgen и получаем make - файл crunch.mk:

crunchgen -p /usr/obf-pico -o "" -m crunch.mk crunch.conf

Результат работы программы  crunchgen - файл crunch.mk содержит директивы для программы make для сборки каждой из программ, указанной в crunch.conf.

Выполняем make - сборку для компиляции всех программ, указанных в  crunch.conf, в модуль crunch:

make -s -f  crunch.mk

Уменьшаем размер модуля командой strip и записываем его в дерево MFS:

strip --remove-section=.note --remove-section=.comment crunch
mv crunch /mnt/stand/crunch
chmod 555 /mnt/stand/crunch

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

Следующий скрипт создает жесткие ссылки для всех системных программ, входящих в модуль:

#!/bin/sh
for i in `crunchgen -l crunch.conf` ; do
ln /mnt/stand/crunch /mnt/stand/${i};
done

Если набор системных программ, указанных в crunch.conf, содержит sshd, необходимо дополнительно сгенерировать ключ:

ssh-keygen -f /mnt/etc/ssh_host_key -N "" -C "root@picobsd"

В заключение, в каталог /etc необходимо записать стартовый сценарий MFS rc.mfs. Этот сценарий не является "классическим" /etc/rc, его задача - смонтировать файловую систему fla и скопировать с нее конфигурационные файлы /etc/* в MFS, включая облегченную версию "классического" сценария /etc/rc.

cp rc.mfs /mnt/etc/rc

Устанавливаем права доступа, демонтируем /mnt, сохраняем образ MFS в mfs.bin заключительной командой vnconfig -u:

(cd /mnt; chown -R root .)
df -ik /mnt
umount /mnt
fsck -p /dev/rvn0c
vnconfig -u vn0

В результате, в файле - образе MFS mfs.bin содержится файловая система со структурой каталогов, включающая основные системные программы. Не хватает только ядра :-)


Компоновка kernel

Ядро компонуется на основе сокращенного файла конфигурации PICOBSD:

make -v -f Makefile.conf

Обращаю внимание, что ядро скомпоновано с опцией MD_ROOT - файловая система будет размещена в памяти (MFS). Опция MD_ROOT_SIZE=6000 соответствует размеру MFS. Исключены опции USERCONFIG, INTRO_USERCONFIG, VISUAL_USERCONFIG для безостановочного старта системы. Опция PCI_QUIET указывает осутствие устройств на PCI. Включена поддержка мультипортовых карт Octagon 5554 опцией COM_MULTIPORT. В файле конфигурации присутствует драйвер fla и наш драйвер unio платы Fastwel UNIO48-5 (Написание драйвера - ниже, в следующей теме).

Уменьшаем полученное ядро, насколько это возможно:

cp -p /usr/src/sys/compile/PICOBSD/kernel ~/kernel
strip kernel
strip --remove-section=.note --remove-section=.comment kernel

Записываем созданную MFS, хранимую в mfs.bin,  в ядро с помощью утилиты wmk (write_mfs_in_kernel.c):

cc -o wmk write_mfs_in_kernel.c
./wmk kernel mfs.bin
rm wmk

Программа write_mfs_in_kernel.c обеспечивает вставку образа MFS в ядро.

Сжимаем ядро и записываем его на Disk-On-Chip:

kgzip -o kernel.gz kernel
mount /dev/fla0a /mnt
cp -p kernel.gz /mnt/kernel
umount /mnt

Осталось наполнить каталог /etc, и компактная версия PicoBSD готова. ОС грузится с DOC и размещается в памяти.


Размещение пользовательских программ и системных библиотек

Каталоги диска fla0a имеют следующую структуру:

/
|- etc/
|      |- ppp/
|- usr/
|      |- sbin/
|      |- lib/
|      |- local/
|               |- bin/
|               |- lib/
|               |- etc/
|                       |- rc.d/

В корне находятся kernel, kernel.config, boot.config. В /etc находится все, что копируется сценарием rc.mfs в каталог /etc MFS. Содержимое каталога /usr один к одному переносится в /usr MFS сценарием rc.mfs. Состав каталогов:


Сценарий старта системы

Во время загрузки, BIOS с поддержкой Disk-On-Chip находит электронный диск и запускает boot1. Boot1 вызывает boot2, который в свою очередь запускает /kernel. Появляется сообщение

Uncompressing kernel... done,

После чего распакованное ядро с MFS располагается в памяти и начинает обычный старт. После инициализации всех драйверов выполняется сценарий MFS rc.mfs. В нем устанавливаются переменные окружения, стандартные пути поиска. Далее, монтируется раздел fla0a и каталоги /etc, /usr вместе с содержимым переносятся в MFS. Монтируется раздел fla0e, и конфигурационные файлы проекта DF переписываются в каталог /usr/local/etc/df. После этого файловые системы демонтируются. Динамический компоновщик ld-elf.so.1 из каталога /usr/lib переносится в свой "родной" каталог /usr/libexec, а фактически - в /stand, поскольку все каталоги для системных исполняемых программ есть просто ссылки на /stand. Командой pwd_mkdb создаются базы данных паролей (из файла master.passwd) и запускается на выполнение сценарий /etc/rc.

Сценарий /etc/rc конфигурится переменными из /etc/rc.conf. Но, перед его выполнением, запускается наш сценарий rc.df, который файтически является продолжением rc.mfs (размер последнего ограничен значением 1024 байт). В этом сценарии в каталоге /dev создаются ссылки на наши устройства, о которых не знает скрипт MAKEDEV, и в каталоге /var/log создаются файлы, необходимые для работы демона протоколирования syslogd (который запускается из /etc/rc). В конце выполняется наш пользовательский сценарий /usr/local/etc/rc.d/df.sh, который конфигурит порты, запускает pppd и наши демоны. Система готова к работе, на консоль выводится приглашение для входа.


BUGS

1. Можно не использовать ldconfig, а сформировать заранее файл ldconfig.hints.
2. Редактор ee лучше вынести из crunch и сделать как динамически компонуемое приложение.
3. На fla0a файлы kernel.config, boot.config ничего не делают. Попытки конфигурить драйверы через них ничего не дали.
4. Со временем /var/log переполняется. Необходимо использовать newsyslog или сделать свое приложение для архивации логов на fla0e.
5. Не реализована поддержка сторожевого таймера CPU686.
6. Создание символьных ссылок в /dev лучше осуществлять программой tar.


 

© Назим Алиев, alnaz@mail.ru, infoATnazim.ru, обновлено 21.10.2003г.

Почта: infoATnazim.ru
На основную страницу: WWW.NAZIM.RU
В начало раздела: NAZIM.RU/BSD