Мелочи жизни


16.01.1999

Решив открыть в нашем журнале постоянную рубрику, посвященную деталям функционирования ОС Unix и Unix-подобных систем, мы предложили вести ее нашему давнему знакомому Игорю Облакову. Игорь имеет огромный опыт системного программирования и администрирования для различных диалектов Unix (HP-UX, Linux, SGI Irix, Sun Solaris, SunOS, IBM AIX, Bestix и т.п.), систем реального времени (VxWorks, OS/9 – OS/9000, PDOS и т.п.), разнообразных порождений платформы Wintel, таких раритетов, как AOS/VS, VMS, RSX11M, RT11 и т.п.; обладает богатой практикой преподавания ОС Unix, в настоящее время периодически ведет ряд курсов в московском Учебном центре Hewlett-Packard, в том числе курс "Диагностика HP-UX". Мы надеемся, что темы для новой рубрики нам подскажут читатели. Что мы можем предложить "в меню"? Познакомьтесь с пробным выпуском рубрики. Ясно, что речь стоит вести только о достаточно общих вопросах, интересных для достаточно широкого класса читателей. Если вы уже гуру, можете рассматривать данную страничку с высоты своего роста, положив журнал на пол.

"Я здесь все мели знаю..."

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

Сколько раз в день вы вызываете команду grep для поиска? Если вам действительно приходится делать в Unix что-то реальное, это случается по несколько раз в день. Большинство ограничивается самым тривиальным способом: ее использования — grep foo * – вывести все строки со словом foo. Однако у grep куда как больше возможностей, а для вышеуказанного поиска лучше подходит fgrep. Команда позволяет найти все строки с одним из нескольких слов, найти строки с повторами и т.п. Однако, для того чтобы воспользоваться этими возможностями, следует познакомиться с механизмом регулярных выражений. Примеры:

	grep (one\|two) files

– найти строки, в которых встречаются цепочки one или two

	grep (^[Bb]egin\|^[Ee]nd) files 

– найти строки, начинающиеся с Begin, begin, End или end.

Если вас интересует наличие шестисимвольных "полиндромов" из букв, то есть комбинаций вида abccba, можно попробовать поискать их следующим образом:

	grep (\([a-zA-z]\)\([a-zA-z]\)\
	([a-zA-Z]\)\3\2\1) files

Увы, это решение не совсем точное. Так, семи- или восьмибуквенная строка с 6 буквами в требуемом порядке будет считаться полиндромом, что, конечно, не так. Попробуйте подправить решение самостоятельно.

Рассмотрим другой вариант повтора. Предположим, нас интересуют строки, в которых некоторая цепочка (one или two) повторяется дважды:

	grep (\(one\|two\).*\1) files

Заметим, что строки вида: oneѕtwo и twoѕone не сопоставляются.

Кунсткамера

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

Некоторое время назад автор услышал на семинаре, что в одной из операционных систем (не станут ее называть – быть может, ее создатели стали осмотрительнее) имеется исключительно разумный компилятор и ассемблер (именно отдельно стоящий компилятор, ассемблер и т.п.). Компилятор, судя по скорости повторной компиляции, как-то учитывает свой предыдущий опыт и в несколько раз быстрее выполняет ту же задачу. Пройти мимо такого чуда было невозможно. Дополнительные тесты подтвердили, что происходит ускорение в несколько раз. В чем же дело? Как компилятор мог учесть произвольные изменения в тексте программы?

Ларчик открылся достаточно просто. Как большинство систем указывает на то, какие блоки принадлежат файлу? Используются самые разнообразные способы. Например, можно хранить ссылки на блоки в описателе файла и каком-то количестве индексов:

бi – указатель на конечный (содержащий данные) блок файла

Можно хранить информацию не об отдельных файлах, а о сегментах:

На этой картинке дi – количество последовательно хранящихся блоков.

Можно хранить информацию о первом блоке и отдельно связанный список блоков:

В упомянутой системе был использован следующий метод:

В каждом блоке содержалось по две ссылки (соответственно на предыдущий и следующий блок). Поскольку это была система реального времени, то, как это обычно бывает в системах этого типа, никакого кэширования данных не предусматривалось. Любые действия над файлами были достаточно утомительными; в частности, для того чтобы просто позиционироваться в конец файла, требовалось прочитать его целиком. Трудной процедурой было и обычное выделение места под файл. Чтобы уменьшить затраты на повторное выделение места под файл, было принято следующее решение: при повторном открытии файла на запись место не освобождается, а просто изменяется счетчик длины. Как вы уже догадались, вероятно, именно этим и объяснялась уникальная "разумность" средств разработки в этой системе.

Рубрика для невезучих

Пользователи Linux, количество которых неуклонно возрастает, время от времени могут оказываться лицом к лицу с неприятнейшей проблемой: компьютер не загружается. При этом, возможно, пользователь даже видит начало загрузки или может убедиться в том, что таблица разбиения диска цела. Грустно констатировать, что ваши данные — вероятно — целы, но как до них добраться? Есть ли у вас дискеты для аварийной загрузки? Надо было подумать заранее. Ставили RedHat c компакта? Вам еще повезло.

Инсталляционный диск (автор лишний раз убедился в этом для версии Redhat 5.2) умеет монтировать систему с целью ее модернизации. Посмотрим, как он это делает. Загрузитесь с инсталляционного компакт-диска и доберитесь до предложения выбрать "Install" или "Upgrade". В этот момент, используя стандартные комбинации типа Alt-F3, Alt-F4, можно изучить диагностику, которую генерирует программа инсталляции. Еще интереснее использовать комбинацию Alt-F2. На экране увидите приглашение ... bash. Если вернуться назад (Alt-F1) и выбрать "Upgrade", диск будет смонтирован под именем /mnt. Обратите внимание на диагностику (Alt-F4). Если предыдущий сеанс был завершен аварийно, то, естественно, нет полной уверенности в корректности файловой системы, о чем свидетельствует диагностическое сообщение: "EXT2-fs warning: mounting unchecked fs, running e2fsck is recommended". Понятно, что в таком случае подобное монтирование может оказаться нежелательным.

Если в этот момент выполнить команду mount (Alt-F2), окажется, что устройством для /mnt служит некий файл из /tmp (скажем, /tmp/hda4). Просмотр /tmp не позволяет найти такой файл (что не мешает выполнить, к примеру, umount /mnt). Вновь посмотрев на диагностику (Alt-F3), вы обнаружите, что этот файл был создан для монтирования и позднее удален. "Красная Шапочка" в момент инсталляции ведет себя как "Рыжая лиса" или "Серый волк", заметая за собой следы и убирая свидетелей. Если необходимо выполнить проверку файловой системы, то можно не выбирать "Upgrade", а проделать все самостоятельно (подставив вместо hda4 собственное значение):

	cd /tmp
	mknod hda4 b 3 4
	e2fsck hda4
	mount hda4 /mnt

После последней операции hda4 исчезнет.

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

Почему так легко написать неправильную программу

Как известно, язык shell (впрочем, как многие другие языки, скажем, тот же C) достаточно своенравен и требует от программиста понимания его "духа". Несколько лет назад автору довелось провести занятие по программированию на shell. Слушатели были настроены крайне самоуверенно и категорически отказывались выполнять упражнения, утверждая, что упражнения эти абсолютно банальны. Однако после того, им было предложено описать результат выполнения следующих примеров:

	if 0 then echo aaaa	fi
	if 1 then echo bbbb	fi
	if [0] then echo cccc	fi
	if [1] then echo dddd	fi

выяснилось, что даже на таком простом уровне shell владели совсем немногие (а можете ли описать результат вы?).

К сожалению, вынужден констатировать, что лишние знания мешают писать правильные программы. Можете проверить это на себе. Попробуйте правильно (то есть для всех без исключения ситуаций) решить казалось бы тривиальную задачу:

С удовольствием познакомлюсь со всевозможными решениями данной задачи.

До скорых встреч!

Кроме приведенных в этой пробной статье, у меня в запасе есть еще несколько потенциальных рубрик. Например, "Ядреное слово" – о деталях реализации и функционирования базовых компонентов ОС Unix. Напишите о своих предложениях относительно возможных рубрик, их названий, вопросы, которые вам кажутся наиболее важными и интересными. Конечно, я не обещаю ответить на любой вопрос, ноѕ попробую – сам или с помощью своих коллег.

Присылайте свои вопросы и пожелания по электронной почте по адресу [email protected].