Тег «UNIX»

Макроязык m4

Подготовка к выступлениям сразу на двух конференциях (SITOP и SofTool, если интересно) совершенно вывела из строя мой широковещательно-эпистолярный процессор. Не то чтобы мне не о чем написать (уж это-то всегда есть), просто темы все у меня какие-то трудоемкие, а кормить вас очередными горстями ссылок, не разбавляя более интеллектуальным контентом, рука не поднимается. Хотя и ссылки тоже будут, уж будьте уверены.

Но сейчас хочу поделиться впечатлениями от языка m4, который я внезапно выучил. Поскольку я тут понемногу готовлю мегапост (а скорее даже статью) про Autotools, то неизбежно заинтересовался, что это за странный язык m4, на котором написан Autoconf. Оказалось, на сайте GNU есть вполне приличный мануал по этому поводу. Есть даже чуть менее приличный мануал на русском.

История m4 уходит в глубину веков. Сначала появился макроязык GPM, аж в 1965 году. Потом Деннис Ричи, основательно перекроив GPM под нужды реального мира, написал m3. Затем к Ричи присоединился Керниган. Но ведь все знают, что когда Керниган и Ричи собираются вместе, обязательно выходит какая-нибудь нетленка. И вот эти два достойных мужа в едином порыве создают в 1977 году язык m4 (имя расшифровывается по аналогии с i18n и l10n). А уже в 1990 году появилась GNUтая версия. С тех пор мало что изменилось, разве что скорость выполнения немного подросла.

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

По мере чтения мануала я проходил следующие стадии:

  1. Ммм, интересно как!
  2. Сложно, блин. Черт, как же этот пример работает?..
  3. Определенно, язык писали идиоты для дебилов!
  4. Аааа, у меня в мозгу кавычки! Вытащите их!!!
  5. Понял! Да тут же все просто!
  6. Пожалуй, напишу на m4 генерацию документации для нашего проекта, а потом еще напишу парсер файлов определения локалей, а еще напишу…

На последней стадии нахожусь до сих пор. Рациональных объяснений этому не нахожу. Зато понял, что автор мануала не шутил, написав в самом начале:

Some people find m4 to be fairly addictive. They first use m4 for simple problems, then take bigger and bigger challenges, learning how to write complex sets of m4 macros along the way. Once really addicted, users pursue writing of sophisticated m4 applications even to solve simple problems, devoting more time debugging their m4 scripts than doing real work. Beware that m4 may be dangerous for the health of compulsive programmers.

В чем принципиальное отличие m4 от других языков? Есть языки императивные и декларативные, есть структурные, функциональные и объектно-ориентированные. Все они в конечном итоге нужны для построения программы, которая выполняет определенные действия. Еще есть языки текстовых фильтров: sed, awk и т.п. Они преобразуют входные данные в соответствии с заданными правилами. Язык m4 — порождающий. Программа, написанная на нем, не выполняется — она разворачивается в текст. Поначалу это несколько выносит мозг, но потом начинаешь ценить простоту и красоту подобных конструкций:

define(`quote', `ifelse(`$#', `0', `', ``$*'')')dnl
define(`foreach',
  `_args(`$0', `$#', 3)
   foreach1($1, $2, $3)'
)
define(`arg1', `$1')
define(`foreach1',
  `ifelse(
      quote($3), `',
      `',
      `define(`$1', `arg1($3)')$2`'$0(`$1', `$2', shift(shift(shift($@))))'
    )'
)

Ага, это именно то, о чем вы подумали. Цикл foreach, построенный на примитивах define, ifelse и рекурсии. Почти как в Lisp.

Да, синтаксис не блещет изяществом. Кроме того, программа не делится на строки; конец строки воспринимается буквально и попадает в порожденный текст (кроме определенных случаев). Но зато язык в действительности очень простой. В нем очень мало встроенных средств, дай бог наберется три десятка встроенных макросов. Но, как и любой язык с малым количеством базисных элементов, m4 требует определенной умственной акробатики при написании сложных конструкций. А свободное восприятие открывающих и закрывающих кавычек и нумерованных переменных a la Perl/bash/PHP — вопрос пары дней практики.

m4 на удивление непопулярен. В крупных проектах, за исключением Autoconf, замечен не был (UPD: еще sendmail, спасибо Юрию). Может быть, есть какие-то другие порождающие языки, намного красивее и удобнее? Пока же я вижу, что m4 очень простое и мощное средство, позволяющее сэкономить массу сил (масса сил… хм). Еще одно преимущество — m4 есть везде, где есть Autoconf, а это практически все *nix-системы.

Если интересно, могу развить тему и написать небольшое руководство.

УжасноПлохоНормальноХорошоОтлично (Еще не оценили)
Loading ... Loading ...

Книга: «Программирование в стандарте POSIX. Часть 2»

Галатенко В.А. Программирование в стандарте POSIX. Часть 2Название: Программирование в стандарте POSIX. Курс лекций. Учебное пособие. Часть 2.
Автор: В.А. Галатенко
Год выхода: 2005
Издательство: Интернет-Университет Информационных Технологий
Тираж: 500
Объем: 384 стр.
Обложка: твердая
Где покупал: нигде (подарок)

Как некоторые, возможно, догадались из названия, эта книга — продолжение уже описанного мною одноименного курса. Как и первая часть, эта книга имеет соответствующий курс на Интуите. Только если первая часть посвящена програмированию в POSIX-системах вообще, то эта — программированию мобильных приложений реального времени. Да еще и в POSIX. Но на обложке об этом ничего не сказано, чтобы, не дай бог, читателей не распугать раньше времени.

Системы реального времени — вообще штука непростая, скажу вам как имеющий к этому делу отношение. А уж мобильное (portable) программирование СРВ — и вовсе высший пилотаж. Хотя бы потому, что мобильность приложений реального времени сродни Неуловимому Джо. Обычно, когда разрабатывается СРВ, софт пишется под конкретную железяку (или уж по меньшей мере под конкретную ОС), на ней же тестируется, и если работает, то его стараются больше не трогать. Переносить уже написанную управляющую программу на другое железо вряд ли кому-то придет в голову. Тем не менее, POSIX имеет определенные средства для написания программ реального времени, и даже позволяет относительно легко написанное потом переносить.

Посмотрим, что нам в этой книге предложено:

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

Почему потоки и их синхронизация относятся именно к реальному времени — для меня загадка. На мой взгляд, потоки следовало бы перенести в первую книгу. Совершенно шикарно описаны сигналы с таймерами, планирование и асинхронный ввод-вывод. Остальное, в принципе, тоже хорошо, но особого восторга у меня не вызвало. Может быть, потому что я уже имел об этом представление. Скучновато было читать про трассировку, но про нее весело написать просто невозможно.

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

Для общего развития вряд ли стоит читать такую книгу, но специалистам по POSIX-программированию однозначно must have. Правда, тираж совершенно мизерный, так что есть вероятность, что в продаже уже не найдешь. Читайте онлайновый курс, там то же самое.

УжасноПлохоНормальноХорошоОтлично (1 голосов, средний: 5.00 из 5)
Loading ... Loading ...

Перенаправление ввода-вывода в bash

Когда я начинал изучать написание сценариев bash, была такая штука, которую я никак не мог понять — перенаправление ввода-вывода. Снова и снова я перечитывал документацию, дословно переписывал оттуда положенные циферки и амперсанды, но никак не мог понять расстановки этих самых магических символов.

Я предполагаю, что вы знакомы с понятиями «файловый дескриптор», «поток ввода (вывода)», «конвейер» и т.д., но просто никак не можете запомнить, как всем этим пользоваться. На самом деле все просто. Смотрите.

Команда перенаправления вывода позволяет все, что записывается в один файловый дескриптор (откуда), записать вместо этого в другой файловый дескриптор (куда):

откуда > куда

Что может быть на месте откуда:

  • ничего: перенаправление из дескриптора 1 (stdout);
  • i: перенаправление из дескриптора i;
  • &: перенаправляются сразу stdout и stderr.

Что может быть на месте куда:

  • &j: перенаправление в дескриптор j;
  • имя файла: файл открывается в режиме записи, и перенаправление осуществляется в него.

Если используется оператор перенаправления >> (дописать в конец) вместо > (очистить и писать сначала), то куда может быть только именем файла.

Указываемый дескриптор не обязательно должен быть открыт. Номера с 3 по 9 можно использовать как «перевалочные пункты»; открытые файлы получают дескрипторы, начиная с 10.

Перенаправление ввода работает очень похоже:

куда < откуда

Теперь при чтении из куда на самом деле чтение будет происходить из откуда. Тут по сравнению с перенаправлением вывода все наоборот. Куда может быть пустым (stdin) или номером дескриптора. Заметьте, что символ & здесь уже не годится — нельзя читать из двух потоков сразу. Откуда может быть или вида &i, или именем файла, который при этом открывается для чтения.

Указанное в команде перенаправление работает только для одной строки сценария; чтобы перенаправление было постоянным, нужно выполнить его с помощью exec. В одной строке можно указывать несколько перенаправлений, причем они комбинируются. Например:

ps -aF > myfile 2>&1

Здесь сначала stdout перенаправляется в файл, а затем stderr перенаправляется в stdout. В результате и stdout, и stderr перенаправляются в файл. Замечу, что перенаправление выполняется по порядку. То есть, такая команда будет работать по-другому:

ps -aF 2>&1 > myfile

Здесь stderr будет выведет на консоль. Правило простое: перенаправление происходит в текущую конечную точку перенаправления дескриптора (если не поняли, прочитайте предложение еще раз).

Еще один тонкий момент: как быть с конвейерами? Распространяется ли действие перенаправления на весь конвейер? Правила просты:

  • перенаправление действует только на ту команду в конвейере, где оно задано;
  • команда выдает в конвейер данные, выведенные в дескриптор 1 (с учетом перенаправлений);
  • команда берет из конвейера данные, полученные из дескриптора 0 (с учетом перенаправлений).

Например, вот так можно перенаправить поток ошибок в конвейер:

ps -aF 3>&1 1>&2 2>&3 | grep something

Здесь stderr и stdout фактически меняются местами.

Конечно, можно много чего еще сказать по поводу перенаправления: про одновременное перенаправление и ввода, и вывода, про перенаправление блока команд, про перенаправление с помощью exec, про закрытие дескрипторов и т.д. Но тогда пост получится слишком объемным, и им неудобно будет пользоваться как справкой. Если у вас есть какие-то вопросы, готов ответить в комментариях. За найденные ошибки также буду признателен.

УжасноПлохоНормальноХорошоОтлично (3 голосов, средний: 5.00 из 5)
Loading ... Loading ...

Книга: «Программирование в стандарте POSIX. Часть 1»

Галатенко В.А. Программирование в стандарте POSIX. Часть 1Название: Программирование в стандарте POSIX. Курс лекций. Учебное пособие. Часть 1.
Автор: В.А. Галатенко
Год выхода: 2004
Издательство: Интернет-Университет Информационных Технологий
Тираж: 2000
Объем: 560 стр.
Обложка: твердая
Где покупал: нигде (подарок)

Книгу мне подарил лично Владимир Антонович, поэтому я посчитал себя обязанным ее всю прочитать и поделиться впечатлениями с «уважаемым all».

Книга является дословным бумажным воплощением одноименного курса на Интуите. Так что все сказанное в равной степени справедливо и для оного курса.

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

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

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

Существует еще и вторая часть книги, в которой рассказывается о «продвинутых» возможностях: потоки, средства реального времени, асинхронный ввод-вывод, трассировка и т.д. Уже взялся ее читать, ждите обзор.

Минусы:

  • код набран слишком крупным шрифтом, ширины страницы часто не хватает, да и по вертикали на странице помещается мало строк.

Плюсы:

  • подробное, но при этом очень сжатое изложение; никакой воды, только информация;
  • рассматриваются многие неочевидные моменты — видно глубокое понимание материала;
  • отличное качество печати и переплета.
УжасноПлохоНормальноХорошоОтлично (1 голосов, средний: 3.00 из 5)
Loading ... Loading ...

Паттерны написания серверов

Нашел совершенно шикарнейший обзор методов написания высоконагруженных серверов в UNIX-подобных осях. Грамотно расписаны все плюсы, минусы и подводные камни. Есть еще даже более развернутая статья на ту же тему, но на английском.

УжасноПлохоНормальноХорошоОтлично (Еще не оценили)
Loading ... Loading ...

Цикл в Makefile

Возникла у меня такая задача: в действиях одной из целей Makefile выбрать из каталога файлы по маске *.eps и скормить их программе epstopdf. Проблема в том, что epstopdf принимает в командной строке только один файл. Нужен цикл. Я нашел два решения.

Первое — использовать шелловский for:

build:
    for epsfile in `ls *.eps`;\
    do\
        epstopdf $$epsfile;\
    done

Здесь две хитрости: экранирование концов строк (потому что for — фактически одна команда) и использование $$ перед именем переменной (потому что иначе она будет считаться макросом Make).

Второй способ использует возможности самого Make:

%.pdf: %.eps
    epstopdf $?
 
build: $(patsubst %.eps, %.pdf, $(wildcard *.eps))

Такой подход имеет большое преимущество: теперь epstopdf не будет вызываться для EPS-файлов, которые не изменились с момента последнего вызова. К тому же это короче, хотя и, как мне кажется, хуже воспринимается визуально.

К сожалению, все это не удастся сократить до такого:

build: $(wildcard *.eps)
    epstopdf $?

Здесь макрос $? развернется в строку, содержащую имена сразу всех EPS-файлов, что противоречит условию.

УжасноПлохоНормальноХорошоОтлично (1 голосов, средний: 5.00 из 5)
Loading ... Loading ...