от Nick Angelow(31-10-2004)

рейтинг (9)   [ добре ]  [ зле ]

Printer Friendly Вариант за отпечатване

УНИВЕРСАЛНИЯТ ИЗХОДЕН КОД

или защо компилираме програми(те)

http://unix.ginras.ru/linux/base012.html

Алексей Федорчук


Колкото голяма и всеобхватна да е дистрибуцията на Linux, избрана от крайния потребител, пред него рано или късно изниква задачата за инсталиране на допълнителни пакети или осъвременяване на вече съществуващите. Какви варианти за избор възникват пред него?

За потребителя на пакетна дистрибуция от типа на Red Hat (или всяка друга, основана на rpm), натрапващото се решение е намиране на необходимия пакет в прекомпилиран вид и съответен формат (има и съответни услуги от типа на rpm-find, но за съжаление, вече съм ги забравил), след което се изпълнява следната проста команда

$ rpm -ihv име_на_пакета.rpm

или нещо подобно. След което пакета или се инсталира, или не се инсталира. Ако пакета се инсталира – добре, а ако не се инсталира – има два изхода – да се откажем или да започнем внимателно да изучаваме полученото съобщение за грешка.

Да ме извинят привържениците на Red Hat и познавачите на rpm, но това съобщение за грешка, според мен няма да бъде особено разбираемо. По-скоро, смисъла му ще е, че при инсталирането на нашият пакет не е намерен файл от типа име_рек.so.6 или нещо подобно. Интуитивно е ясно, че не е намерена някаква библиотека, но да определим в кой пакет да търсим този файл, от полученото съобщение не е толкова лесно – името на файла в общия случай не съвпада (или корелира по някакъв начин) с името на пакета, в който влиза, тоест вероятността потребителя да се сблъска с проблема за зависимостите и тяхното разрешаване е голяма.

И пак, колеги rpm-исти, не хвърляйте по мен ертефемити1. Теоретично знам, че съществува стандартен начин за решаване на този проблем, даже няколко начина (включително и най-радикалният – използване на опцията --nodepens). Но сега, почти две години след последното плътно общуване с rpm-базирани дистрибуции, ми е трудно да посоча по памет тези начини. И във всеки случай, начините за решаване на възникнали проблеми със зависимостите, ще бъдат специфични за всяка отделна дистрибуция (по точно, за приетият в нея формат на пакетите). И навиците, придобити при работата с Red Hat малко ще помогнат при работа с Debian или Slackware.

За избягване на недоразумения, ще отбележа, че знам за метода apt, предвиждащ автоматично получаване на пакетите и тяхното инсталиране, с осигуряване на контрол върху зависимостите. Нещо повече, аз знам, че този метод има шанса да стане универсален за пакетните дистрибуции, независимо от формата на самите пакети. Но при използването на apt възниква друг проблем – проблемът с излишните зависимости. Какво точно представлява този проблем ще бъде подробно разгледано малко по-късно. Засега само ще отбележа, че потребителя се оказва зависим от зависимостите2, определени от разработчиците или от създателите на дистрибуцията, което се изразява в това, че инсталацията на конзолен файлов мениджър или браузър води след себе си инсталация на X-сървър, Gnome и какво ли не още. При това инсталация от Мрежата, по модем или наета линия с прогресивно увеличаваща се тарифа за трафика. Най-главното е, че потребителя е лишен от възможността да влияе върху инсталационният процес. Поради което, ако неговите нужди не попадат в рамките, установени от създателите на дистрибуцията или разработчиците на самите пакети, пред него има една единствена възможност – ръчно компилиране на програмата от изходния код.

Някой може да възрази, че в състава на всеки клон на Red Hat, заедно с двоичните пакети влизат и пакети с изходния код на програмата във формат rpm (*.src.rpm), работата с които е възможна със средствата на програмата за управление на пакетите. Но това не винаги е приложимо във всички възможни ситуации. Създаването на програмата от *.src.rpm се извършва по правила, описани в съответният .spec файл тоест пак по усмотрение на разработчика, а не на конкретният потребител. Разбира се, последният е свободен да променя този .spec файл според своите нужди, но кажете дали това е по-лесно и просто от ръчното компилиране по универсалната схема

$ ./configure && make && make install

И още нещо – не съм силен в създаването на rpm-пакети, но (imho) надали постигната по последният начин гъвкавост на настройките ще бъде надмината от и най-умело конструираният spec-файл.

По друг начин ще постъпи потребителят на някоя source based дистрибуция на Linux или на някоя от версиите на BSD. Във всяка една от тези системи, на негово разположение има цял комплекс за управление на пакетите, известен под името портове (в BSD клонингите или CRUX Linux), portage (в Gentoo Linux), sorcery (в Sorceror Linux и потомците му). Разбира се, и тук изходният код се изтегля от мрежата, а автоматичната система за контрол на зависимостите също е на съвестта на разработчика на порта (portage). Но а) тази система за контрол може да бъде повече (в Gentoo Linux) или по-малко (във всички останали случаи) настроена глобално и б) винаги може да се откажем от автоматичната система за контрол в полза на полуръчното определяне на променливите и опциите на компилация или просто ръчно да конфигурираме всичко.

Въпреки всичко, и на потребителя на портирани системи може да се наложи да компилира ръчно най-малкото отделни пакети,например на тези, които все още не са обхванати от системата на портовете на неговата дистрибуция или просто на най-новите версии. А в крайна сметка, гъвкавостта на ръчното компилиране все едно остава ненадмината. Показателни са думите на Андрей Лаврентиев относно FreeBSD – „всеки уважаващ себе си системен администратор, рано или късно прекомпилира със свои настройки всички критични за него пакети“, което на практика означава ръчно компилиране – във порт-системата на FreeBSD няма средства, аналогични на глобалната променлива USE от Gentoo). И тъй като на настолна машина, всеки потребител е и системен администратор, то горното може да се разпространи и върху всеки (уважаващ себе си) потребител.

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

По-нататък са необходими основните системни библиотеки, такива като glibc. Но те се намират винаги във всяка пълнофункционална дистрибуция – без тях няма да е възможна работата на нито една програма.

И накрая, без да казвам някаква велика мъдрост, ще отбележа, че за компилацията на пакетите от изходен код е необходим самият изходен код. А тук вече започват усложненията. Разбира се, свежият tarball с изходният код лесно може да се изтегли от Мрежата, въпреки, че не винаги това ще е толкова лесно – за такива чудовища като Xfree86, OpenOffice, KDE или GNOME може да се наложи да се изтеглят стотици мегабайтове, което е по силите не на всяка връзка (и не на всеки джоб).

Но ще приемем, че това са трудности на практическата реализация. Но има и принципно усложнение – това са тези прословути зависимости на пакетите. Ако системи от типа apt, портове или portage, освобождават потребителя от този проблеми то при напълно ръчното компилиране той остава с него лице в лице. Няма да споменавам и това, че за сметка на зависимостите, нуждата от изтегляне по Мрежата съществено нараства: достатъчно е да погледнем на debian.org (което е едно от най-добрите места за изучаване на зависимостите на пакетите) списъка на софтуера, необходим за компилацията и работата на някакъв не особено сложен пакет, например от типа на Midnight Commander.

А ако започнем да говорим за Xfree86 ... Изпълнявайки в Gentoo Linux командата

$ emerge --pretend xfree

ние ще намерим в списъка на необходимото и модули за поддържане на планшети wacom, и драйвери за видеокарти 3Dfx, и някакви шрифтови файлове, и много, много други неща, при това независимо от факта дали ние имаме в система графичен планшет, видеокарта 3Dfx и дали имаме наистина нужда от допълнителни шрифтове).

Но този дявол не е толкова страшен, колкото го описват. И в ролята на светена вода влиза разбирането на простата истина: зависимостта за зависимостта lupus est4. Казано по друг начин, има пакети, абсолютно необходими за компилирането и работата на дадена програма, има и пакети, които придават на нашата програма само някои допълнителни свойства.

Почти във всички описания на зависимости, които съм виждал, разлика между абсолютните (или твърди) зависимости и „опционални“ зависимости (може да ги наречем меки) в явен вид не се прави. Немногобройните изключения са същият този debian.org и документацията на Gentoo.org по работата с portages. Разбира се, на посочените сайтове не трябва да се търси универсални рецепти за всички възможни случаи, но разбирането на принципните разлики между абсолютните и опционалните зависимости много ще помогне – и в двата случая, тези разлики се посочват твърде последователно.

Очевидно е, че без абсолютните зависимости няма да можем нито да компилираме, нито да стартираме даден пакет. Всяка програма за Linux изисква някакви функции от главната системна библиотека glibc, практически всички конзолни приложения използват някаква терминална библиотека от типа на ncurses, за приложенията, работещи под X е необходима библиотеката xlib, KDE приложенията не могат да съществуват без библиотеката Qt и така нататък.

Това са очевидни примери. Почти толкова ясно е, че програмите за работа с графика, от простата fbgrab до GIMP са немислими без библиотеките на графичните формати (png, gif, tiff, jpeg), а програмите за работа със звук – без мултимедийните библиотеки за съответните формати (mpeg или ogg-vorbis). По-малко очевидно е, защо за компилацията (и работата) на Mozilla, нямаща никакво отношение към GNOME, е необходима библиотеката Gtk – но засега подобните случаи може само да ги отбелязваме, че съществуват.

Лесно се вижда, че в ролята на абсолютни зависимости обикновено се явяват някакви библиотеки. И основната (и едва ли не единствена) трудност тук е в съответствието на версиите. Например, едни от приложенията, използващи библиотеката Gtk, изискват непременно нейната втора версия, докато други приложения се задоволяват с първата (а с втората, между другото не могат да се компилират). Но в много случаи, твърдата зависимост съществува само за версиите в някакъв интервал (или казано по друг начин – за даден пакет е необходима версия на библиотеката glibc, не по-ниска от ##).

Но без съществуването на абсолютните зависимости, не може да компилираме успешно даден пакет, като е без значение дали връзката му с библиотеката (или някакво приложение) лежи на повърхността или е опосредствена по някакъв друг начин. Ето обаче, че опционалните зависимости, казано строго, не са необходими за компилирането и функционирането на същият този пакет. И макар, че много от тях се определят по подразбиране от разработчиците или компилиращите пакета, е възможно да се отървем от тях, и то по различни начини.

Практически всяка програма под абстрактният Unix, в своя списък със зависимости ще намери groff, а програмите от проекта GNU - и Texinfo. Не поради причината, че тези програми за обработка на текст са необходими за работата примерно на аудиоплейър, а само заради използването на тези програми в системите за екранна документация man и info. А без тези системи за екранна документация, нито една уважаваща себе си програма не може да мине. И затова липсата в системата, примерно, на Texinfo ще предизвика грешка при компилацията или инсталирането практически на всяко едно GNU приложение.

Разбира се, без екранна документация не е много интересно да се живее. Но аз практически не използвам info-страници и затова на моята домашна самоделна система Texinfo не е инсталиран. А за да предотвратя появата на грешки на стадия make или make install, след изпълнението на конфигурацията просто отстранявам ръчно директорията със съответната документация.

Но този пример на действие даже не е пила, а по-скоро топор, защото скрипта за конфигуриране е предназначен именно за изключване на опционалните зависимости. И естествено да ги включи, ако се появи такава нужда. Само дето не мога да си спомня някога да ми се е налагало нещо да включвам ...

В някои случаи, това изключване се извършва от самосебе си – конфигурационния скрипт, изпълнявайки проверка на системата, намира наличието или отсъствието на някаква библиотека и просто игнорира съответната функция в компилирания пакет. В други случаи, това се налага да се направи в явен вид с помощта на опциите --disable или --without в командният ред ./configure.

Трябва да отбележа, че премахването на ненужните зависимости не само води до намаляване на входящия трафик (и до икономия на място, макар, че при днешните размери на твърдите дискове, това не е толкова актуално – по-важно, според мен, е това, че системата се освобождава без изключение от компонентите с неясен произход, които съществуват във почти всички пакетни дистрибуции). Води и до повишаване на удобството на използване при някои програми. Като пример ще приведа моя любим gmp – според мен и mc и особенно links, компилирани без нея, са много по-използваеми, макар че нямам намерение да се отказвам изобщо от конзолната мишка – няма по-удобно средство да преместиш URL от конзола с links в конзола с текстов редактор.

И накрая, изключването на ненужните зависимости в някои случаи оказва благотворно влияние и върху бързодействието на програмите. Но тук вече плавно преминаваме от проблемите на зависимостите към въпросите за оптимизация, което вече е друга история.

Защото главната оптимизация се извършва по време на самата компилация, в хода на изпълнението на програмата make и се задава със специални флагове за компилатора. Най-важните от тях са – флага за задаване на нивото на оптимизация (от -O0, тоест без оптимизация въобще, до -O3) и флага за оптимизация за даден тип процесор ( -mcpu=type и -march=type, като аргументи се използват имената на процесорите, поддържани от текущата версия на gcc, а от версия 3.3.x нагоре се поддържа целият зоопарк от х-86 съвместими процесори). Водени от общи съображения, може да предположим, че за графични и мултимедийни приложения полезна ще се окаже оптимизацията за класически копроцесор или за специалните набори от инструкции от вида SSE или 3DNow (флаг -mfpmath с аргументи 387 или sse, и един от флаговете -msse, -msse2 или -m3dnow, в зависимост от типа процессора), но на практика нещата не са толкова еднозначни (за справка – цикъла на тестване).

В компилатора gcc не са малко и се използват не само за оптимизация, но и за съхраняване на допълнителна информация, полезна за настройването на системата (или за изчистването на тази информация), за повишаване на бързодействието на самия процес5, както и за още много други цели6. В крайна сметка, редът на командата make може да стане много дълъг и да се въвежда всеки път на ръка бързо може да омръзне. Това може да се избегне сравнително лесно – достатъчно е да опишем в профилния файл (на системата или на отделният потребител) следната променлива

CFLAGS="флагове и стойности, разделени с интервал"

за програми на С, и променливата

CXXFLAGS="$CFLAGS"

за програмите на С++, като не забравяме да ги експортираме (за любителите на tcsh – да ги определим с командата setenv). Само трябва да се има предвид, че някои отделни програми може да откажат да се компилират с флагове от типа -O3 и -march, макар, че при Linux такива съм срещал много рядко ( с gcc версия 3.1 и нагоре).

Трябва да споменем и това, че може да прекомпилираме и самият компилатор gcc, което би ускорило процеса на компилация. Особено ефективен в това отношение е така нареченият bootstraping – прекомпилиране на gcc от самия него, което се изпълнява с командата

$ make bootstrap

а флаговете за оптимизация се определят от променлива от типа

BOOT_CFLAGS="-O3 -march=pentium4"

(необходимото да се добави). По съществуващи оценки, това може да доведе до 30% увеличаване на скоростта на компилация, например, на ядрото на системата, което се доближава до истината.

Друг начин за увеличаване на скоростта на компилация (който за пакети от типа на Xfree86, да не споменаваме OpenOffice, може да отнеме повече от един час даже на много мощни машини) може да се реализира при наличие на голямо количество оперативна памет. За тази цел е необходимо да се поддържа виртуалната файлова система tmpfs, което се задава при конфигурацията на ядрото и монтирането и към някаква директория от вида /tmp, /dev/shm (стандартното за нея място)

Преценката на увеличаването на бързодействието на компилираните пакети също така е силно субективна, както между другото и оценката на бързодействието на Linux като цяло. Понеже „Linux по подразбиране по определение е глупост (за разлика от „Windows по подразбиране“), потребителя, който не настройва Linux както му харесва, е за другарския съд на Линч, с принудително преминаване към използване на прозорците (© Владимир Игнатов). А какви настройки са оказали най-голямо влияние за крайното бързодействие е много трудно да се определи точно.

Малко собствен опит – след като свърших с тежкия труд по компилацията на собствена система (по метода Pure Linux с някои модификации, комплектоване на пакетите – с големината на силно олекотен CRUX), аз бях просто потресен от скоростта на нейната работа. Но кое се оказа с по-голяма тежест – дали оптимизацията под Pentium-4 (с посочените по-горе флагове), превъзходството на текущия тогава gcc (версия 3.3) над неговите предшественици, намаляването на услугите и демоните, пускани при стартиране на системата или оптимизацията на ядрото – не мога да твърдя със сигурност. А да повтарям процеса поетапно с количествени измервания – нямам желание, което между другото, може да се разглежда като аргумент в полза на комплексният подход при самостоятелното изграждане на системата.

Но аз започнах този разговор не в контекста на самостоятелното изграждане, а с малко по-други цели. Първо, да насоча вниманието на потребителите на пакетни дистрибуции (за потребителите на дистрибуции, основани на изходен код, най-вероятно не съм казал нищо ново) към реалната алтернатива на техните системи за управление на пакети – ръчното компилиране, поне за критично важни приложения.

Втората цел е да подчертая, че каквато и дистрибуция на Linux да използвате (или даже каквато и да е BSD система), основата на приложенията в тях е обща. И са породени от движението за свободен софтуер в широкият смисъл на думата. А изхождайки от името е ясно, че всяко едно от тези приложения винаги е достъпно като изходен код, и могат да бъдат компилирани самостоятелно по универсалната за всички тях схема.

Като заключение не мога да не изразя своята признателност към Владимир Попов – всичко, изложено по-горе беше окончателно осмислено в процеса на кореспонденцията и личното общуване с него.



превод: Николай Ангелов

1собакит – планинска порода камъни, която се хвърля по кучета, докато ертефемитите (името им произлиза от съкращението RTFM) се хвърлят по нещастните потребители (А.Ф.).

2извинявайте за тавтологията (А.Ф.)

3development tools – така са наречени поне в дистрибуцията Fedora Core 2.

4перифраза на латинската сентенция 'homo hominem lupus est – човек за човека е вълк'

5явно на процеса на компилиране

6за повече детайли е необходимо да се обърнем към съответната страница man gcc за текущата версия на gcc (А.Ф.)



<< Slackware и Promise FastTrak 378 RAID контролер | PPPoE и HomeLan настройка >>