Planethäftet, © F. Kilander, DSV, 1992--1996.
[Top]

Programutveckling under Unix


Skriva källkod

Att skriva och utveckla program under UNIX är inte lika ombonat som i en helintegrerad PC-miljö. Det finns en kraftfull funktionalitet i UNIX-miljön, som vid första anblicken kan te sig komplex och ohanterlig. T ex så måste man välja själv vilken editor man skall använda för att redigera sin programkod, vilken metod man skall använda för att kompilera programmen och vilken debugger som passar bäst för uppgiften. Det finns också stöd för revisionskontroll och utvärdering av prestanda.

En av de bättre editorerna för programkod är Emacs. I Emacs får man stöd för syntaxkänslig formatering av koden, visning av stängande parenteser och kompilering utan att behöva gå ur Emacs. Kompileringsfel visas i ett Emacs-fönster och det finns en funktion i Emacs som hoppar i källkoden till den plats där kompilatorn rapporterade ett fel. Detta stöd är för närvarande bäst utvecklat för C-programmering, men finns även helt eller delvis för andra språk.

Kompilera, länka och avlusa

För att kompilera sitt program kan man med fördel använda programmet make. Make arbetar genom att uppfylla mål. Det har inbyggda regler för hur dessa mål skall uppfyllas. Mål kan ges i en s k Makefile eller i enklare fall direkt på kommandoraden. Ett vanligt mål är att bygga ett program genom att kompilera en eller flera källkodsfiler, länka ihop dem och skriva resultatet som en exekverbar fil. Om flera filer ingår i ett program ser make till att endast de källkodsfiler som är ändrade kompileras om.

För avlusning (debugging) finns flera alternativ: ctrace (endast C-kod), dbx (källkodsdebugger), dxdb (fönsterversion av dbx). En annan kraftfull debugger är gdb (GNU debugger). Ett vanligt krav för att dessa skall kunna användas är att kompilatorn har lagt in utökad symbolinformation. För C, Pascal och Fortran innebär det att programmet skall kompileras utan optimering och med flaggan -g. T ex:

Pascal:       pc  -g -o foo foo.p

C:            cc  -g -o foo foo.c

Fortran:      f77 -g -o foo foo.f

Effektivisera

När man har kommit så långt att man intresserar sig för effektiviteten kan man använda sig av programmen prof, pixie eller pixstats. Dessa kräver också speciella kompileringargument, oftast -p. Kompilatorn lägger då in kod som loggar antalet anrop till varje procedur, hur mycket cpu-tid som gått åt och en del annat. Resultatet läggs i binärfilen mon.out som sedan analyseras med programmet prof. Man får då en lista med statistik över programmets beteende och kan utläsa vilken funktion eller procedur som kanske behöver göras effektivare.

Effektivitetsaspekter är sällan intressanta för mindre program som körs vid enstaka tillfällen. Det är intressant om det är ett program med mycket bearbetning eller (oavsett storlek) om det skall användas ofta av många användare. För interpreterande programspråk som t ex Tcl/Tk kan effektiviteten många gånger vara avgörande för hur användbart programmet blir. Att skriva effektiva program är en hel vetenskap i sig, men några enkla tumregler kan ändå hjälpa en bit på vägen.

Välj effektiva algoritmer. Detta kan inte nog understrykas. En bra algoritm är fortfarande en bra algoritm även om datorn är långsam. Det är väl spenderad tid att anstränga sig en smula redan i programmets designfas. Sortering är ett bra exempel på detta. Bubblesort fungerar på pyttesmå datamängder men blir snabbt sämre när förväntningarna höjs.

Lita på statistiken. Använd profileringsverktygen för att ta reda på var programmet spenderar sin mesta tid. Börja där.

Optimera det värsta stället först. Försök inte förbättra allting på en gång. Optimering tar tid och kan i många fall göra programmet svårare att underhålla. Gör en punktinsats och mät igen, det kan hända att det räcker för acceptabla prestanda.

Titta i looparna. Som regel är det inuti looparna som man kan göra de största vinsterna. Går det att flytta någonting utanför loopen? Måste man använda en loop inuti en annan loop? Går det att hoppa ur om loopen blir klar i förtid? Är någon beräkning i loopen oberoende av loopvariabeln, dvs invariant? Finns det loopar som är dolda i procedureanrop, t ex strängjämförelser?

Minneshantering. Dynamisk allokering och avallokering av minne tar ofta relativt lång tid. I vissa situationer kan det vara effektivare (men också arbetsammare) att allokera ett stort stycke minne och själv programmera hur det skall fördelas.

Flyttalsaritmetik. Matematiska beräkningar med flyttal är ibland långsammare än beräkningar på heltal. Detta blir dock ett allt mindre problem i och med att modern hårdvara för flyttalsoperationer är lika snabb som för heltal. Det hindrar dock inte hackers att krama ur de sista mikrosekunderna ur en snäv loop genom att använda heltal och ett fixt antal decimaler.

Programmera i grupp

Om ett program med många källkodsfiler skall utvecklas av flera personer samtidigt är det praktiskt och riktigt att använda sig av ett revisionskontrollsystem. SCCS (Source Code Control System) och RCS (Revision Control System) är sådana system. Revisionssystemet förhindrar att flera personer samtidigt redigerar samma källkodsfil. Det håller också reda på vilka versioner som finns av de olika filerna vilket gör det enkelt att gå tillbaka till en tidigare version om en ändring visade sig vara olämplig eller inte helt färdig.

Arbetsmodellen mot revisionssystemet går ut på att den som skall arbeta med filen kvitterar ut den från systemet, gör sina ändringar, och sedan lämnar tillbaka filen till revisionskontrollsystemet.