Hlavní navigace

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

Jakub Matys

Dnes se podíváme na další metodu komunikace mezi procesy. Jedná se o datové kolony neboli roury.

Datová kolona (roura) je komunikační zařízení, které umožňuje jednosměrnou komunikaci. Data zapisovaná do datové kolony jedním programem lze jiným programem bezprostředně číst. Datové kolony jsou sériová zařízení, data jsou tedy čtena přesně v tom pořadí, v jakém byla zapsána. Roury se nejčastěji používají ke komunikaci mezi nadřízeným a podřízeným procesem.

Kapacita datové kolony je omezena; pokud zapisující proces generuje data rychleji, než je proces přijímající data schopen číst, kolona není schopna ukládat více dat a proces přijímající data se zablokuje, dokud neuvolní svoji kapacitu. Když se přijímající proces pokouší číst data, ale žádná se mu nedostávají, zablokuje se, dokud se neobjeví nějaká data. Z uvedeného vyplývá, že datová kolona automaticky synchronizuje dva procesy.

Vytvoření datové kolony

Datová kolona se vytváří pomocí funkce pipe(). Argumentem je celočíselné pole o dvou prvcích. Po volání funkce se na první pozici ukládá deskriptor souboru pro čtení a na druhé pozici deskriptor souboru pro zápis.

int pipe_fds[2];
    int read_fd;
    int write_fd;

    pipe(pipe_fds);
    read_fd = pipe_fds[0];
    write_fd = pipe_fds[1];

Data zapsaná do deskriptoru read_fd mohou být čtena zpět z deskriptoru write_fd.

Komunikace mezi nadřízenými a podřízenými procesy

Když proces volá funkci fork(), jsou všechny deskriptory souborů zkopírovány do podřízeného procesu. To znamená, že datové kolony mohou spojovat spřízněné procesy, ale ne procesy nespřízněné.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

/* Zapise COUNT (pocet) kopii zprav (MESSAGES) do STREAM a pote pozastavi svoji cinnost na jednu sekundu */

void writer(const char *message, int count, FILE *stream)
{
  for(;count > 0; --count){
    /* Zapise zpravu */
    fprintf(stream, "%s\n", message);
    fflush(stream);
    sleep(1);
  }
}

/* Cte retezce z STREAM */

void reader(FILE *stream)
{
  char buffer[1024];
  /* Cte dokud nenarazi na konec retezce. fgets cte dokud nenarazi na znak noveho radku nebo end-of-line */
  while(!feof(stream) && !ferror(stream) && fgets(buffer, sizeof(buffer), stream) != NULL)
    fputs(buffer, stdout);
}

int main()
{
  int fds[2];
  pid_t pid;

  /* Vytvori rouru. Deskriptory souboru pro rouru jsou ulozeny do fds */
  pipe(fds);
  /* Duplikuje proces */
  pid = fork();
  if(pid == (pid_t) 0){
    FILE *stream;
    /* Toto je synovsky proces. Zavre svou kopii pro zapis */
    close(fds[1]);
    /* Konvertuje deskriptor souboru pro cteni na objekt typu FILE a cte z neho */
    stream = fdopen(fds[0], "r");
    reader(stream);
    close(fds[0]);
  }
  else{
    /* Toto je rodicovsky proces */
    FILE *stream;
    /* Zavre svou kopii pro cteni */
    close(fds[0]);
    /* Konvertuje deskriptor pro zapis na objekt typu FILE a zapisuje do neho. */
    stream = fdopen(fds[1], "w");
    writer("Hello, world", 5, stream);
    close(fds[1]);
  }
  return(0);
} 

Na začátku funkce main() je deklarováno celočíselné pole o velikosti 2. Funkce pipe() vytvoří datovou kolonu a do pole uloží deskriptory souborů pro čtení a zápis. Program poté pomocí funkce fork() vytvoří podřízený proces. Nadřízený proces zapíše do kolony řetězce a podřízený proces je přečte.

Po zápisu funkcí writer je vyprázdněna vyrovnávací paměť pro datovou kolonu (funkce fflush()). Jinak by totiž řetězec nemohl být okamžitě odeslán skrz datovou kolonu.

To by bylo pro dnešek vše. Příště budeme přesměrovávat standardní vstup, výstup a chybové hlášení, řekneme si něco o funkcích popen() a pclose() a vytvoříme datovou kolonu FIFO.

Našli jste v článku chybu?