Bash в примерах, часть 3

Исследование системы ebuild

Введение в систему ebuild

Я в действительности надеюсь на эту третью завершающую статью Bash в примерах, поскольку теперь, когда мы уже познакомились с основами программирования на bash в части 1 и части 2, мы можем сосредоточиться на более продвинутых темах, разработка приложений bash и планирование программ. Для этой статьи я дам вам хорошую дозу практического опыта из реальной разработки, представляя проект, которому я посвятил много часов для кодирования и улучшения: система Gentoo Linux ebuild.

Одна из моих основных обязанностей, как создателя Gentoo Linux и парня, отвечающего за Funtoo Linux, заключается в том, чтобы убедиться, что все пакеты операционной системы (подобно пакетам RPM) созданы надлежащим образом и работают вместе. Как вы вероятно знаете, стандартная система Linux не состоит из единого унифицированного дерева (подобного BSD), а на самом деле состав собирается из примерно 25+ центральных пакетов, которые работают вместе. Некоторые из пакетов включают:

Пакет Описание
linux Фактическое ядро
util-linux Коллекция разнообразных программ, родственных Linux
e2fsprogs Коллекция утилит, связанных с файловой системой ext2
glibc Библиотека GNU C

Замечание:

Любителям Gentoo: первоначальный текст раньше говорил "Я руководитель архитектуры Gentoo Linux, OS Linux следующего поколения, находящийся сейчас в стадии beta. Одна из моих основных обязанностей -- убедиться, что все бинарные пакеты (подобно пакетам RPM) созданы надлежащим образом и работают вместе." Это заслуживает внимания благодаря тому факту, что начальная цель Gentoo была в том, чтобы обеспечить работающие бинарные пакеты.

Каждый пакет занимает свой собственный архив tar и поддерживается отдельными независимыми разработчиками или командой разработчиков. Чтобы создать дистрибутив, каждый пакет необходимо отдельно скачать, скомпилировать и оформить в виде пакета. Каждый раз, когда пакет требует исправления, обновления или улучшения, шаги компиляции и пакетирования необходимо повторить (и они действительно быстро устаревают). Чтобы помочь устранить повторяющиеся шаги, включающиеся в создание и обновление пакетов, я создал систему ebuild, написанную почти полностью на bash. Чтобы улучшить ваше знание bash, я шаг за шагом покажу вам как я реализовал части распаковки и компиляции системы ebuild. Так как я объясняю каждый шаг, я буду также обсуждать, почему делаются те или иные решения проекта. К концу этой статьи вы не только получите отличное понимание крупномасштабных проектов программирования на bash, но вы также реализуете большую часть полной системы auto-build.

Почему bash?

Bash -- основная компонента системы ebuild Gentoo Linux. Он был выбран в качестве основного языка по ряду причин. Во-первых, он имеет несложный и знакомый синтаксис, который особенно подходить для вызова внешних программ. Система auto-build -- "склеивающая код", которая автоматизирует вызов внешних программ, и bash очень подходит для такого типа приложений. Во-вторых, поддержка интерпретатором bash функций позволяет сделать систему ebuild модульной, с легко понимаемым кодом. В-третьих, система ebuild позволяет воспользоваться поддержкой переменных среды bash, позволяя поддерживающим пакет и разработчикам легко, на лету его конфигурировать.

Обзор процесса создания

Прежде чем взглянуть на систему ebuild, давайте посмотрим, что включается в процесс компиляции и установки пакета. Для нашего примера мы выберем пакет "sed", стандартную утилиту GNU текстового потокового редактирования, которая является частью дистрибутива Linux. Во-первых, скачаем исходный архив tar (sed-3.02.tar.gz) (см. Ресурсы). Мы сохраним этот архив в /usr/src/distfiles, директорию, на которую мы будем ссылаться, используя переменную среды '$DISTDIR'. '$DISTDIR' -- директория, где обитают все оригинальные архивы tar исходных текстов; это большой свод исходных кодов.

Наш следующий шаг -- создать временную директорию с именем work, которая вмещает несжатые исходники. Позже мы будем ссылаться на эту директорию, используя переменную среды '$WORKDIR'. Чтобы сделать это, перейдите в директорию, где мы имеет право записи и наберите следующее:

$ mkdir work
$ cd work
$ tar xzf /usr/src/distfiles/sed-3.02.tar.gz

Архив tar таким образом распаковывается, создавая директорию с именем sed-3.02, которая содержит исходники. Позже мы будем ссылаться на директорию sed-3.02, используя переменную среды '$SRCDIR'. Чтобы скомпилировать программу, наберите следующее:

$ cd sed-3.02
$ ./configure --prefix=/usr
(autoconf генерирует соответствующие файлы makefile, это может занять некоторое время)

$ make

(пакет компилируется из исходников, также занимает немного времени)

Мы собирается пропустить шаг "make install", так как в этой статье мы даем только шаги распаковки и компиляции. Если бы мы хотели написать скрипт bash для выполнения этих шагов, он мог бы выглядеть так:

#!/usr/bin/env bash

if [ -d work ]
then
# удалить старую директорию work, если она существует
      rm -rf work
fi
mkdir work
cd work
tar xzf /usr/src/distfiles/sed-3.02.tar.gz
cd sed-3.02
./configure --prefix=/usr
make

Обобщение кода

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

#!/usr/bin/env bash

# P -- имя пакета

P=sed-3.02

# A -- имя архива

A=${P}.tar.gz

export ORIGDIR=`pwd`
export WORKDIR=${ORIGDIR}/work
export SRCDIR=${WORKDIR}/${P}

if [ -z "$DISTDIR" ]
then
# установить переменную DISTDIR в /usr/src/distfiles, если еще не установлена
        DISTDIR=/usr/src/distfiles
fi
export DISTDIR

if [ -d ${WORKDIR} ]
then    
# удалить старую директорию work, если она существует
        rm -rf ${WORKDIR}
fi

mkdir ${WORKDIR}
cd ${WORKDIR}
tar xzf ${DISTDIR}/${A}
cd ${SRCDIR}
./configure --prefix=/usr
make

Мы добавили в код множество переменных, но он в основном все еще делает то же самое. Однако теперь, чтобы скомпилировать любой стандартный архив tar, основанный на autoconf, мы можем просто скопировать этот файл в новый (с подходящим именем, чтобы отразить имя нового пакета, который он компилирует), а затем изменить значения '$A' и '$P' на новые значения, Все остальные переменные среды автоматически приведутся в правильные установки, и скрипт будет работать, как ожидается. Хотя это удобно, есть еще улучшение, которое можно сделать в этом коде. Этот конкретный код много длиннее оригинального скрипта, который мы создали. Так как одна из целей для любого программного проекта это уменьшение сложности для пользователя, было бы хорошо эффектно сократить код, или по крайней мере, организовать его лучше. Мы можем сделать это, выполнив изящный трюк -- мы разобьем код на два отдельных файла. Сохраним этот файл как sed-3.02.ebuild:

#the sed ebuild file -- very simple!
P=sed-3.02
A=${P}.tar.gz

Наш первый файл тривиален и содержит только те переменные среды, которые должны конфигурироваться на основе пакетов. Вот второй файл, который содержит интеллектуальную часть операции. Сохраните его как "ebuild" и сделайте его исполнимым:

#!/usr/bin/env bash


if [ $# -ne 1 ]
then
        echo "ожидается один аргумент."
        exit 1
fi

if [ -e "$1" ]
then
        source $1
else
        echo "Файла ebuild $1 не найден."
        exit 1
fi

export ORIGDIR=`pwd`
export WORKDIR=${ORIGDIR}/work
export SRCDIR=${WORKDIR}/${P}

if [ -z "$DISTDIR" ]
then
        # установить переменную DISTDIR в /usr/src/distfiles, если еще не установлена
        DISTDIR=/usr/src/distfiles
fi
export DISTDIR

if [ -d ${WORKDIR} ]
then    
        # удалить старую директорию work, если она существует
        rm -rf ${WORKDIR}
fi

mkdir ${WORKDIR}
cd ${WORKDIR}
tar xzf ${DISTDIR}/${A}
cd ${SRCDIR}
./configure --prefix=/usr
make

Теперь, когда мы разделили нашу систему построения на два файла, держу пари вы желаете узнать, как она работает. По существу, чтобы скомпилировать sed, наберите:

$ ./ebuild sed-3.02.ebuild

Когда выполняется "ebuild", сначала производится попытка выполнить команду "source" с переменной '$1'. Что это означает? Вспомните из моей первой статьи, что '$1' это первый аргумент командной строки, в нашем случае sed-3.02.ebuild. В bash команда "source" читает из файла предложения bash и выполняет их так, как если бы они появились непосредственно в файле, в котором находится команда "source". Итак, "source ${1}" заставляет скрипт "ebuild" выполнить команды в sed-3.02.ebuild, что определяет '$P' и '$A'. Это изменение конструкции действительно удобно, потому что если мы захотим скомпилировать вместо sed другую программу, мы можем просто создать новый файл .ebuild и передать его как аргумент нашему скрипту "ebuild". Таким образом, файлы .ebuild оказываются действительно простыми, тогда как сложные мозги системы ebuild сохраняются в одном месте -- нашем скрипте "ebuild", помещая детали реализации вне файлов ebuild. Вот пример модельного файла ebuild для 'gzip' :

#еще один действительно простой скрипт ebuild!
P=gzip-1.2.4a
A=${P}.tar.gz

Добавим функциональности

Хорошо, мы добились некоторого прогресса. Но есть некоторая дополнительная функциональность, которую я хотел бы добавить. Я хотел бы, чтобы скрипт ebuild принимал второй аргумент командной строки, который был бы 'compile' , 'unpack' , or 'all' . Этот второй аргумент командной строки говорит скрипту ebuild, какой конкретный шаг процесса построения нужно выполнить. Таким образом, Я могу приказать ebuild распаковать архив, но не компилировать его (на всякий случай мне нужно проверить исходный архив перед началом компиляции). Чтобы сделать это, я добавлю предложение case, которое будет проверять переменную '$2' , и будет делать разные действия взависимости от ее значения. Вот как выглядт код теперь:

#!/usr/bin/env bash

if [ $# -ne 2 ]
then
        echo "Пожалуйста, задайте два аргумента - файл .ebuild file и unpack, compile или all"
        exit 1
fi


if [ -z "$DISTDIR" ]
then
 # установить переменную DISTDIR в /usr/src/distfiles, если еще не установлена
        DISTDIR=/usr/src/distfiles
fi
export DISTDIR

ebuild_unpack() {
         #убедитесь, что мы в правильной директории
        cd ${ORIGDIR}
        
        if [ -d ${WORKDIR} ]
        then    
                rm -rf ${WORKDIR}
        fi

        mkdir ${WORKDIR}
        cd ${WORKDIR}
        if [ ! -e ${DISTDIR}/${A} ]
        then
            echo "${DISTDIR}/${A} не существует.  Пожалуйста, скачайте сначала."
            exit 1
        fi    
        tar xzf ${DISTDIR}/${A}
        echo "${DISTDIR}/${A} распакован."
        #исходник теперь корректно распакован
}


ebuild_compile() {
        
         #убедимся, что мы в правильной директории
        cd ${SRCDIR}
        if [ ! -d "${SRCDIR}" ]
        then
                echo "${SRCDIR} не существует -- пожалуйста, распакуйте сначала."
                exit 1
        fi
        ./configure --prefix=/usr
        make     
}

export ORIGDIR=`pwd`
export WORKDIR=${ORIGDIR}/work

if [ -e "$1" ]
then
        source $1
else
        echo "Файл ebuild $1 не найден."
        exit 1
fi

export SRCDIR=${WORKDIR}/${P}

case "${2}" in
        unpack)
                ebuild_unpack
                ;;
        compile)
                ebuild_compile
                ;;
        all)
                ebuild_unpack
                ebuild_compile
                ;;
        *)
                echo "Пожалуйста, укажите unpack, compile или all во втором аргументе"
                exit 1
                ;;
esac

Мы сделали много изменений, давайте рассмотрим их. Во-первых, мы поместили шаги compile и unpack в их собственные функции с именем 'ebuild_compile()' и 'ebuild_unpack()' соответственно. Это хороший шаг, так как код становится более сложным, а новые функции обеспечивают модульность, которая помогает поддерживать некоторую организованность. В первой строке каждой функции я явно выполняют 'cd' в директорию, в которой я хочу быть, поскольку наш код становится более модульным, а не линейным, более вероятно, что мы можем ошибиться и выполнить функцию в неправильной текущей директории. Команда 'cd' явно переводит нас в правильное место и предотвращает нас от последующих ошибок -- важный шаг -- особенно, если вы будете удалять файлы в функциях.

Я добавил также полезную проверку в начале функции 'ebuild_compile()' . Теперь она провереяет существует ли '$SRCDIR' , и, есл нет, печатает сообщение об ошибке, сначала говоря пользователю, что надо распаковать архив, а затем завершается. Если хотите, вы можете изменить поведение так, что если '$SRCDIR' не существует, наш скрипт ebuil будет распаковывать исходный архив автоматически. Вы можете сделать это заменив 'ebuild_compile()' следующим кодом:

ebuild_compile() {
        #убедимся, что мы в правильной директории
        if [ ! -d "${SRCDIR}" ]
        then
                ebuild_unpack
        fi
        cd ${SRCDIR}
        ./configure --prefix=/usr
        make     
}

Одно из самых очевидных изменений в нашей второй версии скрипта ebuild это новое предложение case в конце кода. Это предложение case просто проверяет второй аргумент командной строки и выполняет правильное действия в зависимости от его значения. Если мы теперь наберем:

$ ebuild sed-3.02.ebuild

Мы на самом деле получим сообщение об ошибке. ebuild теперь хочет, чтобы ему сказали, что делать, например:

$ ebuild sed-3.02.ebuild unpack

или:

$ ebuild sed-3.02.ebuild compile

или:

$ ebuild sed-3.02.ebuild all

Важно!

Если вы зададите второй аргумент командной строки отличный от перечисленных выше, вы получите сообщение об ошибке (случай *), и программа завершится.

Сделаем код модульным

Теперь, когда код довольно продвинут и функционален, вы можете поддаться искушению создать еще несколько скриптов ebuild для распаковки и компиляции ваших любимых программ. Если вы сделаете это, рано или поздно вы столкнетесь с какими-нибудь исходниками, которые не используют autoconf ( './configure' ) или, может быть, другими, с не стандартным процессом компиляции. Нам нужно сделать еще некоторые изменения системы ebuild, чтобы приспособиться к этим программам. Но прежде чем мы сделаем это, неплохо подумать немного о том, как это выполнить.

Одно из самых замечательных во включении в код нашей стадии компиляции './configure --prefix=/usr; make' то, что в большинстве случаев это работает. Но мы должны также приспособить систему ebuild и к исходникам, которые не используют autoconf или обычные файлы makefile. Для решения этой проблемы я предлагаю, чтобы наш скрипт ebuild по умолчанию делал следующее:

  • Если скрипт configure есть в '${SRCDIR}' , выполнить его следующим образом: './configure --prefix=/usr' . В противном случае пропустить этот шаг.
  • Выполнить следующую команду: make

Так как ebuild запускает configure только, если он действительно существует, мы теперь можем автоматически приспособиться к тем программам, которые не используют autoconf, но имеют стандартные файлы makefile. Но что если простой "make" для некоторых исходников не достигает цели? Нам нужен способ заменить наши разумные умолчания некоторым специальным кодом, чтобы справиться с такими ситуациями. Чтобы сделать это, мы преобразуем нашу функцию 'ebuild_compile()' в две функции. Первая функция, которую можно рассматривать, как "родительскую" функцию, все еще будет называться 'ebuild_compile()' . Однако мы будем использовать новую функцию с именем 'user_compile()' , которая содержит только наши приемлемые умалчиваемые действия:

user_compile() {
        #мы уже в ${SRCDIR}
        if [ -e configure ]
        then
                #запускаем скрипт configure, если он существует
                ./configure --prefix=/usr
        fi
        #запускаем make
        make
}              

ebuild_compile() {
        if [ ! -d "${SRCDIR}" ]
        then
                echo "${SRCDIR} не существует -- пожалуйста, сначала распакуйте."
                exit 1
        fi
        #убедимся, что мы в правильной директории
        cd ${SRCDIR}
        user_compile
}

Может показаться не очевидным, почему я делаю это прямо сейчас, но потерпите. Хотя код работает почти идентично нашей предыдущей версии ebuild, мы можем теперь сделать то, что не могли раньше -- мы можем перекрыть 'user_compile()' в sed-3.02.ebuild. Итак, если умалчиваемая функция 'user_compile()' не отвечает нашим нуждам, мы можем определить новую в нашем файле .ebuild, который содержит команды, требующиеся для компиляции пакета. Например, во файл ebuild для 'e2fsprogs-1.18' , который требует немного отличающуюся строку './configure' :

#этот файл ebuil перекрывает умалчиваемую user_compile()
P=e2fsprogs-1.18
A=${P}.tar.gz
 
user_compile() {
       ./configure --enable-elf-shlibs
       make
}

Теперь 'e2fsprogs' будет компилироваться точно так, как мы хотим. Но, для большинства пакетов любую специальную функцию 'user_compile()' в файле .ebuild мы можем опустить, вместо этого будет использоваться умалчиваемая функция 'user_compile()' .

Как скрипт ebuild точно узнает, какую фунцию 'user_compile()' использовать? Это совсем просто. В скрипте ebuild умалчиваемая функция 'user_compile()' определена перед тем как обработывается файл e2fsprogs-1.18.ebuild. Если в 'user_compile()' есть в e2fsprogs-1.18.ebuild, он перекрывает ранее определенную умалчиваемую версию. Если нет, используется умалчиваемая функция 'user_compile()' .

Это замечательная вещь; мы добавили немало гибкости, не требуя, если это не является необходимым, какого-то сложного кода. Мы не рассматриваем это здесь, но вы могли бы сделать подобные модификации с 'ebuild_unpack()', чтобы пользователи могли перекрывать умалчиваемый процесс распаковки. Это могло бы оказаться удобным, если существуют какие-либо патчи или файлы содержатся в нескольких архивах. Хорошо бы также модифицировать наш распаковывающий код так, чтобы он по умолчанию распознавал архивы tar, сжатые bzip2.

Файлы конфигурации

Мы рассмотрели до сих пор множество тайных приемов bash, теперь пришло время еще одного. Часто для программы удобно иметь глобальный файл конфигурации, который располагается в /etc. К счастью, с помощью bash это легко сделать. Просто создайте следующий файл и сохраните его как /etc/ebuild.conf:

# /etc/ebuild.conf: set system-wide ebuild options in this file

# MAKEOPTS -- опции, передаваемые make
MAKEOPTS="-j2"

В этом примере я включил только одну опцию конфигурации, но вы могли бы включить намного больше. Одно из самых красивых в bash то, что этот файл можно включить просто использовав команду source. Этот конструкторский трюк, который работает в большинстве интерпретируемых языков. После того, как /etc/ebuild.conf использован в операторе source, '$MAKEOPTS' определена в нашем скрипте ebuild. Мы будем использовать ее, чтобы позволить пользователям передать опции команде make. Обычно эта опция будет использоваться, чтобы позволить пользователю сказать ebuild, что нужно выполнить make в параллельном режиме. Объясним это позже.

Замечание:

Что такое параллельный режим make? Чтобы ускорить компиляцию на многопроцессорных системах, make поддерживает параллельную компиляцию программ. Это означает, что вместо того, чтобы компилировать только один исходный файл в единицу времени, make компилирует указанное пользователем число исходных файлов одновременно (так что в мультипроцессорной системе используются дополнительные процессоры). Параллельный режим команды make задается с помощью передачи make опции '-j #' , например 'make -j4 MAKE="make -j4"' . Этот код сообщает make, что можно компилировать четыре программы одновременно. Аргумент 'MAKE="make -j4"' говорит make, что опцию '-j4' нужно передать любом потомку процесса make, который он запустит.

Вот окончательная версия нашей программы ebuild:

#!/usr/bin/env bash

if [ $# -ne 2 ]
then
        echo "Пожалуйста укажите файл ebuild file и unpack, compile или all"
        exit 1
fi

source /etc/ebuild.conf

if [ -z "$DISTDIR" ]
then
        # установить переменную DISTDIR в /usr/src/distfiles, если еще не установлена
        DISTDIR=/usr/src/distfiles
fi
export DISTDIR

ebuild_unpack() {
        #убедимся, что мы в правильной директории
        cd ${ORIGDIR}
        
        if [ -d ${WORKDIR} ]
        then    
                rm -rf ${WORKDIR}
        fi

        mkdir ${WORKDIR}
        cd ${WORKDIR}
        if [ ! -e ${DISTDIR}/${A} ]
        then
                echo "${DISTDIR}/${A} не существует.  Пожалуйста, скачайте сначала."
                exit 1
        fi
        tar xzf ${DISTDIR}/${A}
        echo "${DISTDIR}/${A} распакован."
        #исходник теперь корректно распакован
}

user_compile() {
        #мы уже в ${SRCDIR}
        if [ -e configure ]
        then
                #запускаем скрипт configure, если он существует
                ./configure --prefix=/usr
        fi
        #запускаем make
        make $MAKEOPTS MAKE="make $MAKEOPTS"  
} 

ebuild_compile() {
        if [ ! -d "${SRCDIR}" ]
        then
                echo "${SRCDIR} не существует -- пожалуйста, распакуйте сначала."
                exit 1
        fi
        #убедитесь, что мы в правильной директории
        cd ${SRCDIR}
        user_compile
}

export ORIGDIR=`pwd`
export WORKDIR=${ORIGDIR}/work

if [ -e "$1" ]
then
        source $1
else
        echo "Файла ebuild $1 не найден."
        exit 1
fi

export SRCDIR=${WORKDIR}/${P}

case "${2}" in
        unpack)
                ebuild_unpack
                ;;
        compile)
                ebuild_compile
                ;;
        all)
                ebuild_unpack
                ebuild_compile
                ;;
        *)
                echo "Пожалуйста, укажите unpack, compile или all во втором аргументе"
                exit 1
                ;;
esac

Обратите внимание, /etc/ebuild.conf используется командой source в начале файла. Также обратите внимание, что мы используем '$MAKEOPTS' в нашей умалчиваемой функции 'user_compile()' . Вы можете заинтересоваться, как это в конце концов работает, мы ссылаемся на '$MAKEOPTS' до того как передаем soirce /etc/ebuild.conf, который на самом деле определяет '$MAKEOPTS' первый раз. К счастью для нас, все хорошо, поскольку раскрытие переменных происходит только, когда 'user_compile()' выполняется. К тому времени, когда выполняется 'user_compile()' , /etc/ebuild.conf уже обработана командой source и '$MAKEOPTS' установлена в соответствующее значение.

В заключение

В этой статье мы рассмотрели множество приемов программирования на bash, но мы только прикоснулись к верхнему слою способностей bash. Например, система ebuild изготовления Gentoo Linux не только автоматически распаковывает и компилирует каждый пакет, но она также:

  • Автоматически скачивает исходные файлы, если их не в '$DISTDIR'
  • Проверяет с помощью профиля сообщения MD5, не испорчены ли исходыне файлы
  • Если требуется, устанавливает скомпилированные приложения в реальную файловую систему, записывая все установленные файлы так, чтобы пакет можно было впоследствии легко удалить.
  • Если требуется, формирует макет скомпилированного приложения в архив tar (сжатый так, как вы хотите) так, что он его можно установить позже, на другом компьютере или в процессе установки с CD (если вы создаете CD дистрибутива)

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

Ясно, что bash может выполнить гораздо больше того, чего я коснулся в этой серии статей. Надеюсь, вы узнаете массу вещей об этом невероятном инструменте, и это побудит вас использовать bash для ускорения и усиления ваших разработок.

Ресурсы

Полезные ссылки


Daniel Robbins

silly photo
This is not really me

Daniel Robbins is the founder of the Gentoo community and creator of the Gentoo Linux operating system. Daniel resides in New Mexico with his wife Mary and two energetic daughters, and is founder and lead of Funtoo. Daniel has also written many technical articles for IBM developerWorks, Intel Developer Services and C/C++ Users Journal.