At last!
int pid = fork(); if (pid) { printf("Yahoo!\n"); drink(); } else if (!pid) { execl("Boris", ""); } else { // never executed }
int pid = fork(); if (pid) { printf("Yahoo!\n"); drink(); } else if (!pid) { execl("Boris", ""); } else { // never executed }
Fedora перестала меня удовлетворять, и главным образом по двум причинам.
Во-первых, мне было совершенно непонятно, что там у нее происходит под капотом. Я обращался с ней как с черным ящиком, и ровно как полагается черному ящику она себя и вела — иногда непредсказуемо, иногда излишне самостоятельно. И однажды я понял, что для человека из той группы, к которой я себя причисляю, непозволительно быть таким невежей в настройке и понимании рабочей ОС.
Во-вторых, концепция релизов меня окончательно разочаровала. Релизы хороши для корпоративного использования, когда развитие проекта должно двигаться вперед, но в то же время для распространяемых дистрибутивов должна гарантироваться поддержка и стабильность. Для домашнего использования в этом мало смысла: почему я должен ждать следующего релиза чтобы моя любимая программа обновилась до свежей версии? В принципе, учитывая мою консервативность, проблема свежего софта меня не очень напрягала, но сама мысль мешала спокойно спать.
В результате я пришел к непростому выбору из Gentoo, Arch и FreeBSD. Бесполезно меня спрашивать, почему именно эти три, и почему только они. Не знаю. Возможно, Gentoo просто на слуху, Arch мне советовал коллега (который сам при этом поклонник Gentoo), а FreeBSD — это просто старая платоническая любовь к неведомому миру, который «тоже опенсорс, но другой». Уважение к опыту коллеги перевесило все остальные доводы, и я поставил Arch Linux.
Как человек, развращенный автоматическими установщиками, графическими утилитами настройки и прочими bells and whistles, я готовился к худшему. Отчетливо представлялся процесс установки в виде загрузки с live-диска в голую консоль с приглашением «Вот вам консоль. Пожалуйста, установите Linux на свой вкус и цвет. Спасибо». После этого я должен был погрузиться в чтение мануалов, провести за этим занятием пару недель, отрастить бороду до груди и приобрести интенсивный красный цвет глаз. Эти атрибуты позволили бы мне достигнуть просветления, и я, пятнадцать раз пересобрав ядро и шестнадцать раз — все остальное, — получил бы, наконец, вожделенную системищу. Вот такую жуть напредставлял.
И конечно же, все оказалось совсем не так. Новичковый мануал написан в духе «делай раз, делай два», и точное следование указаниям позволяет уже через пару часов получить работающую систему с настроенным иксовым окружением. На самом деле, я уже где-то с середины мануала плюнул на их скучные инструкции, и стал все делать по-своему. Все оказалось настолько просто и логично, что я оказался даже немножко разочарован. Хотелось почувствовать себя суровым админом, но нет, не судьба.
Что сразу подкупает в установке «почти вручную», так это что после окончания процесса ты знаешь, где у тебя что лежит, как, откуда и в каком порядке запускается, и где хранятся все настройки. Этакое ощущение тотального контроля появляется, как будто вертолет из Лего собираешь. Перечислю вкратце ключевые достоинства:
Короче говоря, друзья, категорически рекомендую Arch тем, для кого Ubuntu — это «для чайников», а Gentoo — «для красноглазых». Очень разумный компромисс.
Не люблю, когда make выплевывает километры всякого мусора. Зачем мне для каждого файла полный вызов gcc со всеми флагами? Спасибо, я эти флаги сам задавал, знаю что к чему. Зато среди всей этой ерунды пропустить что-то важное — проще простого. Значит, надо как-то выделить важное, а неважное вовсе не показывать. Вот какой я мудрый!
В обычных Makefile’ах все просто:
chset.c: chset.lex
@echo " FLEX $(@:.c=)"
@$(LEX) --outfile=$@ $<Символ ‘@’, указанный перед командой, подавляет вывод на stdout, оставляя возможность увидеть ошибки, выводимые в stderr. Строчка-подсказка выводится как раз для того, чтобы увидеть, на каком этапе сборки произошла ошибка, если таковая будет.
При использовании Autotools все становится сложнее, тут уже одними собачками не обойдешься. Интересное решение предлагает Shave: он подменяет вызовы компилятора и libtool на вызовы своих сценариев, которые молча выполняют все что нужно, а выводят только краткую диагностику, как в предыдущем примере. Вот так, например:
[nkalex@stables build]$ make Making all in src CC main.o CC msg.o CC ctype.o CC collate.o CC charset.o CC ldefpars.o CC locopts.o CC chset.o CC ldeflex.o LINK localedef
Как настраивать Shave, написано в его кратком руководстве, но там есть пара граблей, на которые я не преминул наступить. Поэтому лучше уж я свою инструкцию напишу, чтоб люди не мучались.
LT_INIT
AC_CONFIG_FILES([ shave shave-libtool Makefile src/Makefile ])
SHAVE_INIT(.,enable)
$ libtoolize $ automake --add-missing
Теперь по умолчанию Shave включен, и вывод при сборке будет кратким. Чтобы все-таки посмотреть подробный вывод, нужно добавить ключ в вызов configure:
$ configure --disable-shave
Грабли, на которые я наступил, заключались в необходимости включить поддержку libtool. Без этого вызов SHAVE_INIT выдает крайне невнятную диагностику в духе syntax error, и пришлось читать shave.m4, чтобы разобраться, в чем, собственно, дело.
Иногда в аргументах сценария приходят пути к файлам и каталогам в разнообразных форматах, ну например:
./file1 ~/cat1/cat2/ ../../cat1/file2 justfile cat1/file3
А хочется в каждом случае получить полный путь, скажем, «/home/user/cat1/file». И если второй вариант раскроется оболочкой в полный путь еще до передачи сценарию, то остальные так и останутся с точками и относительными путями.
Есть простое и изящное решение, подсмотренное на каких-то форумах:
absPath=$(readlink -f "$(dirname "$relPath")")/$(basename "$relPath")
Эта штука замечательно работает, но ровно до тех пор, пока мы не передадим ей путь «.», «./», «..» или «../». В таком случае basename "$relPath" выдаст «.» или «..», и мы в результате получим путь, который выглядит, соответственно, как-то вроде «/home/user/cat1/cat2/.» или «/home/user/cat1/cat2/..», который работать-то будет, но выглядит не очень.
Можно, конечно, почистить путь от стоящих в конце точек, но в случае с двумя точками придется еще и удалить предпоследнюю компоненту. Но зачем делать работу самому, если ее за тебя могут сделать другие? Вот такой получился окончательный вариант:
absPath=$(readlink -f $(readlink -f "$(dirname "$relPath")")/$(basename "$relPath"))
Если кто будет спрашивать, cond — это из мира Лиспа.
Подобную же конструкцию можно сделать и в C:
var = (cond1 ? expression1 : cond2 ? expression2 : /* ... */ else-expression);
Переменной var присваивается значение выражения expression1, если истинно значение выражения cond1, присваивается значение expression2, если истинно значение cond2, и т.д. Если ни один condX не истинен, присваивается значение else-expression.
По-моему, красиво. Для пущего сходства с Лиспом можно добавить скобочек.
Последние две недели мне приходилось разбираться в чужом коде с целью отловить и уничтожить неведомую ошибку. Не сказать, чтобы код был особенно сложен, но сам я настолько далек от звания гуру современного программирования, что несколько седых волос где-то в глубинах моей шевелюры наверняка за это время зародились. Задача усугублялось невозможностью выполнить код под отладчиком (это был планировщик ОС), так что в моем распоряжении были только
Были еще встроенные в ОС средства протоколирования, но они именно в эти две недели не работали. Удивительное совпадение. Хотя нет, скорее, все дело в кривизне рук. Неважно.
Так вот, отладочная печать прекрасно себя зарекомендовала. Изучение длинных простыней протоколов из строк вроде «(kernDispatchThread) pre-switch ct=174380000 diff=55489 nn=4», «test 4»и даже просто «hahaha!!!» позволяют получить истинное удовольствие от расследования а-ля Хаус. Ну-ка, кто у нас тут вытеснил этот поток? Но вот аномальная последовательность строк найдена, и теперь никуда не денешься — придется разглядывать код, который эту аномалию продемонстрировал.
Казалось бы, самое время сказать: «Трепещите, о презренные байты! Склонитесь перед интеллектом Человека!». Но нет, байты трепетать отказываются, функции плодятся прямо на глазах, макросы-сволочи делают вид, что они функции, или даже того хуже — оперируют именами локальных переменных (ужас-ужас!). Вся эта компания разбегается по двум десяткам файлов, откуда, ехидно посмеиваясь, гадит в глобальные переменные, норовя смешать физическую и виртуальную адресацию.
Вот как-то так я и провел две недели.
И не переставало мне думаться, что текст в качестве носителья исходного кода всем хорош: и редактировать его легко, и всякие diff’ы со слияниями делать, и от платформы не зависит (ну, почти), да и сколько уже проверенных временем алгоритмов на строках есть. Вот только читать чужие исходники неудобно. Хорошая IDE позволяет от вызова функции перейти к ее определению, но серебряной пулей тут и не пахнет. Пока читаешь код вызванной функции, забываешь, что делала вызывающая. И хорошо еще когда в когнитивном процессе участвует две функции. А что если 10? Или 50?
В таком случае поможет только абстрагирование: надо поделить 50 функций на 5 групп, рассмотреть взаимодействие этих групп, а потом — внутри каждой в отдельности. Да, модульное программирование явно неглупые люди придумали. Вот только выделение абстракций — отдельная большая работа. Если мы читаем свой код, то эта работа уже сделана еще при его написании. А вот выделение абстракций на уже существующем коде… Вот тут-то его текстовая природа и подкладывает нам козу: чтобы выделить абстракции, нужно сначала код прочитать и понять.
Короче, умные люди решили, что неплохо бы дать возможность людям видеть сразу весь интересующий их код. А чтобы удобнее было выделять абстракции, дать возможность еще и как-то помечать фрагменты. Ну и редактировать, само собой. Примерно так и работает Code Bubbles. Обязательно посмотрите демонстрационное видео.
В таком подходе есть ряд очевидных недостатков. Например, нужен гигантский монитор. И отсутствие аллергии на работу мышкой. И ширина строк кода должна быть вменяемой (обратили внимание, какие они короткие в демке?). Но в целом идея здравая: кто сказал, что код — это обязательно плоский текст? (Я не говорил.) При анализе чужого кода такая штука уж точно будет полезной.
P.S. Я давно уже дочитал «Организацию ЭВМ» и с пяток книжек сверху. Просто под линуксом не работает мой антикварный сканер. Постараюсь что-нибудь придумать.