další předchozí obsah
Další: Řízení procesů Předchozí: Systémové soubory

Prostředí procesů





Úvod

Před popsáním nástrojů pro řízení procesů se musíme nejdřív zabývat prostředím, ve kterém procesy běží. Dále se budeme zabývat otázkou dodatečné správy paměti.



Funkce main

Každý program v C musí nutně obsahovat funkci main. Její prototyp je:

int main (int argc, char *argv[]);

Kde argc je počet argumentů na příkazové řádce a argv je ukazatel na pole argumentů. Při spuštění programu nejprve proběhnou start-up funkce, které zajistí vyplnění těchto struktur.



  Ukončení procesu

je několik základních cest, jak ukončit proces:

  1. Normální ukončení
    1. return z funkce main
    2. volání exit
    3. volání _exit
  2. Abnormální ukončení
    1. volání abort
    2. ukončení signálem


Funkce exit a _exit

Tyto dvě funkce mohou přímo ukončit program. Funkce _exit přímo vrátí řízení jádru a exit provede uklízecí procedury.

#include <stdlib.h>
void exit (int status);
#include <unistd.h>
void _exit (int status)

Klasický příklad programu v C...

#include <stdio.h>

main()
{
printf("hello, world\n");
}
...je vlastně nesprávný -- nemá definován návratový kód.

Aby byl program úplný, měli bychom definovat návratový kód takto:

     return(0);
nebo
     exit(0);

Při definování návratových kódů si ale musíme uvědomit, že druhé řešení povede k výstražnému hlášení -- konflikt prototypů.



Funkce atexit

Při použití ANSI C můžeme použít až 32 funkcí, které se automaticky provedou při volání exit. Tyto funkce (exit handlers) můžeme registrovat pomocí funkce atexit.

#include <stdlib.h>
int atexit (void (*func) (void));
Vrací: 0 když OK, nenulová hodnota při chybě

Při volání exit norma ANSI zaručuje, kromě jiného, uzavření otevřených souborů. Obrázek 5 ukazuje, jak program v C startuje a jakými možnými způsoby lze ukončit.

Zde má být moc hezký obrázek 'c_exit', škoda, že ho nevidíte

Obrázek 5: Jak program v C startuje a jak se ukončuje

Příklad:

static void    my_exit1(void), my_exit2(void);
int
main(void)
{
    if (atexit(my_exit2) != 0)
        err_sys("can't register my_exit2");
    if (atexit(my_exit1) != 0)
        err_sys("can't register my_exit1");
    if (atexit(my_exit1) != 0)
        err_sys("can't register my_exit1");
    printf("main is done\n");
    return(0);
}
static void
my_exit1(void)
{
    printf("first exit handler\n");
}
static void
my_exit2(void)
{
    printf("second exit handler\n");
}



Argumenty příkazové řádky

Když je program prováděn, funkce exec dovoluje předat programu příkazovou řádku.

Příklad:

Normálně příkaz echo neprovádí echo celé řádky. Po zkompilování následujícího programu získáme echo příkazového řádku.

int
main(int argc, char *argv[])
{
  int   i;

  for (i = 0; i < argc; i++)   /* echo all command-line args */
     printf("argv[%d]: %s\n", i, argv[i]);
  exit(0);
}

ANSI i POSIX.1 garantují, že argv[argc] je NULL pointer. To nám alternativně umožní psát:

      for (i=0; argv[i] != NULL; i++)



Rozdělení paměti u programu v C

Historicky se program skládal z těchto částí:

Text segment
Strojové instrukce prováděné CPU. Často je tento segment označen jako read-only -- ochrana proti náhodnému přepisu
Segment inicializovaných dat
Často se jmenuje jen data segment a obsahuje přiřazené proměnné. Např. deklarace v C
      int maxcount = 99;
uvedená mimo jakoukoliv funkci, bude uložena právě v této oblasti.
Segment neinicializovaných dat
Tento segment se také nazývá bss (block started by symbol). Jádro vyplní tento segment před spuštěním nulami. Zde bude uloženo např. pole, deklarované, mimo jakoukoli funkci, jako
      long count[1000];
Zásobník
Zde budou ukládány lokální proměnné spolu s návratovými adresami funkcí.
Hromada
Dynamické přidělování paměti se často odehrává v tomto segmentu. Historicky byla hromada (heap) umístěna mezi konec neinicializovaných dat a zásobník.

Viz též obrázek 6.

Zde má být moc hezký obrázek 'memory', škoda, že ho nevidíte

Obrázek 6: Typické uspořádání paměti



Sdílené knihovny

Sdílené knihovny byly popsány již v prvních implementacích Systemu V. Hlavní princip spočívá v odstranění knihoven z programů a společné sdílení jediné jejich kopie v paměti. Při spuštění více programů najednou se výrazně ušetří paměť. Nejjednodušší cestou, jak použít sdílených knihoven, je správné nastavení parametrů programů cc a ld. Po zkompilování se sdílenými knihovnami rapidně poklesne velikost výstupního souboru (jednotlivé části programu zjistíme příkazem size).



Správa paměti

V ANSI C jsou definovány tři základní funkce pro dynamické přidělování paměti.

  1. malloc. Alokuje odpovídající velikost paměti v bajtech. Hodnoty v alokované paměti nejsou určeny.
  2. calloc. Alokuje paměť pro určený počet objektů specifikované velikosti. Hodnoty paměti jsou nastaveny na 0.
  3. realloc. Mění velikost již jednou přidělené paměti. Při zvětšování rozsahu může přesunout celou oblast na nové místo.

#include <stdlib.h>
void *malloc (size_t size);
void *calloc (size_t nobj, size_t size);
void *realloc (void *ptr, size_t newsize);
Všechny tři vrací: ukazatel když OK, NULL při chybě
void free(void *ptr);

Poslední je uvedena funkce free, která slouží k uvolnění alokovaného místa.



Proměnné prostředí

Každému programu je také předáno prostředí (seznam proměnných prostředí popisující). Adresa pole určujícího prostředí je uložena v globální proměnné environ.

      extern char **environ

Např. prostředí, skládající se ze čtyř řetězců, může vypadat následovně. Viz obr. 7.

Zde má být moc hezký obrázek 'environm', škoda, že ho nevidíte

Obrázek 7: Prostředí sestávající ze čtyř řetězců

Konvence pro prostředí vypadá takto: name=value. Většina předdefinovaných jmen je psána velkými písmeny, ale je to jen úmluva.

Unixové jádro nikdy nečte proměnné -- jejich interpretace záleží na jednotlivých aplikacích.

ANSI C definuje funkci, která slouží ke zjištění hodnoty proměnné prostředí.

#include <stdlib.h>
char *getenv (const char *name);
Vrací: ukazatel na value patřící k name když OK, NULL při chybě

Obdobné funkce existují i pro nastavování proměnných, ale ty nejsou přenositelné (putenv, setenv, unsetenv, claerenv).



Cvičení

  1. ??(nejaky uvod, zadani nejajeho programku...)

    Pokud to váš systém umožňuje, zkompilujte tento program jednou staticky a jednou se sdílenými knihovnami. Porovnejte veliskosti výsledných souborů.


další předchozí obsah
Další: Řízení procesů Předchozí: Systémové soubory

Ladislav Dobias
Sat Nov 1 15:38:32 MET 1997