Programiranje na Linuxu, deo 1

S obzirom da je Linux kernel nastao kao projekat na fakultetu računarskih nauka, a pritom je pisan tako da imitira funkcionalnost operativnog sistema Unix koji je takođe pravljen za inženjere od strane inženjera, ne čudi da je programiranje na Linuxu kao „dobar dan“, odnosno da gotovo sve aktuelne Linux distribucije dolaze sa manjom ili većom podrškom za razvoj softvera odmah po instalaciji. Pozabavimo se ovom tematikom malo detaljnije i pogledajmo šta sve programerima stoji na raspolaganju ako ih interesuje Linux kao platforma

Ivan Todorović Ukoliko se bojite da nastavite sa čitanjem – nemojte. Za razumevanje ovog teksta poželjno je da imate nekakvo predznanje iz programiranja i da znate kako da napišete i prevedete program na Windowsu, za šta je dovoljno i znanje koje se stiče u srednjoj školi. Pre nego što krenemo sa bilo kakvom pričom o programiranju na Linuxu, od velikog je značaja upoznati se sa pojmom GCC. Kada je Ričard Stolman pre skoro trideset godina inicirao razvoj Unixolikog operativnog sistema GNU (koji, o ironije, ni dan danas nije završen), znao je da jako bitno da postoji funkcionalan C kompajler ako misli da taj operativni sistem zaživi sa stanovištva softverske podrške. Otud i skraćenica GCC, koja je prvobitno značila GNU C Compiler.  Tokom godina, podrška se širila i na ostale jezike, pa sada GCC zvanično uključuje C, C++, Objective-C, Objective-C++, Javu, Fortran, Adu i Googleov Go, uz malo manje zvaničnu podršku za Paskal, Modula-2, Modula-3 i nekolicinu manje popularnih jezika. GCC sada zvanično znači GNU Compiler Collection, iako komanda „gcc“ na podržanim operativnim sistemima i dalje podrazumevano poziva C kompajler.

Kada je Stolman saznao za Linux kernel i nakon što se pojavila prva distribucija sastavljena od Linux kernela i GNU alata (otud i naziv GNU/Linux koji se često sreće), GCC je postao podrazumevani set kompajlera na ovoj platformi. Ipak, GCC nije puka kolekcija kompajlera, već nudi i odgovarajuće programske biblioteke na koje se oslanjaju programi pisani u određenim jezicima. Recimo, da biste u jeziku C mogli da koristite dinamičku alokaciju i dealokaciju memorije, potrebno je da postoji odgovarajuća C biblioteka koja nudi potrebne funkcije i sve pozadinske mehanizme koji će to obezbediti. GCC nudi mogućnost kreiranja izvršnih fajlova koji su statički ili dinamički linkovani. Prva opcija znači da će rezultujući izvršni fajl sadržati sve potrebne biblioteke u sebi, dok druga varijanta podrazumeva se program oslanja na deljene sistemske biblioteke (na primer, DLL fajlovi na Windowsu). Isto tako, kao deo GNU projekta postoji i GNU Classpath, open-source implementacija Java biblioteke na koju se oslanjaju programi napisani u GCC Javi.

Osim podrške za razne programske jezike, izuzetno je važno što GCC podržava ogroman broj hardverskih arhitektura na koje je portovan i čije izvršne fajlove može da proizvede. Tako osim programa koji će se vrteti na uobičajenom x86 i x86-64 Linuxu, pomoću GCC-a možete proizvesti izvršne fajlove za mnoge arhitekture počevši od poznatih ARM i PowerPC računara, pa sve do teške egzotike koju verovatno nikada nećete videti uživo ili se odavno ne proizvodi.
S obzirom da GCC predstavlja softver otvorenog kôda i može se besplatno koristiti na mnogim operativnim sistemima (uključujući nebrojene Linux portove za razne hardverske arhitekture), izuzetno je popularan kao deo „toolchaina“ za razvoj softvera u embedded vodama, odnosno u izradi računara usko specifične namene (od video plejera koji se ugrađuju u televizore pa do telefona koji koriste operativni sistem Android, a koji se takođe kompajlira pomoću GCC-ovog C i C++ kompajlera).

Kada GCC ne bi nudio ovakvu atraktivnu kombinaciju cene od nula dolara i izuzetne podrške za ogroman broj hardverskih arhitektura, razvoj ne-PC uređaja bio bi i skuplji i dugotrajniji, jer firme koje proizvode sopstvene komercijalne toolchainove nisu tako hitre u ispravci bagova i nadogradnji svojih proizvoda, dok se GCC baš zbog toga što ga održava open-source zajednica vinuo u nebesa i sada predstavlja jedno od najkvalitetnijih rešenja na tržištu. Naravno, open-source zajednicu ne čine hobisti koji nemaju druga posla u životu, već se mahom radi o profesionalcima koji već rade za velike kompanije ili ih iste sponzorišu jer u unapređenju GCC-a vide sopstveni interes. Svejedno, zbog toga se GCC tretira kao opšte dobro i zbog toga ima izuzetan „nevidljivi“ uticaj kako na savremeno računarstvo, tako i na „pridružene“ grane poput sveta prenosnih i mobilnih uređaja, pa čak i potrošačke elektronike. Pomenuli smo da je GCC deo kompleta formalno nazvanog GNU Toolchain. Toolchain u kompjuterskom žargonu označava skup alata („tool“) koji se uobičajeno izvršavaju automatizovano ili poluautomatizovano jedan za drugim, u “lancu” (chain). U ovom slučaju, GNU Toolchain označava skup svih alata u okviru GNU projekta koji se koriste u procesu kreiranja neke aplikacije – od preprocesiranja i prevođenja izvornog kôda pa do „sklapanja” gotovog izvršnog fajla ili dinamički povezive biblioteke. Iako onima koji su naviknuti na pisanje manjih aplikacija u nekom razvojnom okruženju, gde se EXE proizvodi jednostavnim pritiskom na neki taster, ovo može delovati kao bespotrebno komplikovanje, u realnosti je situacija daleko složenija jer se iza takvog procesa kriju brojni alati na koje pada ceo posao (preprocesor, kompajler, linker, a vrlo često asembler i dibager). Čak i moćna razvojna okruženja poput Visual Studija, Eclipsea i KDevelopa suštinski predstavljaju frontend za odgovarajući skup alata i brinu se za njihovo pravilno pozivanje i izvršavanje. Nećemo se temeljno baviti svim komponentama GNU Toolchaina, već smo ga pomenuli samo da se stekne okvirna slika o tome da kreiranje izvršnog programa ne podrazumeva samo kompajliranje, već su tu umešani i mnogi drugi alati.

Kako kreirati najjednostavniji program na Linuxu? Kao primer uzećemo programski jezik C. Najprostija metoda za pravljenje najjednostavnijih programa podrazumeva pisanje izvornog kôda u nekom od tekst editora (recimo Gedit ili Kate), snimanje fajla (npr. „program.c”) i pozivanje komande iz terminala: gcc program.c -o program Ovim pozivamo C kompajler kome navodimo ime izvornog fajla i kažemo da hoćemo da nam se izlazni fajl („output“) zove program. Ako smo pisali C++ a ne C kôd, fajlu ćemo nadenuti ekstenziju .cpp (za C++ izvorne fajlove često se koriste i ekstenzije .cc, .c++, .cxx i .C, ali se ipak držite uobičajenog .cpp). Pritom, umesto „gcc“ kompajlera pozvaćemo „g++“. Rezultat će biti izvršni fajl koga iz tekućeg direktorijuma možemo pozvati sa: ./program Ako pokušate ovaj program da izvršite na nekoj starijoj Linux distribuciji, vrlo je moguće da ćete biti „pozdravljeni“ greškom. O čemu se tačno radi? Naime, C kompajler podrazumevano kreira izvršni fajl tako da se oslanja na deljenu sistemsku C biblioteku (GNU/Linux varijanta se zove „glibc“). Verzija kompajlera koju ste koristili za pravljenje izvršnog programa, kao i prateća deljena C biblioteka mogu se značajno razlikovati od onoga što ćete zateći na nekoj starijoj distribuciji, što znači da izvršni program dobijen na ovaj način nećete moći da pokrećete na svim Linux distribucijama iako su namenjene istoj hardverskoj arhitekturi.

GCC nudi mogućnost takozvanog statičkog linkovanja izvršnih programa. Umesto da se vaš program oslanja na sistemsku C runtime biblioteku sa kojom će se pozivati u toku izvršavanja, moguće je u izvršni fajl ubaciti („linkovati“) statičku verziju ove biblioteke tako da će program biti potpuno samostalan i biće moguće pokretati ga na bilo kojoj Linux distribuciji koja podržava tu arhitekturu. U tom slučaju, gornji program ćemo prevesti i linkovati komandom: gcc –static -o program program.c Prvo što pada u oči jeste drastično veća veličina izvršnog fajla – sa desetak kilobajta skočila je na oko pola megabajta. Treba voditi računa da se dodavanjem ovog parametra ne uključuju baš sve statičke biblioteke koje će možda zatrebati vašem programu – recimo, ako ste u C programu uradili „#include <math.h>“, biblioteka sa matematičkim funkcijama neće biti automatski linkovana, već se mora proslediti dodatni parametar u obliku: gcc –static –lm -o program program.c

Autori GCC C kompajlera su se odlučili na ovaj potez kako bi sprečili da statički linkovan program naraste preko svake mere, jer broj dostupnih biblioteka nije mali. Inače, matematička biblioteka ima svoje konkretno ime i nalazi se u konkretnom folderu, ali se ona (i mnoge druge korisne biblioteke) često upotrebljava u C programima, pa zato postoje predefinisane skraćenice poput „-lm“ i druge. Korisnik može i ručno specificirati linkovanje neke nestandardne biblioteke, prosleđujući parametar „-l/putanja/do/fajla.a”, dok za standardne C biblioteke postoje pomenute ugrađene skraćenice.  Statičko linkovanje nije nešto za čim treba posezati bez preke potrebe. Dinamičko povezivanje sa deljenim bibliotekama u toku izvršavanja nekog programa nije izmišljeno tek tako i nudi brojne prednosti. Na prvom mestu, uzmimo primer jedne uobičajene Linux distribucije gde je 99% sistemskog softvera i alata napisano u C-u. Ako bi svaki od tih programa bio statički linkovan, potrošnja na disku bi otišla nebu pod oblake, jer bi svaki od izvršnih fajlova u sebi sadržao kopiju identične C runtime biblioteke.

Pritom ne zaboravimo da je Linux multitasking operativni sistem i da se u svakom trenutku u memoriji izvršavaju brojni procesi (najprostije rečeno za manje upućene, procesom se naziva program koji je u fazi izvršavanja). Ako bi imali deset aktivnih procesa koji su statički linkovani sa C runtime bibliotekom koja je velika skoro pola megabajta, samo na identičnim delovima koda koji se nalaze u memoriji imali bi potpuno nepotrebnu potrošnju od pet megabajta RAM-a. U današnje vreme to ne deluje kritično, ali zamislite da se ne radi o deset procesa već o stotinu, i da nije u pitanju samo C runtime biblioteka već mnoge druge sistemske i aplikativne biblioteke koje mogu biti instalirane na sistemu. Jasno je da ovakav pristup u najvećem broju slučajeva nije opravdan i da je znatno bolje rešenje imati deljenu biblioteku koju istovremeno može da poziva više programa. U tom slučaju, u RAM-u će biti samo jedna a ne deset kopija ove biblioteke, čime se može napraviti značajna ušteda. Ne treba zaboraviti ni aspekt sigurnosti. Recimo da je u nekoj uslužnoj biblioteci pronađen sigurnosni propust. Programeri koji održavaju tu biblioteku korigovaće njen izvorni kôd, rekompajlirati je i time je problem rešen. Međutim, šta ako vi već imate gomilu programa statički linkovanu sa starom, nebezbednom verzijom biblioteke? To znači da vas čeka posao relinkovanja svih vaših programa kako bi uključivali novu verziju. Ali, ako je programer napravio novu verziju deljene biblioteke, dovoljno je da je instalirate u odgovarajući sistemski folder. Tada će svi vaši programi, koje ste prevedeni tako da dinamički pozivaju deljenu biblioteku umesto da u sebi uključuju njenu „statičnu“ verziju, automatski biti oslobođeni sigurnosnog propusta jer će koristiti novu verziju biblioteke.

Jedina očigledna mana dinamičkog linkovanja je što Linux distribucija na kojoj mislite da pokrećete vaš program mora sadržati istu verziju dinamičke biblioteke (ili sličnu a dovoljno kompatibilnu) kao na računaru na kome ste preveli program. Kod Linux distribucija za stone računare ovo najčešće nije veliki problem, ali ako radite takozvano kros-kompajliranje, to može biti daleko napornije (kros-kompajliranje je proces pisanja i prevođenja programa za jednu platformu na nekoj drugoj; recimo pisanje programa za ARM čipove na računaru Intel arhitekture). U takvim slučajevima, problem ćete najlakše rešiti statičkim linkovanjem i biti oslobođeni briga o pokretanju vašeg programa. Druga mana, koju je gotovo nemoguće primetiti, jeste sporije pokretanje programa koji se oslanjaju na deljene biblioteke, jer je potrebno vreme sa režijske poslove pronalaženja odgovarajuće entry tačke deljene biblioteke u memoriji, evidentiranje novog procesa koji je koristi i slično. Statički linkovan program se pokreće drastično brže jer ne zavisi od drugih biblioteka, ali napominjemo da se radi o razlici koja je neprimetna čak i na jako sporim računarima i u realnim primenama je ne treba koristiti kao argument u prilog statičkom linkovanju.

U svakom slučaju, dinamičko linkovanje ima vrlo konkretne prednosti i treba ga se držati kad god je to moguće. Statičko linkovanje je tu samo za „zlu ne trebalo“. Inače, jedan od osnovnih razloga zašto su nastali paket-menadžeri na Linuxu jeste da se omogući jednostavno instaliranje programa u zavisnosti na koje se deljene biblioteke ostavlja. U jednom Ubuntuu danas je dovoljno da otkucate ime aplikacije u Synaptic package manageru i kliknete na „Install“. Tada će iz instalacionog paketa biti pročitani imena i verzije svih biblioteka i programa na koje se aplikacija oslanja, onda će biti provereno da li su te zavisnosti instalirane i da li im verzije odgovaraju, i sve će biti obavljeno bez vašeg daljeg zalaganja. Ručna briga o prisustvu i verzijama deljenih biblioteka je daleko, daleko napornija. Za kraj U ovom tekstu samo smo zagrebali tematiku programiranja na Linuxu. U nastavku pozabavićemo se alatom GNU Make koji automatizuje kompajliranje i linkovanje većih projekata, kao i nekolicinom popularnih razvojnih okruženja pomoću kojih je razvoj softvera daleko udobniji i efikasniji nego u školskom primeru opisanom u ovom tekstu. Stoga, budite uz Benchmark i očekujte nastavak ove priče uskoro…  

Ostani u toku

Prijavi se na newsletter listu i jednom nedeljno cemo ti poslati email sa najnovijim testovima i vestima iz sveta tehnologije.

Hvala!

Uspešno ste se prijavili na na naš newsletter! Proverite vaš email nalog kako bi potvrdili prijavu.

Možda vam se svidi