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.
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.
je několik základních cest, jak ukončit proces:
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ů.
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.
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");
}
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++)
Historicky se program skládal z těchto částí:
int maxcount = 99;uvedená mimo jakoukoliv funkci, bude uložena právě v této oblasti.
long count[1000];
Viz též obrázek 6.
Obrázek 6:
Typické uspořádání paměti
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).
V ANSI C jsou definovány tři základní funkce pro dynamické přidělování paměti.
| #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.
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.
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).
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ů.
Ladislav Dobias