Hlavní navigace

Programování pod Linuxem pro všechny (8)

Jakub Matys

V dnešním dílu se podíváme na vytváření procesů. Proberu zde funkci fork() a funkce ze skupiny exec. Máte se tedy na co těšit...

Vytváření procesů

Pro snadné vytvoření procesu (přesněji spuštění programu) z programu je možné použít funkci system(). Použití této funkce se ale obecně nedoporučuje, protože nejdříve vytvoří subproces, ve kterém běží bash (/bin/sh), který pak zadaný příkaz spustí. To s sebou nese bezpečnostní rizika – spuštěný program podléhá všem vlastnostem, omezením a bezpečnostním opatřením platným pro systémový příkazový procesor.

Vytvoření podřízeného procesu funkcí fork()

Pomocí funkce fork() se vytvoří duplicitní kopie aktuálního procesu, která se nazývá podřízený proces. Nadřízený proces pokračuje ve své existenci a v realizaci svého kódu následujícího po volání funkce fork(). Podřízený proces realizuje stejný program od stejného místa. Funkce fork() vrací jiný návratový kód nadřízenému procesu a jiný podřízenému procesu. Návratovou hodnotou v nadřízeném procesu je identifikační číslo podřízeného procesu, návratovou hodnotou v podřízeném procesu je nula. Díky tomuto faktu je možné odlišit nadřízený proces od podřízeného při psaní kódu.

Program v příkladu vytvoří duplikát procesu. První blok v příkazu if se provede pouze v nadřízeném procesu, zatímco blok po kauzuli else se provede pouze v podřízeném procesu.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  pid_t child_pid;

  printf("ID procesu je %d\n", (int) getpid());

  child_pid = fork();
  if (child_pid != 0) {
    printf("Toto je rodicovsky proces, s ID %d\n", (int) getpid());
    printf("ID podrizeneho procesu je %d\n", (int) child_pid);
  }
  else
    printf("Toto je podrizeny proces s ID %d\n", (int) getpid());

  return(0);
} 

Spouštění programů pomocí funkcí skupiny exec

Funkce exec nahradí program běžící v podřízeném procesu jiným progarmem. Při volání funkce exec proces ukončí provádění aktuálního programu a zahájí realizaci specifikovaného programu od začátku (pokud nenastala chyba).

Ve skupině funkcí exec je několik funkcí, které se mírně liší způsobem volání a výsledkem své činnosti:

  • Pokud funkce ve svém označení obsahují písmeno p (execvp, execlp), přijímají jako argument jmého programu a hledají uvedený program v aktuálním seznamu adresářů uvedeném v proměnné PATH. Pokud jméno funkce neobsahuje písmeno p, musí se uvést jméno spouštěného programu včetně úplné cesty.
  • Funkce s písmenem v (execv, execvp a execve) přijímají seznam argumentů (nulou ukončené pole ukazatelů na znakové řetězce), které mají předat spouštěnému prograu.
  • Funkce obsahující ve svém označení písmeno e (execve a execle) akceptují další argument, pole systémových proměnných. Tímto argumentem je nulou ukončené pole ukazatelů na znakové řetězce. Každý řetězec musí být ve tvaru „PROMENNA=hodnota“.

Seznam argumentů předávaný novému programu pracuje na stejném principu jako seznam argumentů předávaný funkci main(). Nesmíte zapomenout, že první argument musí obsahovat jméno programu (stejně jako argv[0]).

Spuštění programu pomocí funkcí fork() a exec

Při spouštění podprogramu uvnitř jiného programu nejdříve volejte funkci fork() a teprve pak funkce ze skupiny exec. Tímto docílíte toho, že volající kód v nadřízeném procesu může pokračovat ve své realizaci, zatímo v podřízeném procesu je volající program nahrazen novým programem.

Program v příkladu zobrazuje seznam souborů v hlavním adresáři pomocí příkazu ls -l /:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

/* Funkce vytvori podrizeny proces a spusti v nem novy
program. PROGRAM je jmeno programu, ktery se ma spustit.
ARG_LIST je seznam argumentu programu. Vraci ID
vytvoreneho procesu. */

int spawn(char *program, char **arg_list)
{
  pid_t child_pid;

  /* Vytvoreni noveho procesu */
  child_pid = fork();
  if (child_pid != 0)
    /* Toto je rodicovsky proces */
    return(child_pid);
  else {
    /* Nyni se spusti PROGRAM */
    execvp(program, arg_list);
    /* Toto funkce vraci pouze pokud nastala chyba */
    fprintf(stderr, "Chyba pri volani funkce execvp()\n");
    abort();
  }
}

int main()
{
  /* Pole argumentu predavanych pri volani prikazu ls */
  char *arg_list[] = {
    "ls",
    "-l",
    "/",
    NULL
  };

  /* Volani funkce spawn */
  printf("PID spousteneho procesu je %d\n", (int) spawn("ls", arg_list));

  return(0);
} 

To by bylo pro dnešek vše, v příštím dílu se podíváme na plánování procesů pomocí funkce nice() a na zpracování signálů.

Našli jste v článku chybu?
23. 4. 2004 19:00
Taif (neregistrovaný)

Cuz, syscall fork(), zduplicitni dany proces (viz. novy task_struct atd...) ... nemusis se bat o dynamickou pamet ci o otevrene soubory -> nebudou se ovlivnovat (resp. read-on-write metoda => zduplicidni se po prvnim pouziti jednim z techto dvou), takze se prakticky o nic starat nemusis. [jine je to s VLAKNAMA!!!]. Co se tyce exec, tak je to jednoduche ... stary program v pameti je nahrazen novym (+ ošetření).

23. 4. 2004 9:13
Dan (neregistrovaný)

Ahoj, prispivam uz trosku pozde, ale zrovna ted je to pro me aktualni. Protoze je to clanek pro zacatecniky, a fork() a exec() jsem jeste nikdy nepouzil, chci se zeptat na par drobnosti: Jak je to s uvolnovanim zdroju? Co kdyz ma rodicovsky proces dynamicky alokovanou nejakou pamet, otevrene soubory ci zarizeni, namapovane soubory nebo jine veci. Je treba, aby toto vsechno potomek uvolnoval/uzaviral? Na co si dat pozor mezi spustenim fork() a exec()? Existuje nejaky soubor pravidel, co udela…