V. fejezet - Nyílt forráskódú rendszerek fejlesztése

Tartalom
V.1. A Linux rendszer felépítése
V.1.1. A Unix története
V.1.2. Az Open Source programkészítési modell
V.1.3. A Linux operációs rendszer
V.1.4. Linux disztribúciók
V.1.5. X Window System
V.1.6. A beágyazott Linux
V.2. A GCC fordító
V.2.1. A GCC keletkezése
V.2.2. A forditás lépései GCC-vel
V.2.3. Host és Target
V.2.4. A GCC gyakran használt opciói
V.2.5. A make segédprogram
V.2.6. A gdb debugger (hibakereső/nyomkövető)
V.3. Posix C, C++ rendszerkönyvtárak
V.3.1. stdio.h
V.3.2. math.h
V.3.3. stdlib.h
V.3.4. time.h
V.3.5. stdarg.h
V.3.6. string.h
V.3.7. dirent.h
V.3.8. sys/stat.h
V.3.9. unistd.h

V.1. A Linux rendszer felépítése

Könyvünk eddigi részében főleg Microsoft Windows alapú programfejlesztéssel foglalkoztunk. Azonban más sikeres szoftverplatformok is léteznek, amelyek manapság elterjedtek. Ezek egyik irányvonala a Unix, amelynek programrészletei, elgondolásai, megoldásai, ötletei be lettek építve a Windows-ba. A további részben a különbségekről és hasonlóságokról lesz szó.

V.1.1. A Unix története

A Unix története 1969-ben kezdődik. Két programozó (Ken Thompson és Dennis Ritchie) a Bell Laboratóriumban nem volt megelégedve a használaton kívüli DEC PDP-7-es operációs rendszerével. Elhatározták, hogy készítenek egy interaktív, többfelhasználós, többfeladatos operációs rendszert a gépre, amelyen a fő alkalmazás a programok fejlesztése. A rendszert el is neveztél UNICS-nak, amiből egyszer csak UNIX lett. Az operációs rendszert BCPL (B) nyelven készítették. 1971-re el is készült, szövegszerkesztőt, assembler fordítót, a dokumentáláshoz szükséges szövegformázó eszközöket is tartalmazott. 1972-ben a B nyelvből Ritchie megalkotta a C nyelvet. A C nyelv megalkotásáról többen azt tartották, hogy lehetetlen olyan magas szintű nyelvet készíteni, amely a különböző bitszélességű processzorokon és memóriaszervezésen is működni képes. A C nyelv használata az operációs rendszert portolhatóvá tette, ugyanis egy új utasításkészletű CPU estén csak az adott CPU assembler-ét kellett megírni, utána az operációs rendszert és a felhasználói programokat le lehetett fordítani az új hardverre. 1975-ben az egyetemeknek, többek közt a Berkeley-nek is odaadták a Unix-ot. Az egyetemen továbbfejlesztették a forrást BSD néven, és a továbbfejlesztések, pl. az Internet kezelését lehetővé tevő Sockets programcsomag, onnan kerültek vissza a Bell Labs utód USG UNIX-ba. A Unix a tudományos társadalom eszközévé vált.

A Unix rendszer fontos eleme a szabványosítás. A kezdetektől fogva többféle rendszer létezett, ahhoz, hogy a programok minden rendszeren futtathatók legyenek, a rendszerhívásokat (pl. fájl megnyitása, karakter kiírása a konzolra) szabványosították. A szabványnak megfelelő rendszerhívásokat tartalmazó programok futtathatók a szabványos Unix operációs rendszeren. A szabványt 1988-ban POSIX-nek nevezték. Többféle szervezet is foglalkozott a szabványosítással, és a UNIX márkanév is gazdáról-gazdára vándorolt. Ilyenek az X/Open, OSF, The Open Group - manapság ezekből a csoportokból megalakult az Austin Common Standards Revision Group, amelynek több, mint 500 tagja van, például az iparból, a kormányzati szférából, a nyílt forráskódú társadalomból. Unix-nak azok a rendszerek tekinthetők, amelyek ennél a szervezetnél található The Open Group-nál szabványosíttatják magukat (ennek természetesen költségei is vannak). Unix-nak tekinthető például a Mac OS X, a HP-UX, a Sun Solaris és az IBM AIX. Nem tekinthető Unix-nak a BSD, annak ellenére, hogy a szabványos rendszerfüggvényeket (Posix - IEEE-1003.1) BSD-n tesztelik.

V.1.2. Az Open Source programkészítési modell

A BSD-nél megjelent egy új elv: a nyílt forráskód elve. A nyílt forráskód annyit jelent, hogy az operációs rendszer (és segédprogramjainak) lefordításához szükséges összes forrás legálisan rendelkezésre áll, módosítható, szabadon tanulmányozható, és másnak átadható. Ugyanezt az elvet vallotta magáénak a GNU projekt, amely jó minőségű, nyílt forráskódú eszközöket készít Unix típusú operációs rendszerekhez. Legismertebb projektjük az 1987-ben indult C fordító, amit GCC-nek hívunk, azóta több nyelvet (C, C++, Fortran) is támogat (Gnu Compiler Collection). A programokat a GPL (General Public License) alatt terjesztik. A GPL és a BSD nyílt forráskódú licenszek közti különbség, hogy a GPL licensszel ellátott termékekből előállított termékhez kötelező forrásprogramot adni, a BSD-nél azonban nem.

V.1.3. A Linux operációs rendszer

A kezdeti Unix-ok (és a BSD-k) csak nagyszámítógépeken működtek. Ugyanis az úgynevezett „személyi számítógép”-ekben az 1980-as években nem volt akkora memória, hogy az operációs rendszer magját (a kernelt) betöltse. Az IBM PC ebben változást hozott: erre a Microsoft is árult Unix-ot Xenix néven, de nem volt sikeres. Jobb próbálkozás volt az Andrew Tanenbaum professzor által készített minix, amely a 80-as évek Pc-in (8086 és 80286-os CPU-val) futó Unix klón, oktatási céllal készült. A Microsoft Windows sem volt túl sikeres (gyors) ezeken a CPU-kon, ekkor az Intel nagy fejlesztésének eredményeként kijött a 80386-os CPU-val, amely 32-bites memóriakezeléssel, valódi védett üzemmóddal támogatta a többfeladatos operációs rendszereket. Ennek a processzornak a memóriakezelésével foglalkozott egy finn egyetemista, Linus Torvalds. Egy internetes levelezőlistán keresztül közzétette a fejlesztéseket, néhányan segítettek neki, és egyszer csak elkészült egy Unix szerű operációs rendszer kernelje, amit valaki – hasonlóan az UNICS-UNIX módosításhoz - Linux-nak nevezett el. GNU GPL licensszel készült, és az akkoriban (a 90-es évek közepén-végén) a nagyközönség számára elérhető Interneten keresztül terjesztették, de sok internetes kiszolgáló operációs rendszerének is a Linux-ot választották, mert egy relatíve olcsó PC-n néha jobb teljesítményt nyújtott, mint az 1-2 nagyságrenddel drágább úgynevezett nagyszámítógépeken a szabványosított Unix-ok. Meg kell jegyeznünk, hogy a Linux nem nevezhető UNIX-nak. A Linux bár megfelel a Posix szabványoknak, nem fizetett a licenszért, így (akárcsak a BSD) nem tekintendő Unix-nak, csak „Unix-like”-nak. A Linux szorosabb értelemben csak a rendszermagot (kernel) jelenti, de önmagában a kernel nem üzemképes. C fordító a Linux-ban a már említett GCC, a legtöbb segédprogram (utility) a Gnu projektből származik, így a GNU projekt kérésére a Linuxot GNU/Linux-nak nevezik. A Linux kernel definíció szerint „úgy van megírva, hogy GCC-vel lefordítható legyen”.

V.1.4. Linux disztribúciók

Disztribúción egy összerakott, üres háttértárral ellátott számítógépre feltelepíthető rendszert értünk. Ennek többféle segédprogramot kell tartalmaznia: lemezpartícionáló, betöltő (boot) készítő, csomagválasztó (más segédprogramok és beállítások kellenek egy internetes kiszolgálóhoz, mint egy szövegszerkesztésre használt munkaállomáshoz), konfiguráció beállító. Több ilyen disztribúció is létezik, és bármelyik disztribúció felhasználói szerint az általuk használt a legjobb, a többi rossz, és annak a felhasználói „hozzá nem értő kezdők”. Az összes disztribúcióban ugyanaz a Linus nevével fémjelzett kernel található, és természetesen a GCC fordító valamelyik verziója. A rendszerindító szkriptek és a telepíthető programcsomagokat kezelő „package manager” minden disztribúcióban más és más. Néhány szoftverekkel foglalkozó cég is felkarolt egy-egy disztribúciót, ezzel megoldva a terméktámogatási kérdést. Ugyanis az ingyenes disztribúciókhoz nem jár terméktámogatás, a bankok, internetszolgáltatók nem használhatnak támogatás nélküli termékeket. A támogatással vásárolható disztribúció legtöbb esetben ugyanaz, amit le is lehet tölteni a disztribúció honlapjáról. Létezik adatbáziskezelővel ellátott nagyvállalati disztribúció is, természetesen megfelelő áron.

V.1.5. X Window System

Amikor a Unix készült, a grafikus megjelenítők még nem voltak elterjedtek. Még a soros kábellel a számítógéphez kapcsolt alfanumerikus display is (terminál) újdonságnak számított. Emiatt a Unix (és a Linux is) a felhasználóval a terminálon tartja a kapcsolatot, alapvetően egy parancsértelmező (shell) programon keresztül. Ha úgy gondoljuk, hogy ez az architektúra – egy központi gép sok rákapcsolt terminállal – elavult, nézzünk meg egy felhő alapú alkalmazást: soros kábel helyett internet, egy számítógép helyett sok számítógép, logikailag egy számítógépként látszik. A Unix-os világban is megjelent azonban a grafikus felhasználói felület (GUI), mint a számítógép kezelésének könnyen elsajátítható eszköze. Nem a kétbetűs parancsot kell megjegyezni, amely nem változott az utóbbi 43 évben, hanem azt, hogy hova kell kattintani az egérrel az adott művelethez, és ezt a gyártók (pl. Microsoft Office) előszeretettel szervezik át minden új verzióban. A Unix-os GUI elnevezése X Window System, amely alatt a jól ismert alapfogalmak jelennek meg: egér, kurzor, ikon, kattintás. Az X Window System tulajdonságai:

  • nem része az operációs rendszernek, csak egy alkalmazás. A Unix nélküle is üzemképes

  • network-transparent: a futó alkalmazás és a megjelenítés közt elég, ha internetes (TCP/IP) kapcsolat van.

  • önmagában nincs felülete, egy grafikus asztalból és egy egérmutatóból áll. Szükséges a használathoz egy Window Manager (WM) program is, amely az ablakokat és az ikonokat kezeli, az ikonok mögött található alkalmazásokat elindítja.

  • létezik nyílt forráskódú változat is, a GPL operációs rendszereken való használatra. Ezt az X.org internetes oldalon találjuk, ha esetleg le akarnánk fordítani. Grafikus kártya meghajtóprogramokat nem tartalmaz, ezeket az xfree86.org oldalon találjuk. Az xfree86-ot a Linux disztribúciók tartalmazzák, némelyik már az telepítést is grafikusan végzi el.

V.1.6. A beágyazott Linux

A Linux, miután C nyelven íródott, más CPU-ra is lefordítható. A TCP/IP támogatás, a grafika (X Window System), a széles kiszolgáló választék (web, fájl, nyomtatás, adatbázis) ideálissá teszi az internetre kapcsolt termékek (ADSL router, network hard drive, DVD és szórakoztató eszközök: médialejátszó, mobiltelefon, tablet, ipari automatizálás) kis költséggel testreszabható operációs rendszerévé. Egyetlen hátránya: az eszközök méretéhez és energiaellátásához (hordozható eszközök esetén akkumulátor) képest viszonylag nagy memóriaigény. Legismertebb formája a Google mobiltelefonos operációs rendszere, az Android, amely egy módosított Linux kernelen futó módosított GNU felhasználói programok segítségével készült.

V.2. A GCC fordító

A GCC fordító a GNU projekten belül, a GNU operációs rendszerhez készült. Tartalmaz Fortran, Java, C, C++, Ada felületeket, valamint szabványos fejállomány gyűjteményt a programok fordításához, és könyvtárakat a szerkesztéshez/futtatáshoz/hibakereséshez. A GNU rendszerből szinte minden el is készült, hogy egy Unix-like operációs rendszerré váljon – a kernelt kivéve. Az alkalmazások: a fordító, a shell, a segédprogramok viszont jól jöttek a Linux-nak, mert ott csak a kernel készült el.

V.2.1. A GCC keletkezése

A GCC fordító tervezésénél és a kódolásánál a fő szempont egy jó minőségű, ingyenes eszköz készítése volt. Hasonló cél a különféle számítógép architektúrák támogatása. A GNU rendszer is az interneten közreműködő fejlesztők munkájaként alakult ki: a fordító forrásprogramja mindenki számára elérhető, ha valaki javítást vagy bővítést készít hozzá, és elküldi a fejlesztést koordináló szervezetnek, az általa fejlesztett kód is belekerül a rendszerbe.

V.2.2. A forditás lépései GCC-vel

A GCC fordítóprogram a IV.1.1. szakasz fejezetben ismertetett módon .C forrásprogramból készít futtatható állományokat, a Windows-os Microsoft fordítóval szemben úgy, hogy a .C fájlok gépi kódú utasításokat tartalmazó assembly nyelvű szövegfájlokra fordulnak le, amit aztán egy assembler fordít le .obj fájlokra. Nézzük az alábbi, lehető legegyszerűbb mintaprogramot, ezt egy szövegszerkesztővel írtuk az x.c nevű fájlba:

#include <stdio.h>
 
int main(void) {
  int i;
  i=0;
  i++;
  printf("%i\n",i);
  return 0;
}

Erre a programra először az előfeldolgozót (preprocesszor) engedjük rá, az #include-okat és a #define-okat oldja fel: a #define-t behelyettesíti (ebben a programban nem kell) és az #include-nál bemásolja az ott található header-t. A legegyszerűbb header-t választottuk, amely a kiíratáshoz kell. A preprocesszált c forrás az x.i állományba kerül:

# 1 "x.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "x.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 40 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 59 "/usr/include/sys/cdefs.h" 3 4
# 1 "/usr/include/machine/cdefs.h" 1 3 4
# 60 "/usr/include/sys/cdefs.h" 2 3 4
# 1 "/usr/include/sys/cdefs_elf.h" 1 3 4
# 62 "/usr/include/sys/cdefs.h" 2 3 4
# 41 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/sys/featuretest.h" 1 3 4
# 42 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/sys/ansi.h" 1 3 4
# 35 "/usr/include/sys/ansi.h" 3 4
# 1 "/usr/include/machine/int_types.h" 1 3 4
# 47 "/usr/include/machine/int_types.h" 3 4
typedef signed char __int8_t;
typedef unsigned char __uint8_t;
typedef short int __int16_t;
typedef unsigned short int __uint16_t;
typedef int __int32_t;
typedef unsigned int __uint32_t;
typedef long int __int64_t;
typedef unsigned long int __uint64_t;
typedef long __intptr_t;
typedef unsigned long __uintptr_t;
# 36 "/usr/include/sys/ansi.h" 2 3 4
typedef char * __caddr_t;
typedef __uint32_t __gid_t;
typedef __uint32_t __in_addr_t;
typedef __uint16_t __in_port_t;
typedef __uint32_t __mode_t;
typedef __int64_t __off_t;
typedef __int32_t __pid_t;
typedef __uint8_t __sa_family_t;
typedef unsigned int __socklen_t;
typedef __uint32_t __uid_t;
typedef __uint64_t __fsblkcnt_t;
typedef __uint64_t __fsfilcnt_t;
# 43 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/machine/ansi.h" 1 3 4
# 75 "/usr/include/machine/ansi.h" 3 4
typedef union {
 __int64_t __mbstateL;
 char __mbstate8[128];
} __mbstate_t;
# 45 "/usr/include/stdio.h" 2 3 4
typedef unsigned long size_t;
# 1 "/usr/include/sys/null.h" 1 3 4
# 51 "/usr/include/stdio.h" 2 3 4
typedef __off_t fpos_t;
# 74 "/usr/include/stdio.h" 3 4
struct __sbuf {
 unsigned char *_base;
 int _size;
};
# 105 "/usr/include/stdio.h" 3 4
typedef struct __sFILE {
 unsigned char *_p;
 int _r;
 int _w;
 unsigned short _flags;
 short _file;
 struct __sbuf _bf;
 int _lbfsize;
 void *_cookie;
 int (*_close)(void *);
 int (*_read) (void *, char *, int);
 fpos_t (*_seek) (void *, fpos_t, int);
 int (*_write)(void *, const char *, int);
 struct __sbuf _ext;
 unsigned char *_up;
 int _ur;
 unsigned char _ubuf[3];
 unsigned char _nbuf[1];
 struct __sbuf _lb;
 int _blksize;
 fpos_t _offset;
} FILE;
extern FILE __sF[];
# 214 "/usr/include/stdio.h" 3 4
void clearerr(FILE *);
int fclose(FILE *);
int feof(FILE *);
int ferror(FILE *);
int fflush(FILE *);
int fgetc(FILE *);
int fgetpos(FILE * __restrict, fpos_t * __restrict);
char *fgets(char * __restrict, int, FILE * __restrict);
FILE *fopen(const char * __restrict , const char * __restrict);
int fprintf(FILE * __restrict , const char * __restrict, ...)
    __attribute__((__format__(__printf__, 2, 3)));
int fputc(int, FILE *);
int fputs(const char * __restrict, FILE * __restrict);
size_t fread(void * __restrict, size_t, size_t, FILE * __restrict);
FILE *freopen(const char * __restrict, const char * __restrict,
     FILE * __restrict);
int fscanf(FILE * __restrict, const char * __restrict, ...)
    __attribute__((__format__(__scanf__, 2, 3)));
int fseek(FILE *, long, int);
int fsetpos(FILE *, const fpos_t *);
long ftell(FILE *);
size_t fwrite(const void * __restrict, size_t, size_t, FILE * __restrict);
int getc(FILE *);
int getchar(void);
void perror(const char *);
int printf(const char * __restrict, ...)
    __attribute__((__format__(__printf__, 1, 2)));
int putc(int, FILE *);
int putchar(int);
int puts(const char *);
int remove(const char *);
void rewind(FILE *);
int scanf(const char * __restrict, ...)
    __attribute__((__format__(__scanf__, 1, 2)));
void setbuf(FILE * __restrict, char * __restrict);
int setvbuf(FILE * __restrict, char * __restrict, int, size_t);
int sscanf(const char * __restrict, const char * __restrict, ...)
    __attribute__((__format__(__scanf__, 2, 3)));
FILE *tmpfile(void);
int ungetc(int, FILE *);
int vfprintf(FILE * __restrict, const char * __restrict, __builtin_va_list)
    __attribute__((__format__(__printf__, 2, 0)));
int vprintf(const char * __restrict, __builtin_va_list)
    __attribute__((__format__(__printf__, 1, 0)));
char *gets(char *);
int sprintf(char * __restrict, const char * __restrict, ...)
    __attribute__((__format__(__printf__, 2, 3)));
char *tmpnam(char *);
int vsprintf(char * __restrict, const char * __restrict,
    __builtin_va_list)
    __attribute__((__format__(__printf__, 2, 0)));
int rename (const char *, const char *);
# 285 "/usr/include/stdio.h" 3 4
char *ctermid(char *);
char *cuserid(char *);
FILE *fdopen(int, const char *);
int fileno(FILE *);
void flockfile(FILE *);
int ftrylockfile(FILE *);
void funlockfile(FILE *);
int getc_unlocked(FILE *);
int getchar_unlocked(void);
int putc_unlocked(int, FILE *);
int putchar_unlocked(int);
int pclose(FILE *);
FILE *popen(const char *, const char *);
# 332 "/usr/include/stdio.h" 3 4
int snprintf(char * __restrict, size_t, const char * __restrict, ...)
    __attribute__((__format__(__printf__, 3, 4)));
int vsnprintf(char * __restrict, size_t, const char * __restrict,
     __builtin_va_list)
    __attribute__((__format__(__printf__, 3, 0)));
int getw(FILE *);
int putw(int, FILE *);
char *tempnam(const char *, const char *);
# 361 "/usr/include/stdio.h" 3 4
typedef __off_t off_t;
int fseeko(FILE *, __off_t, int);
__off_t ftello(FILE *);
int vscanf(const char * __restrict, __builtin_va_list)
    __attribute__((__format__(__scanf__, 1, 0)));
int vfscanf(FILE * __restrict, const char * __restrict, __builtin_va_list)
    __attribute__((__format__(__scanf__, 2, 0)));
int vsscanf(const char * __restrict, const char * __restrict,
    __builtin_va_list)
    __attribute__((__format__(__scanf__, 2, 0)));
# 398 "/usr/include/stdio.h" 3 4
int asprintf(char ** __restrict, const char * __restrict, ...)
    __attribute__((__format__(__printf__, 2, 3)));
char *fgetln(FILE * __restrict, size_t * __restrict);
char *fparseln(FILE *, size_t *, size_t *, const char[3], int);
int fpurge(FILE *);
void setbuffer(FILE *, char *, int);
int setlinebuf(FILE *);
int vasprintf(char ** __restrict, const char * __restrict,
    __builtin_va_list)
    __attribute__((__format__(__printf__, 2, 0)));
const char *fmtcheck(const char *, const char *)
    __attribute__((__format_arg__(2)));
FILE *funopen(const void *,
  int (*)(void *, char *, int),
  int (*)(void *, const char *, int),
  fpos_t (*)(void *, fpos_t, int),
  int (*)(void *));
int __srget(FILE *);
int __swbuf(int, FILE *);
static __inline int __sputc(int _c, FILE *_p) {
 if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n'))
  return (*_p->_p++ = _c);
 else
  return (__swbuf(_c, _p));
}
# 2 "x.c" 2
int main(void) {
  int i;
  i=0;
  i++;
  printf("%i\n",i);
  return 0;
}

A példa méretéből látható, hogy egy include több másik include-t is behívhat. A preprocesszált forrás assembly-re lefordítva, az x.s fájlban:

    .file    "x.c"
    .section    .rodata
.LC0:
    .string    "%i\n"
    .text
.globl main
    .type    main, @function
main:
.LFB3:
    pushq    %rbp
.LCFI0:
    movq    %rsp, %rbp
.LCFI1:
    subq    $16, %rsp
.LCFI2:
    movl    $0, -4(%rbp)
    incl    -4(%rbp)
    movl    -4(%rbp), %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    ret
.LFE3:
    .size    main, .-main
    .section    .eh_frame,"a",@progbits
.Lframe1:
    .long    .LECIE1-.LSCIE1
.LSCIE1:
    .long    0x0
    .byte    0x1
    .string    "zR"
    .uleb128 0x1
    .sleb128 -8
    .byte    0x10
    .uleb128 0x1
    .byte    0x3
    .byte    0xc
    .uleb128 0x7
    .uleb128 0x8
    .byte    0x90
    .uleb128 0x1
    .align 8
.LECIE1:
.LSFDE1:
    .long    .LEFDE1-.LASFDE1
.LASFDE1:
    .long    .LASFDE1-.Lframe1
    .long    .LFB3
    .long    .LFE3-.LFB3
    .uleb128 0x0
    .byte    0x4
    .long    .LCFI0-.LFB3
    .byte    0xe
    .uleb128 0x10
    .byte    0x86
    .uleb128 0x2
    .byte    0x4
    .long    .LCFI1-.LCFI0
    .byte    0xd
    .uleb128 0x6
    .align 8
.LEFDE1:
    .ident    "GCC: (GNU) 4.1.3 20080704"

Ebben is felismerhető a programrészletünk: pl. az i++-ból incl -4(%rbp) lett. A fordító a verziószámát is beletette, de csak szövegként. Ez a szövegrészlet szerkesztés után is benne marad a futtatható állományban.

Alaphelyzetben, külön opció használata nélkül ezek a fájlok törlődnek, és a C állomány mellett csak egy futtatható fájl marad. Ha nem adjuk meg a nevét, a.out lesz. Ha meg szeretnénk adni a futtatható fájl nevét, az –o fájlnév opcióval kell indítani a fordítást:

Parancs

eredmény

futtatás

gcc x.c

a.out

./a.out

gcc –o x x.c

x

./x

gcc –save-temps –o x x.c

x.i, x.s, x.o, x

./x

V.2.3. Host és Target

A fordítóprogram (esetünkben szorítkozzunk a C(++) fordítóra) forrásokban adott. Egyik szövegfájlból (a .c forrásból) egy másik szöveges állományt, az assembler forrásfájlt készíti el. Tehát elvileg a fordító forrása bármelyik számítógépen lefordítható úgy, hogy azon a gépen fusson. A fordítóprogramból készül egy futtatható állomány, amely elindítható (Unix-on gcc, windows-on gcc.exe). Azt a rendszert, ahol a fordítóprogram elindul, Compiler Host-nak nevezzük (fordítási gazda), ugyanis ezen fog történni a fordítás. A compiler lefordításakor meg kell adni, hogy milyen CPU utasításkészletet alkalmazzon. Ezt nevezzük Compiler Target-nek (fordítási cél). A fordítóprogramot általában asztali számítógépen vagy laptopon használjuk, itt történik a programfejlesztés, így a host rendszer általában intel-linux vagy intel-windows. A 64-bites változatot történeti okokból amd64-nek, vagy x86_64-nek hívják. Ha a gép operációs rendszerében fogjuk futtatni a lefordított programunkat (target==host) “natív” fordítóról beszélünk. A Microsoft Visual Studio C++ - a natív fordító: Windows alatt fut, a kimenete is Windows alkalmazás. Az embedded Linux-ok általában terjedelmi okok miatt nem tartalmaznak GCC fordítót, az őket futtató gépek nem alkalmasak programfejlesztésre. Ekkor egy PC-n készítjük el a programot, ott is fordítjuk le, csak a célt állítjuk az embedded rendszerre. A folyamat neve keresztfordítás (cross-compiling). Gyakran a SOC-ok (lásd a VI. fejezet fejezetben) teljes programrendszere így készül, a keresztfordító bemenete egy teljes könyvtárstruktúra forrásprogramokkal, kimenete pedig a SOC Rom-jának tartalma.

V.2.4. A GCC gyakran használt opciói

A gcc fordítónak, miután parancssoros üzemeltetésre tervezték, a parancssorba írt kapcsolókkal lehet módosítani a működésén. Alapvető parancs a gcc forrásfájl, amely lefordítja a forrásfájlt egy a.out elnevezésű futtatható fájlba. Vigyázat: Unix alatt nem csak a C nyelvű programok, de az opciók is kis/nagybetű megkülönböztetéssel értelmeződnek.

Opció

Példa

Hatása

-o kimeneti fájl

gcc –o x x.c

x nevű futtatható fájl készül, nem a.out

-Wall

gcc –Wall x.c

az összes figyelmeztetést megjeleníti, pl. konverziókor pontosságcsökkenést, vagy pointer által mutatott adat máshogy értelmezését

-g

gcc –g x.c

a futtatható fájl szimbólumtáblát is tartalmaz, így a gdb debuggerrel hiba kereshető

-save-temps

gcc –save-temps x.c

nem törli le a fordítási folyamat közben keletkező átmeneti fájlokat

-c

gcc –c x.c

ne készítsen futtatható fájlt, csak az obj fájl készüljön el, mert több lefordítandó fájlból áll a program

-x nyelv

gcc –x c x.c

programozási nyelv megadása. Ha nem adjuk meg, a kiterjesztésből állapítja meg: a .cpp végződés C++ nyelvet jelent

-E

gcc –E x.c

csak a preprocesszort futtassa le, assembly file ne készüljön

-S

gcc –S x.c

csak az assembly fájlig fordítson, obj file ne készüljön

-v

gcc –v x.c

verbose: mutassa a fordítás közben végrehajtott parancsokat a preprocesszálástól a szerkesztésig

-O[szám]

gcc –O3 x.c

optimalizálás: O0-nál nincs, O3-nál teljes optimalizálás, a fordítás lassabb lesz, és több memóriát használ, de a kész program gyorsabban fut

-D makró

gcc –Dteszt x.c

makró definiálása. Ugyanaz, mint a #define a programszövegben

-Ikönyvtárnév

gcc –I. x.c

a könyvtárnevet hozzáadja az include fájlok keresési útvonalához. Vagyis itt is lehetnek a programunk include-jai. Az opció a nagy “I” betű, mint „Include”

-nostdinc

gcc –nostdinc x.c

Ne keresse a szabványos include-okat. SOC-nál az adott rendszer include-jait kell használni, a példa sem fog működni –I opció nélkül

-lkönyvtár

gcc –l. x.c

a szerkesztéshez használt elérési út: itt is lehetnek .lib fájlok. Az opció a kis „l” betű, mint „link dir”

-shared

gcc –shared lib.c

nem futtatható programot fordítunk, csak egy részletet egy könyvtárba, amit majd később használunk fel

-time

gcc –time x.c

kiírja az egyes fordítási lépések idejét, Unix felfogásban: felhasználói és operációs rendszeridő fogyasztás.

-fverbose-asm

gcc –S –fverbose-asm x.c

az assembly fájlba helyezzen megjegyzéseket, pl. a C forrás aktuális sorát, hogy könnyebben olvasható legyen.

V.2.5. A make segédprogram

A gcc fordító az előzőekben látott módon képes lefordítani egy C nyelven írt programot. Azonban a program lehet nagyobb is: több fájlból is állhat. Ha több C forrásfájl van, akkor először lefordítjuk őket a –c opcióval, a fordítás végén szerkesztünk az erre a célra készített ld (linker) programmal. Erre a folyamatra akár egy shell scriptet (parancsokat tartalmazó text fájl – hasonló, mint a windows .bat vagy .cmd állománya) is írhatunk, hogy az egyes fájlokat milyen sorrendben fordítsuk le, és a végén hogy szerkesszük össze. Ha tényleg nagy a program, a fordítás eltarthat napokig is. Egy ekkora programnál a fejlesztés sem egyszerű: a forrás módosítása után újra lefordítani az összes C fájlt a legelsőtől az utolsóig felesleges lenne, már csak azért is, mert csak néhány fájl változott meg, a többiekből készített tárgy fájlokat szükségtelen lenne újra generálni. A fájlok megváltozása egyszerű módszerrel figyelhető: amelyik .c fájl újabb (az utolsó módosítás dátuma alapján), mint a belőle készült .o fájl, az a fájl megváltozott, az .o fájlt le kell törölni, és a .c fájl újra kell fordítani. Ezt a fájlok közötti kapcsolatot függőségnek (depedency) nevezzük. A futtatható programhoz is készíthető ilyen feltétel, az .o fájlok megváltozásához. A program, amely ezt a feltételrendszert kezelni tudja, a make. Készítünk a programunkhoz egy Makefile nevű fájlt, amelyben megadjuk, hogy melyik fájl melyikből származik, és ha újra szükséges készíteni, milyen paranccsal tegyük ezt meg. Ezen túl még a make-nek további lehetőségei is vannak: a fájlban megadhatók egyéb, parancssori opciók, mit kell tennie például a make clean, makeinstall parancsok kiadásakor. Egy több forrásfájlból álló kis program Makefile-ja a következő lehet:

tr1660: main.o libtr1660.a screen.o
    gcc -o tr1660  -lcurses main.o libtr1660.a screen.o
    size tr1660
 
kondi:  kondi.o libtr1660.a screen.o
    gcc -o kondi -lm -lcurses kondi.o libtr1660.a screen.o
    rm kondi.o libtr1660.a screen.o
    size kondi
     
libtr1660.a: tr1660.h tr1660.c
    gcc -Wall -c -o tr1660.o tr1660.c
    ar rcs libtr1660.a tr1660.o
    rm tr1660.o
      
main.o: main.c
    gcc -c -Wall -o main.o main.c
    
kondi.o: kondi.c
    gcc -c -Wall -o kondi.o kondi.c
    
screen.o: screen.c screen.h
    gcc -c -Wall -o screen.o screen.c
 
clean:
    rm -f *.o *.a *.so tr1660 kondi *~ a.out

A főprogram neve a tr1660, és 3 külön forrásból áll: a main.c-ből, a libtr1660-ból (fejből és forrásból egy könyvtár lesz), és a screen.c-ből. Lefordítható még a kondi.c-ből a kondi program, a fenti könyvtárat használva, és a teljes projekt kitakarítható a clean esetén.

V.2.6. A gdb debugger (hibakereső/nyomkövető)

Előfordulhat, hogy a programunk nem úgy működik, ahogy szeretnénk. Lefordítható, de hibaüzenettel kidobja az operációs rendszer, vagy hibás eredmény jön ki. Ekkor használható a debugger: képes töréspontot elhelyezni a programban, addig teljes sebességgel futni, majd ott megállni, és a változók értékét megtekinthetjük, lépésenként mehetünk tovább. A GNU projektnél ezt a programot gdb-nek nevezték el. Fontos megjegyezni, hogy a debuggerben mi a C nyelvű programsorokat és változókat látjuk, miközben a program gépi kódja fut. Ezt csak úgy tudja megtenni, ha a programot a gcc-vel, a –g opcióval fordítottuk le. Ekkor a futtatható fájlba szimbólumok is kerülnek: melyik gépi kódú utasítás melyik forrásfájlból keletkezett.

A következő példában kövessük nyomon az V.2.2. szakasz fejezet programját: helyezzünk el töréspontot a programban, hajtsuk végre lépésenként és nézzük meg a változó értékét!

Először fordítsuk le nyomkövetésre alkalmas módon a programunkat!

bash-4.2# gcc -Wall -g x.c -o x

Majd indítsuk el a debuggert, a programunkkal!

bash-4.2# gdb x
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.

Kérjünk forráslistát, ha már nem emlékszünk, mit is irtunk!

(gdb) l
1       #include <stdio.h>
2       
3       int main(void) {
4         int i;
5         i=0;
6         i++;
7         printf("%i\n",i);
8         return 0;
9       }

Tegyünk töréspontot az 5. sorra!

(gdb) b 5
Breakpoint 1 at 0x400918: file x.c, line 5.

Indulhat a program !

(gdb) r
Starting program: /root/prg.c/x 
 
Breakpoint 1, main () at x.c:5
5    i=0;

Megállt a program, az 5. sorban. Lépjünk egyet és hajtsuk végre az i=0-t!

(gdb) n
6    i++;

Hajtsuk végre az i++-t is!

(gdb) n
7    printf("%i\n",i);

Írassuk ki i értékét !

(gdb) p i
$1 = 1

Menjünk tovább teljes sebességgel!

(gdb) c
Program exited normally.

A nyomkövetés kész, kiléphetünk a debuggerből.

(gdb) q

V.3. Posix C, C++ rendszerkönyvtárak

Az V.1.1. szakasz szakaszban láttuk, hogy a C(++) nyelvű programjaink akkor lesznek hordozhatók, ha több rendszeren is le lehet őket fordítani. Ehhez a szabványosítási szervezetek definiáltak egy közösen használható függvénykönyvtárat, amelynek elemei minden posix szabvány szerinti rendszerben megtalálhatók. Ezekben a függvényekben gyakran használt programrészleteket találunk, amelyekre a programozó számíthat. Például nem kell megírnia a lebegőpontos számok kiíratását, mert a printf(”%f”) függvény rendelkezésre áll, az #include <stdio.h> után. A Posix ugyan a Unix-okról szólt, de ezek a könyvtárak elérhetők akár a Visual Studióban is. A könyvtár nagyszámú fejállományt és nagyszámú include-ot tartalmaz, terjedelmi okok miatt a további részben csak az oktatásban előforduló függvényeket ismertetjük röviden. A teljes rendszer megtalálható a Wikipedián, „C POSIX library” címszó alatt. A következőkben az egyes fejállományokban található függvényeket ismertetjük röviden.

V.3.1. stdio.h

Az stdio.h-ban (c++: cstdio) alapvető i/o műveletek találhatók.

  • fopen: fájl megnyitása,

  • freopen: fájl újranyitása,

  • fflush: kimenet szinkronizálása az aktuális állapotra,

  • fclose: fájl bezárása,

  • fread: fájlból adat olvasása,

  • fwrite: fájlba adat írása,

  • getc/fgetc: karakter beolvasás szabványos inputról/fájlból,

  • gets/fgets: string olvasása szabványos inputról/fájlból,

  • putc/fputc: karakter kiírása standard outputra/fájlba,

  • puts/fputs: string írása standard outputra/fájlba,

  • ungetc: már kiolvasott karakter visszatétele az adatfolyamba,

  • scanf/fscanf/sscanf: adat formázott olvasása a szabványos inputról/fájlból/sztringből. A formátumbeállításokat lásd az I.2. szakasz fejezetben,

  • printf/fprintf/sprintf/snprintf: adat formázott kiírása a szabványos outputra/fájlba/sztringbe/ adott hosszúságú sztringbe. Az snprintf()-nél nem léphet fel buffer túlcsordulási hiba,

  • ftell: az aktuális fájlpozíció lekérdezése,

  • fseek : a fájlpozíció beállítása, fájlmutató mozgatásával,

  • rewind: fájlmutató mozgatása a fájl elejére,

  • feof : a fájl végének ellenőrzése,

  • perror: szöveg kiírása standard error fájlra,

  • remove: fájl törlése,

  • rename: fájl átnevezése,

  • tmpfile: átmeneti, egyedi nevű fájl létrehozása, és pointerének visszaadása,

  • tmpnam: átmeneti, egyedi fájlnév visszaadása,

  • EOF: a fájlvége konstans,

  • NULL: garantáltan sehova sem mutató pointer konstans,

  • SEEK_CUR: az aktuális pozícióhoz képest mozgatjuk a fájlmutatót,

  • SEEK_END: a fájl végéhez képest mozgatjuk a fájlmutatót,

  • SEEK_SET: a fájl elejéhez képest mozgatjuk a fájlmutatót,

  • stdin, stdout,stderr: a szabványos adatfolyamok definíciói,

  • FILE : a fájlváltozók struktúrája,

  • size_t: a sizeof operátor visszatérési típusa.

V.3.2. math.h

A math.h fájlban (c++: cmath) matematikai függvényeket és konstansokat találunk.

  • abs/fabs: abszolút érték egész/valós számokra,

  • div/ldiv: egész osztás eredménye és maradéka egy div_t típusú struktúrába,

  • exp: az exponenciális függvény,

  • log: a logaritmus függvény (e alapú),

  • log10: 10-es alapú logaritmus függvény,

  • sqrt: a négyzetgyök függvény,

  • pow: a hatványozó függvény (hatványozás művelet nincs a C++-ban, de függvény igen),

  • sin/cos/tan/atan: a trigonometrikus függvények: szinusz/koszinusz/tangens/inverz tangens,

  • sinh/cosh/tanh/atanh: a hiperbolikus trigonometrikus függvények,

  • ceil: visszaadja az argumentumánál nem nagyobb egészet (lefele kerekít),

  • floor: visszaadja az argumentumánál nem kisebb egészet (felfele kerekít),

  • round: egészre kerekít.

V.3.3. stdlib.h

Az stdlib.h-ban (c++: cstdlib) a Unix-ban szükséges egyéb, szokásosnak tekinthető függvényeket találunk: szöveg-szám konverzióra, véletlen szám generálásra, memóriafoglalásra, folyamatvezérlésre.

  • atof: sztringet konvertál lebegőpontosra (gyakorlatilag double),

  • atoi: sztringet konvertál egészre,

  • atol: sztringet konvertál hosszú egészre,

  • rand: egy egész véletlen számot ad vissza,

  • srand: beállítja a véletlen szám generátor kezdőértékét. A gépi idővel szokásos inicializálni,

  • malloc: helyfoglalás a memóriában,

  • realloc: lefoglalt memória átméretezése,

  • calloc: memória foglalás, és a kapott memóriablokk feltöltése 0-val,

  • free: a lefoglalt memória felszabadítása,

  • abort: a programunk azonnal leáll, takarítás nélkül,

  • exit: a programunk leáll, normál leállítással, takarítással,

  • atexit: megadhatjuk, hogy kilépéskor melyik függvényünk hívódjon meg,

  • getenv: a program a környezeti változóihoz fér hozzá, amelyek név=érték formátumúak,

  • system: az operációs rendszer parancsértelmezőjének hívása, az argumentumában megadott parancssorral. Külső programok futtatására szolgál.

V.3.4. time.h

A time.h (c++: ctime) fejállományban az idő kezeléséhez szükséges adatstruktúrák és függvények találhatók. A Unix típusú idő egy egész szám, az 1970 január 1. éjfél óta eltelt másodpercek száma, UTC-ben (régi nevén GMT), hogy a különböző időzónákban lévő gépek is jól kommunikáljanak. A gépnek a helyi időt az aktuális időzóna segítségével kell kiszámolnia. A 32-bites érték használata időként a Unix rendszer tervezésekor jó ötletnek tűnt, de manapság már – főleg az y2k-s pánikkeltés hatására – figyelembe vesszük, hogy a 32-bites idő 2038 január 18-án túlcsordul. A mai rendszereken a time_t már 64-bites, amely 293 billió évig lesz használható.

  • time: az aktuális idővel tér vissza, time_t formátumban (Unix time),

  • difftime: két időpont különbségét számítja ki,

  • clock: megadja az aktuális programunk (mint folyamat) indítása óta lefutott CPU órajelek számát,

  • asctime: a tm struktúrát konvertálja szöveggé,

  • ctime: a tm struktúrában található időt, a helyi idő szerint, konvertálja sztringgé,

  • strftime: mint az asctime, csak mi adhatjuk meg a formátumot (például: éééé-hh-nn óó:pp:mm),

  • gmtime: a time_t típusból (egész) tm struktúrába konvertál UTC időt,

  • localtime: a time_t típusból (egész) tm struktúrába konvertál helyi időt,

  • mktime: a tm struktúrából konvertál time_t típusú időt,

  • tm: az idő adattípus. Tagjai: tm_sec másodperc, tm_min perc, tm_hour óra , tm_mday hónap napja, tm_mon hónap (0:január.. 11:december), tm_year: év 1900 óta, tm_wday: a hét napja (0:vasárnap), tm_yday: napok január 1 óta, tm_isdst: nyári időszámítás aktív jelző,

  • time_t: egész típusú idő, a másodpercek száma 1970 január 1 óta,

  • clock_t: egész típus, cpu órajelek számlálására.

V.3.5. stdarg.h

Az stdarg.h (c++: cstdarg) fejlécek közt a II.2. szakasz fejezetben ismertetett változó paraméterszámú függvények (mint pl. a printf) írásához szükséges típusokat és makrókat találjuk:

  • va_list: a változó számú lista típusa,

  • va_start: a lista kezdetére állás makrója,

  • va_arg: a következő elem lekérése a listából,

  • va_end: a lista felszabadítása,

  • va_copy: a lista másolása egy másik listába.

V.3.6. string.h

A string.h (c++: cstring) fejállományok a "C típusú", 0-val végződő, egybájtos karakterekből álló sztringeket kezelő függvényeket és konstansokat tartalmazza. A sztring/folytonos memóriablokk kezelés minden programozási nyelvben fontos feladat. Léteznek függvények, amelyek a 0 végjelig dolgoznak az adattömbön, és léteznek, amelyek nem veszik figyelembe a végjelet: ezekben mindig van legalább egy paraméter, amely a kezelendő adat hosszára utal.

  • strcpy: sztring másolása 0 végjelig,

  • strncpy: sztring másolása megadott darabszámú karakter hosszan,

  • strcat: egyik sztring végéhez a másik másolása, konkatenáció, 0 végjelig,

  • strncat: egyik sztring végéhez a másik másolása, konkatenáció, megadott darabszámú karakter hosszan,

  • strlen: a sztring aktuális hosszának (0 végjelig) kiszámítása,

  • strcmp: két sztring összehasonlítása, 0 végjelig. Kimenet: 0, ha a két sztring azonos karaktereket tartalmaz, negatív, ha az első kisebb (ASCII-ban), mint a második, pozitív, ha a második argumentum kisebb,

  • strncmp: két sztring összehasonlítása, megadott hosszan. Kimenet az strcmp()-nél definiált,

  • strchr: a sztringben egy karakter (char) keresése. Ha megtalálta, visszaadja a karakterre mutató pointert, ha nem találta, NULL értékkel tér vissza,

  • strstr: a sztringben egy másik sztringet keres. Visszatérő értéke az strchr()-nél leírttal azonos,

  • strtok: a sztringet tokenek mentén szétdarabolja. Vigyázat, a sztring eredeti tartalmát elrontja!

  • strerror: az operációs rendszer által visszaadott numerikus hibakódot angol nyelvű hibaüzetenet tartalmazó sztringgé alakítja,

  • memset: a pointer által mutatott memóriaterületet ugyanazzal a karakterrel tölti fel, az argumentumként megadott mennyiségben,

  • memcpy: az egyik pointer által mutatott memóriaterületet átmásolja a másik pointer által mutatott területre, az argumentumként megadott számú bájtot másol,

  • memmove: mint a memcpy(), csak a memóriaterületek átlapoltak is lehetnek (vagyis az egyik mutató+hossz<=másik muató). Ekkor először átmeneti memóriába másol, majd onnan a célmemóriába,

  • memcmp: a két memóriaterület összehasonlítása, adott hosszon,

  • memchr: megkeresi az adott karaktert a memóriában, adott hosszon. Ha nem találja, NULL-t ad vissza.

V.3.7. dirent.h

A dirent.h fájlban a háttértárolón található könyvtárak kezelésének adattípusai és függvényei találhatók. A dirent.h nem része a C szabványnak, de a POSIX rendszer tartalmazza. A Microsoft Visual C++ nem tartalmaz dirent.h-t. Ott ezen függvények Win32-es megfelelőit kell alkalmazni (FindFirstFile, FindNextFile,FindClose).

  • struct dirent: a könyvtárbejegyzés adattípusa a memóriában. Tartalmazza a fájl sorozatszámát, és nevét, mint char[] tömböt. Tartalmazhat még fájl offsetet, rekordméretet, névhosszat, és fájltípust,

  • opendir: megadott nevű könyvtár kinyitása, memóriaváltozóba helyezése,

  • readdir: bejegyzés olvasása a kinyitott könyvtárból. A könyvtár végén (ha már nincs több bejegyzés), NULL-t ad vissza,

  • readdir _r: a közvetlen elérésű fájlokhoz hasonlóan direkt cimzéssel is olvashatjuk a katalógust,

  • seekdir: a readdir_r() által használt mutató mozgatása,

  • rewinddir: a readdir_r() által használt mutató mozgatása az első elemre,

  • telldir: a readdir_r() által használt mutató lekérdezése,

  • closedir: a könyvtár bezárása. Egyetlen rendszerben sincs végtelen sok fájl vagy könyvtárkezelő.

V.3.8. sys/stat.h

Ebben a fejállományban a fájlok állapotával kapcsolatos függvények és konstansok kaptak helyet.

  • struct stat: a fájl állapotát ebbe a struktúrába képes beolvasni. A struktúra tagjai:

    • st_dev: az eszköz, ahol a fájl található,

    • st_ino: a fájl inode-ja,

    • st_mode: a fájl elérési jogok, és tipus: ST_ISREG: közönséges fájl, ST_ISDIR: könyvtár.

    • st_nlink: a fájlhoz tartozó hard linkek (=további fájlnevek) száma,

    • st_uid: a fájl tulajdonosának felhasználói azonosítója (user id),

    • st_gid: a fájl tulajdonosának goup id-je,

    • st_rdev: az eszköz azonosítója, ha az nem reguláris fájl (pl. eszköz),

    • st_size: a fájl mérete,

    • st_blksize: a fájl blokkmérete,

    • st_blocks: a fájl által lefoglalt blokkok száma,

    • st_atime: az utolsó elérés ideje,

    • st_mtime: az utolsó tartalommódosítás ideje,

    • st_ctime: az utolsó állapotmódosítás ideje.

  • stat: egy névvel adott fájl állapotát lekérdező függvény a struct stat típusú mutató által megcímzett területre. Ha a file egy szimbolikus link, a stat az eredeti file paramétereit adja vissza,

  • fstat: mint a stat(), de nem fájlnevet adunk meg, hanem a nyitott fájlra jellemző leírót,

  • lstat: mint a stat(), de nem az eredeti fájl állapotát adja vissza, hanem visszatér a linkkel.

V.3.9. unistd.h

Az unistd.h fájlban a szabványos Unix-ok alatti programüzemeltetéssel kapcsolatos konstansok és függvények találhatók. Ezek közül néhányat ismertetünk:

  • chdir: az aktuális könyvtár cseréje,

  • chown: a fájltulajdonos cseréje,

  • close: nyitott fálj zárása, leírójának segítségével,

  • crypt: jelszó titkosítása,

  • dup: fájlkezelő másolása,

  • execl/ecexle/execv: új folyamat (program) futtatása,

  • _exit: kilépés a programból,

  • fork: alfolyamat futtatása, a sajátunkból indítva. Lemásoljuk neki a környezetünket, elindítjuk, majd megvárjuk, amíg végez,

  • link: fájlhoz hard vagy szimbolikus link készítése,

  • lseek: a fájlmutató mozgatása,

  • nice: program futtatása, ha a rendszer terhelése alacsony,

  • pipe: csővezeték készítése az adatoknak,

  • read: olvasás fájlból, leírójának használatával,

  • rmdir: könyvtár törlése,

  • setuid: aktuális felhasználó azonosítójának beállítása,

  • sleep: várakozás megadott ideig,

  • sync: a fájlrendszer puffereinek aktualizálása,

  • unlink: fájl törlése,

  • write: fájl írása, leírójának használatával.