Vytváření nových procesů v jazyce C: Porovnání verzí

Z Milan Kerslager
Přejít na: navigace, hledání
(Rozšíření)
m (+kat)
 
(Není zobrazena jedna mezilehlá verze od stejného uživatele.)
Řádka 3: Řádka 3:
 
V následujícím příkladu je použita funkce <CODE>execl()</CODE>, která vyžaduje, aby spouštěný program byl zadán s plnou cestou a je možné předávat přepínače a parametry jako na příkazovém řádku v shellu.
 
V následujícím příkladu je použita funkce <CODE>execl()</CODE>, která vyžaduje, aby spouštěný program byl zadán s plnou cestou a je možné předávat přepínače a parametry jako na příkazovém řádku v shellu.
  
    1  #include <stdio.h>
+
<source lang="C">
    2  #include <unistd.h>
+
#include <stdio.h>
    3  int main ()
+
#include <unistd.h>
    4  {
+
int main ()
    5    int ret;
+
{
    6    printf("Start... execl /bin/ls\n");
+
  int ret;
    7    ret = execl ("/bin/ls", "ls", "-l", (char *)0);
+
  printf("Start... execl /bin/ls\n");
    8    printf("Chyba: navrat uz nebude\n");
+
  ret = execl ("/bin/ls", "ls", "-l", (char *)0);
    9    return 1;
+
  printf("Chyba: navrat uz nebude\n");
    10  }
+
  return 1;
 +
}
 +
</source>
  
 
Další příklad používá funkci <CODE>execle()</CODE>, která umožňuje nastavit proměnné prostředí. Nedojde tedy k dědění proměnných prostředí z rodiče, což může být užitečné v případech, kdy by mohlo dojít k nežádoucímu ovlivnění nově spuštěného programu. Na řádku 6 jsou připravené proměnné prostředí HOME a LOGNAME. Pomocí funkce ''execle'' se následně spustí příkaz <CODE>printenv</CODE>, který vypíše všechny proměnné prostředí. Po spuštění programu budou ve výpisu jen výše zmíněné 2 proměnné <CODE>HOME</CODE> a <CODE>LOGNAME</CODE>. Pokud byste zkusili stejný příkaz spustit v předchozím příkladu, bude vypsáno nepoměrně více proměnných prostředí, které jsou při použití ''execl'' zděděny od rodiče.
 
Další příklad používá funkci <CODE>execle()</CODE>, která umožňuje nastavit proměnné prostředí. Nedojde tedy k dědění proměnných prostředí z rodiče, což může být užitečné v případech, kdy by mohlo dojít k nežádoucímu ovlivnění nově spuštěného programu. Na řádku 6 jsou připravené proměnné prostředí HOME a LOGNAME. Pomocí funkce ''execle'' se následně spustí příkaz <CODE>printenv</CODE>, který vypíše všechny proměnné prostředí. Po spuštění programu budou ve výpisu jen výše zmíněné 2 proměnné <CODE>HOME</CODE> a <CODE>LOGNAME</CODE>. Pokud byste zkusili stejný příkaz spustit v předchozím příkladu, bude vypsáno nepoměrně více proměnných prostředí, které jsou při použití ''execl'' zděděny od rodiče.
  
    1  #include <stdio.h>
+
<source lang="C">
    2  #include <unistd.h>
+
#include <stdio.h>
    3  int main ()
+
#include <unistd.h>
    4  {
+
int main ()
    5    int ret;
+
{
    6    char *env[] = { "HOME=/usr/home", "LOGNAME=home", (char *)0 };
+
  int ret;
    7    printf("Start... exec /usr/bin/printenv\n");
+
  char *env[] = { "HOME=/usr/home", "LOGNAME=home", (char *)0 };
    8    ret = execle ("/usr/bin/printenv", (char *)0, env);
+
  printf("Start... exec /usr/bin/printenv\n");
    9    printf("Chyba: navrat uz nebude\n");
+
  ret = execle ("/usr/bin/printenv", (char *)0, env);
    10    return 1;
+
  printf("Chyba: navrat uz nebude\n");
    11  }
+
  return 1;
 +
}
 +
</source>
  
 
Funkce ''fork'' slouží k vytvoření procesu potomka, který je identický s rodičem. Rozlišení mezi rodičem a potomkem je možné pomocí testování návratového kódu funkce ''fork''. Rodič získá po návratu funkce PID procesu potomka. Potomek získá jako návratový kód 0 (nulu), protože si může zjistit svoje PPID (Parent PID, tj. číslo procesu rodiče) pomocí funkce <CODE>getppid()</CODE> (není potřeba žádný parametr).
 
Funkce ''fork'' slouží k vytvoření procesu potomka, který je identický s rodičem. Rozlišení mezi rodičem a potomkem je možné pomocí testování návratového kódu funkce ''fork''. Rodič získá po návratu funkce PID procesu potomka. Potomek získá jako návratový kód 0 (nulu), protože si může zjistit svoje PPID (Parent PID, tj. číslo procesu rodiče) pomocí funkce <CODE>getppid()</CODE> (není potřeba žádný parametr).
Řádka 32: Řádka 36:
 
V níže uvedeném programu se řádky 12 až 15 budou provádět jen v procesu potomka a řádky 17 až 24 jen v rodičovském procesu, přičemž kód na řádcích 17 až 18 bude proveden jen v případě neúspěchu funkce ''fork''. Neúspěšný návrat z funkce ''fork'' nastane pouze v neobvyklých případech, například při vyčerpání povoleného množství procesů pro přihlášeného uživatele nebo při celkovém zahlcení celého systému (například z důvodu vyčerpání dostupné paměti, vyčerpání maximálního počtu současně spustitelných procesů a podobně).
 
V níže uvedeném programu se řádky 12 až 15 budou provádět jen v procesu potomka a řádky 17 až 24 jen v rodičovském procesu, přičemž kód na řádcích 17 až 18 bude proveden jen v případě neúspěchu funkce ''fork''. Neúspěšný návrat z funkce ''fork'' nastane pouze v neobvyklých případech, například při vyčerpání povoleného množství procesů pro přihlášeného uživatele nebo při celkovém zahlcení celého systému (například z důvodu vyčerpání dostupné paměti, vyčerpání maximálního počtu současně spustitelných procesů a podobně).
  
    1  #include <stdlib.h>
+
<source lang="C">
    2  #include <stdio.h>
+
#include <stdlib.h>
    3  #include <unistd.h>
+
#include <stdio.h>
    4  #include <sys/types.h>
+
#include <unistd.h>
    5  #include <sys/wait.h>
+
#include <sys/types.h>
    6  int main ()
+
#include <sys/wait.h>
    7  {
+
int main ()
    8    pid_t pid;
+
{
    9    int retcode;
+
  pid_t pid;
    10    pid = fork();
+
  int retcode;
    11    if (pid == 0) {
+
  pid = fork();
     12      printf("Potomek...\n");
+
  if (pid == 0) {
     13      sleep(1);
+
     printf("Potomek...\n");
     14      printf("Potomek skoncil\n");
+
     sleep(1);
     15      exit(99);
+
     printf("Potomek skoncil\n");
    16    } else if (pid < 0) {
+
     exit(99);
     17      printf("Fork selhal\n");
+
  } else if (pid < 0) {
     18      exit(2);
+
     printf("Fork selhal\n");
    19    } else {
+
     exit(2);
     20      printf("Rodic zna PID potomka: %d...a ceka na jeho konec\n",pid);
+
  } else {
     21      wait(&retcode);
+
     printf("Rodic zna PID potomka: %d...a ceka na jeho konec\n",pid);
     22      printf("Potomek skoncil s kodem: %d\n",WEXITSTATUS(retcode));
+
     wait(&retcode);
     23      printf("Rodic konci\n");
+
     printf("Potomek skoncil s kodem: %d\n",WEXITSTATUS(retcode));
     24      exit(3);
+
     printf("Rodic konci\n");
    25    }
+
     exit(3);
    26    exit(1);
+
  }
    27  }
+
  exit(1);
 +
}
 +
</source>
  
 
== Cvičení ==
 
== Cvičení ==
Řádka 69: Řádka 75:
  
 
;Příklad 4: Napište síťového démona, který po připojení klienta odštěpí potomka, který klienta obslouží. Klient bude reagovat na jakýkoliv vstup pouze vlastní identifikací.
 
;Příklad 4: Napište síťového démona, který po připojení klienta odštěpí potomka, který klienta obslouží. Klient bude reagovat na jakýkoliv vstup pouze vlastní identifikací.
 +
 +
[[Kategorie:Jazyk C v Linuxu]]

Aktuální verze z 6. 4. 2009, 20:41

Vznik nových procesů je v unixových systémech spojen se dvěma funkcemi – fork() a exec(). Nejprve se blíže podíváme na rodinu funkcí exec, kterých je několik. Liší se způsobem, jakým jsou jim předávány parametry, možnostmi v zadávání parametrů a způsobem spuštění nového procesu. Jejich společným principem je nahrazení právě vykonávaného programu programem jiným, který je načten do stejného paměťového prostoru. Tím dojde k plnému nahrazení běžícího programu. Zachován zůstane PCB (Process Control Block), tj. např. PID (Process IDentification), práva souboru a také otevřené soubory a proměnné prostředí. Při volání funkce exec je potřeba na tyto okolnosti dávat pozor. Nejčastější chybou bývají zapomenuté otevřené deskriptory souborů nebo nežádoucí proměnné prostředí.

V následujícím příkladu je použita funkce execl(), která vyžaduje, aby spouštěný program byl zadán s plnou cestou a je možné předávat přepínače a parametry jako na příkazovém řádku v shellu.

#include <stdio.h>
#include <unistd.h>
int main ()
{
  int ret;
  printf("Start... execl /bin/ls\n");
  ret = execl ("/bin/ls", "ls", "-l", (char *)0);
  printf("Chyba: navrat uz nebude\n");
  return 1;
}

Další příklad používá funkci execle(), která umožňuje nastavit proměnné prostředí. Nedojde tedy k dědění proměnných prostředí z rodiče, což může být užitečné v případech, kdy by mohlo dojít k nežádoucímu ovlivnění nově spuštěného programu. Na řádku 6 jsou připravené proměnné prostředí HOME a LOGNAME. Pomocí funkce execle se následně spustí příkaz printenv, který vypíše všechny proměnné prostředí. Po spuštění programu budou ve výpisu jen výše zmíněné 2 proměnné HOME a LOGNAME. Pokud byste zkusili stejný příkaz spustit v předchozím příkladu, bude vypsáno nepoměrně více proměnných prostředí, které jsou při použití execl zděděny od rodiče.

#include <stdio.h>
#include <unistd.h>
int main ()
{
  int ret;
  char *env[] = { "HOME=/usr/home", "LOGNAME=home", (char *)0 };
  printf("Start... exec /usr/bin/printenv\n");
  ret = execle ("/usr/bin/printenv", (char *)0, env);
  printf("Chyba: navrat uz nebude\n");
  return 1;
}

Funkce fork slouží k vytvoření procesu potomka, který je identický s rodičem. Rozlišení mezi rodičem a potomkem je možné pomocí testování návratového kódu funkce fork. Rodič získá po návratu funkce PID procesu potomka. Potomek získá jako návratový kód 0 (nulu), protože si může zjistit svoje PPID (Parent PID, tj. číslo procesu rodiče) pomocí funkce getppid() (není potřeba žádný parametr).

V níže uvedeném programu se řádky 12 až 15 budou provádět jen v procesu potomka a řádky 17 až 24 jen v rodičovském procesu, přičemž kód na řádcích 17 až 18 bude proveden jen v případě neúspěchu funkce fork. Neúspěšný návrat z funkce fork nastane pouze v neobvyklých případech, například při vyčerpání povoleného množství procesů pro přihlášeného uživatele nebo při celkovém zahlcení celého systému (například z důvodu vyčerpání dostupné paměti, vyčerpání maximálního počtu současně spustitelných procesů a podobně).

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main ()
{
  pid_t pid;
  int retcode;
  pid = fork();
  if (pid == 0) {
    printf("Potomek...\n");
    sleep(1);
    printf("Potomek skoncil\n");
    exit(99);
  } else if (pid < 0) {
    printf("Fork selhal\n");
    exit(2);
  } else {
    printf("Rodic zna PID potomka: %d...a ceka na jeho konec\n",pid);
    wait(&retcode);
    printf("Potomek skoncil s kodem: %d\n",WEXITSTATUS(retcode));
    printf("Rodic konci\n");
    exit(3);
  }
  exit(1);
}

Cvičení

Příklad 1
Napište program démon, který se zbaví svého rodiče (rodičem se stane proces init) a odpojí se od terminálu.
Příklad 2
Napište démona, který neodpojí od terminálu svůj výstup a po přijetí signálu vypíše hlášení.
Příklad 3
Napište obdobu programu nohup.
Příklad 4
Napište síťového démona, který po připojení klienta odštěpí potomka, který klienta obslouží. Klient bude reagovat na jakýkoliv vstup pouze vlastní identifikací.