GNU/Linux >> Znalost Linux >  >> Linux

Meziprocesová komunikace v Linuxu:Použití kanálů a front zpráv

Toto je druhý článek ze série o meziprocesové komunikaci (IPC) v systému Linux. První článek se zaměřil na IPC prostřednictvím sdíleného úložiště:sdílené soubory a sdílené paměťové segmenty. Tento článek se zaměřuje na kanály, což jsou kanály, které propojují procesy pro komunikaci. Kanál má zápisový konec pro zápis bajtů a konec čtení pro čtení těchto bajtů v pořadí FIFO (první dovnitř, první ven). Při typickém použití jeden proces zapisuje do kanálu a jiný proces čte ze stejného kanálu. Samotné bajty mohou představovat cokoli:čísla, záznamy zaměstnanců, digitální filmy a tak dále.

Pipe se dodávají ve dvou variantách, pojmenované a nepojmenované, a lze je používat interaktivně z příkazového řádku nebo v rámci programů; příklady se chystají. Tento článek se také zabývá paměťovými frontami, které vyšly z módy – ale nezaslouženě.

Příklady kódu v prvním článku uznaly hrozbu rasových podmínek (buď založených na souborech nebo na paměti) v IPC, který používá sdílené úložiště. Přirozeně vyvstává otázka týkající se bezpečné souběžnosti pro IPC založené na kanálech, které bude zahrnuto v tomto článku. Příklady kódu pro kanály a paměťové fronty používají API s razítkem schválení POSIX a hlavním cílem standardů POSIX je bezpečnost vláken.

Zvažte manuálové stránky pro mq_open funkce, která patří do rozhraní API fronty paměti. Tyto stránky obsahují část o atributech s touto malou tabulkou:

Rozhraní Atribut Hodnota
mq_open() Bezpečnost vláken MT-Safe

Hodnota MT-Safe (s MT pro vícevláknové) znamená, že mq_open funkce je vláknově bezpečná, což zase implikuje procesně bezpečnou:Proces se provádí přesně v tom smyslu, v jakém se provádí jedno z jeho vláken, a pokud mezi vlákny stejným nemůže vzniknout spor procesu, taková podmínka nemůže nastat mezi vlákny v různých procesech. MT-Safe atribut zajišťuje, že při vyvolání mq_open nevznikne spor . Obecně platí, že IPC založené na kanálech je souběžně bezpečné, i když v následujících příkladech je uvedena varovná poznámka.

Nepojmenované kanály

Začněme s umělým příkladem příkazového řádku, který ukazuje, jak fungují nepojmenované kanály. Na všech moderních systémech je svislý pruh | představuje nepojmenované potrubí na příkazovém řádku. Předpokládejme % je řádek příkazového řádku a zvažte tento příkaz:

% sleep 5 | echo "Hello, world!" ## writer to the left of |, reader to the right

spánek a echo utility se spouštějí jako samostatné procesy a nepojmenovaná roura jim umožňuje komunikovat. Příklad je však vymyšlen tak, že nedochází k žádné komunikaci. Pozdrav Ahoj, světe! se objeví na obrazovce; poté, asi po pěti sekundách, se příkazový řádek vrátí, což znamená, že oba spí a echo procesy byly ukončeny. Co se děje?

V syntaxi svislého pruhu z příkazového řádku je proces nalevo (spánek ) je autor a proces napravo (echo ) je čtenář. Ve výchozím nastavení čtečka blokuje, dokud nejsou k dispozici bajty ke čtení z kanálu, a zapisovač – po zapsání svých bajtů – skončí odesláním značky konce toku. (I když zapisovač předčasně ukončí, do čtečky se odešle značka konce proudu.) Nepojmenovaná roura přetrvává, dokud zapisovač i čtečka neukončí.

[Stáhněte si kompletního průvodce meziprocesovou komunikací v Linuxu]

Ve vymyšleném příkladu spánek proces nezapíše do kanálu žádné bajty, ale asi po pěti sekundách se ukončí, což kanálu odešle značku konce streamu. Mezitím echo proces okamžitě zapíše pozdrav na standardní výstup (obrazovka), protože tento proces nečte žádné bajty z kanálu, takže nečeká. Jakmile usnete a echo procesy se ukončí, nepojmenovaná roura – která se pro komunikaci vůbec nepoužívá – zmizí a vrátí se příkazový řádek.

Zde je užitečnější příklad s použitím dvou nepojmenovaných rour. Předpokládejme, že soubor test.dat vypadá takto:

this
is
the
way
the
world
ends

Příkaz:

% cat test.dat | sort | uniq

zprostředkuje výstup z cat (zřetězit) proces do třídění proces produkující setříděný výstup a poté seřazený výstup převádí do uniq proces k odstranění duplicitních záznamů (v tomto případě dva výskyty the snížit na jednu):

ends
is
the
this
way
world

Scéna je nyní nastavena pro program se dvěma procesy, které komunikují prostřednictvím nepojmenovaného kanálu.

Příklad 1. Dva procesy komunikující prostřednictvím nepojmenovaného kanálu.

#include <sys/wait.h> /* wait */
#include <stdio.h>
#include <stdlib.h>   /* exit functions */
#include <unistd.h>   /* read, write, pipe, _exit */
#include <string.h>

#define ReadEnd  0
#define WriteEnd 1

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1);    /** failure **/
}

int main() {
  int pipeFDs[2]; /* two file descriptors */
  char buf;       /* 1-byte buffer */
  const char* msg = "Nature's first green is gold\n"; /* bytes to write */

  if (pipe(pipeFDs) < 0) report_and_exit("pipeFD");
  pid_t cpid = fork();                                /* fork a child process */
  if (cpid < 0) report_and_exit("fork");              /* check for failure */

  if (0 == cpid) {    /*** child ***/                 /* child process */
    close(pipeFDs[WriteEnd]);                         /* child reads, doesn't write */

    while (read(pipeFDs[ReadEnd], &buf, 1) > 0)       /* read until end of byte stream */
      write(STDOUT_FILENO, &buf, sizeof(buf));        /* echo to the standard output */

    close(pipeFDs[ReadEnd]);                          /* close the ReadEnd: all done */
    _exit(0);                                         /* exit and notify parent at once  */
  }
  else {              /*** parent ***/
    close(pipeFDs[ReadEnd]);                          /* parent writes, doesn't read */

    write(pipeFDs[WriteEnd], msg, strlen(msg));       /* write the bytes to the pipe */
    close(pipeFDs[WriteEnd]);                         /* done writing: generate eof */

    wait(NULL);                                       /* wait for child to exit */
    exit(0);                                          /* exit normally */
  }
  return 0;
}

pipeUN výše uvedený program používá systémovou funkci fork vytvořit proces. Přestože má program pouze jeden zdrojový soubor, během (úspěšného) provádění dochází k vícenásobnému zpracování. Zde jsou podrobnosti v rychlém přehledu toho, jak knihovna funguje rozvětvení funguje:

  • rozvětvení funkce, volaná v nadřazeném proces, vrátí -1 rodičům v případě neúspěchu. V pipeUN například volání je:
    pid_t cpid = fork(); /* called in parent */

    Vrácená hodnota je v tomto příkladu uložena v proměnné cpid celočíselného typu pid_t . (Každý proces má své vlastní ID procesu , nezáporné celé číslo, které identifikuje proces.) Rozvětvení nového procesu může selhat z několika důvodů, včetně úplné tabulky procesů , struktura, kterou systém udržuje pro sledování procesů. Zombie procesy, brzy objasněny, mohou způsobit zaplnění tabulky procesů, pokud tyto nejsou sklizeny.

  • Pokud rozvětvení volání uspěje, tím se vytvoří (vytvoří) nový podřízený proces, který vrátí jednu hodnotu nadřazenému, ale jinou hodnotu podřízenému. Nadřazený i podřízený proces provádějí stejné kód, který následuje po volání fork . (Dítě zdědí kopie všech proměnných deklarovaných dosud v nadřazeném prvku.) Zejména úspěšné volání funkce fork vrátí:
    • Od nuly k podřízenému procesu
    • ID procesu podřízeného pro rodiče
  • if/else nebo ekvivalentní konstrukce se obvykle používá po úspěšném rozvětvení volání pro oddělení kódu určeného pro rodiče od kódu určeného pro dítě. V tomto příkladu je konstrukce:
    if (0 == cpid) {    /*** child ***/
    ...
    }
    else {              /*** parent ***/
    ...
    }

Pokud se rozvětvení dítěte podaří, pipeUN program pokračuje následovně. Existuje celočíselné pole:

int pipeFDs[2]; /* two file descriptors */

uchovávat dva deskriptory souborů, jeden pro zápis do roury a druhý pro čtení z roury. (Prvek pole pipeFDs[0] je deskriptor souboru pro konec čtení a prvek pole pipeFDs[1] je deskriptor souboru pro konec zápisu.) Úspěšné volání systémové roury funkce, provedená bezprostředně před voláním fork , naplní pole dvěma deskriptory souborů:

if (pipe(pipeFDs) < 0) report_and_exit("pipeFD");

Další zdroje pro Linux

  • Cheat pro příkazy Linuxu
  • Cheat sheet pro pokročilé příkazy systému Linux
  • Bezplatný online kurz:Technický přehled RHEL
  • Síťový cheat pro Linux
  • Cheat sheet SELinux
  • Cheat pro běžné příkazy pro Linux
  • Co jsou kontejnery systému Linux?
  • Naše nejnovější články o Linuxu

Rodič a potomek mají nyní kopie obou deskriptorů souborů, ale oddělení problémů vzor znamená, že každý proces vyžaduje právě jeden z deskriptorů. V tomto příkladu rodič píše a dítě čte, ačkoli role mohou být obráceny. První příkaz v podřízeném prvku if -kód klauzule proto uzavírá konec zápisu roury:

close(pipeFDs[WriteEnd]); /* called in child code */

a první příkaz v nadřazeném else -kód klauzule uzavírá čtecí konec roury:

close(pipeFDs[ReadEnd]);  /* called in parent code */

Rodič pak zapíše několik bajtů (ASCII kódů) do nepojmenovaného kanálu a potomek je přečte a odešle je na standardní výstup.

Ještě jeden aspekt programu potřebuje objasnění:volání na čekání funkce v nadřazeném kódu. Jakmile je podřízený proces vytvořen, je do značné míry nezávislý na svém nadřízeném, stejně jako krátké pipeUN program ilustruje. Dítě může spustit libovolný kód, který nemusí mít nic společného s rodičem. Systém však upozorní rodiče prostřednictvím signálu – pokud a kdy dítě skončí.

Co když rodič skončí dřív než dítě? V tomto případě, pokud nebudou přijata preventivní opatření, se dítě stane a zůstane zombie proces se záznamem v tabulce procesů. Opatření jsou dvou širokých typů. Jedním z opatření je nechat rodiče upozornit systém, že rodič nemá zájem na ukončení dítěte:

signal(SIGCHLD, SIG_IGN); /* in parent: ignore notification */

Druhým přístupem je nechat rodiče provést čekání o zániku dítěte, a tím zajistit, aby rodič dítě přežil. Tento druhý přístup se používá v pipeUN program, kde nadřazený kód má toto volání:

wait(NULL); /* called in parent */

Toto volání čekejte znamená počkat, dokud nedojde k ukončení jakéhokoli dítěte a v pipeUN existuje pouze jeden podřízený proces. (NULL argument by mohl být nahrazen adresou celočíselné proměnné, aby byl zachován výstupní stav dítěte.) Existuje flexibilnější waitpid funkce pro jemné ovládání, např. pro specifikaci konkrétního podřízeného procesu z několika.

pipeUN program přijímá další opatření. Když rodič čeká, rodič ukončí volání na běžný exit funkce. Naproti tomu dítě skončí voláním na _exit varianta, která zrychluje oznámení o ukončení. Ve skutečnosti dítě říká systému, aby co nejdříve informoval rodiče, že dítě skončilo.

Pokud dva procesy zapisují do stejného nepojmenovaného kanálu, mohou být bajty prokládány? Pokud například proces P1 zapíše:

foo bar

do roury a proces P2 současně zapisuje:

baz baz

do stejné roury, zdá se, že obsah roury může být něco libovolného, ​​jako například:

baz foo baz bar

Standard POSIX zajišťuje, že zápisy nebudou prokládány, pokud žádný zápis nepřekročí PIPE_BUF bajtů. V systémech Linux PIPE_BUF má velikost 4 096 bajtů. U dýmek preferuji mít jednoho autora a jednoho čtenáře, čímž se tomuto problému vyhneme.

Pojmenované kanály

Nepojmenovaný kanál nemá žádný podpůrný soubor:systém udržuje vyrovnávací paměť v paměti pro přenos bajtů ze zapisovače do čtečky. Jakmile se zapisovač a čtečka ukončí, vyrovnávací paměť je znovu získána, takže nepojmenovaná roura zmizí. Naproti tomu pojmenovaný kanál má podpůrný soubor a odlišné API.

Podívejme se na další příklad příkazového řádku, abychom získali podstatu pojmenovaných rour. Zde jsou kroky:

  • Otevřete dva terminály. Pracovní adresář by měl být pro oba stejný.
  • V jednom z terminálů zadejte tyto dva příkazy (výzva je opět % a moje komentáře začínají ## ):
    % mkfifo tester  ## creates a backing file named tester
    % cat tester     ## type the pipe's contents to stdout

    Na začátku by se v terminálu nemělo nic objevit, protože do pojmenovaného roura ještě nebylo nic zapsáno.

  • Do druhého terminálu zadejte příkaz:
    % cat > tester  ## redirect keyboard input to the pipe
    hello, world!   ## then hit Return key
    bye, bye        ## ditto
    <Control-C>     ## terminate session with a Control-C

    Cokoli napíšete do tohoto terminálu, se přenese do druhého. Jednou Ctrl+C je zadáno, vrátí se běžná výzva příkazového řádku v obou terminálech:roura byla uzavřena.

  • Proveďte vyčištění odstraněním souboru, který implementuje pojmenovaný kanál:
    % unlink tester

Jako název nástroje mkfifo znamená, že pojmenovaná roura se také nazývá FIFO, protože první bajt dovnitř je první bajt ven a tak dále. Existuje funkce knihovny s názvem mkfifo který v programech vytváří pojmenované roury a je použit v dalším příkladu, který se skládá ze dvou procesů:jeden zapisuje do pojmenovaného roura a druhý z tohoto roura čte.

Příklad 2. fifoWriter program

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

#define MaxLoops         12000   /* outer loop */
#define ChunkSize           16   /* how many written at a time */
#define IntsPerChunk         4   /* four 4-byte ints per chunk */
#define MaxZs              250   /* max microseconds to sleep */

int main() {
  const char* pipeName = "./fifoChannel";
  mkfifo(pipeName, 0666);                      /* read/write for user/group/others */
  int fd = open(pipeName, O_CREAT | O_WRONLY); /* open as write-only */
  if (fd < 0) return -1;                       /* can't go on */

  int i;
  for (i = 0; i < MaxLoops; i++) {          /* write MaxWrites times */
    int j;
    for (j = 0; j < ChunkSize; j++) {       /* each time, write ChunkSize bytes */
      int k;
      int chunk[IntsPerChunk];
      for (k = 0; k < IntsPerChunk; k++)
        chunk[k] = rand();
      write(fd, chunk, sizeof(chunk));
    }
    usleep((rand() % MaxZs) + 1);           /* pause a bit for realism */
  }

  close(fd);           /* close pipe: generates an end-of-stream marker */
  unlink(pipeName);    /* unlink from the implementing file */
  printf("%i ints sent to the pipe.\n", MaxLoops * ChunkSize * IntsPerChunk);

  return 0;
}

fifoWriter výše uvedený program lze shrnout následovně:

  • Program vytvoří pojmenovanou rouru pro zápis:
    mkfifo(pipeName, 0666); /* read/write perms for user/group/others */
    int fd = open(pipeName, O_CREAT | O_WRONLY);

    kde název potrubí je název podpůrného souboru předávaného do mkfifo jako první argument. Pojmenovaná roura se poté otevře již známým voláním open funkce, která vrací deskriptor souboru.

  • Pro dotek realismu nabízí fifoWriter nezapíše všechna data najednou, ale místo toho zapíše kus, uspí náhodný počet mikrosekund a tak dále. Celkem je do pojmenovaného kanálu zapsáno 768 000 4bajtových celočíselných hodnot.
  • Po zavření pojmenovaného kanálu, fifoWriter také odpojí soubor:
    close(fd);        /* close pipe: generates end-of-stream marker */
    unlink(pipeName); /* unlink from the implementing file */

    Systém získá zpět záložní soubor, jakmile každý proces připojený k kanálu provedl operaci odpojení. V tomto příkladu existují pouze dva takové procesy:fifoWriter a fifoReader , přičemž oba provedou odpojení operace.

Tyto dva programy by měly být spouštěny v různých terminálech se stejným pracovním adresářem. Nicméně fifoWriter by měl být spuštěn před fifoReaderem , protože první vytváří potrubí. fifoReader poté přistoupí k již vytvořenému pojmenovanému kanálu.

Příklad 3. fifoReader program

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

unsigned is_prime(unsigned n) { /* not pretty, but efficient */
  if (n <= 3) return n > 1;
  if (0 == (n % 2) || 0 == (n % 3)) return 0;

  unsigned i;
  for (i = 5; (i * i) <= n; i += 6)
    if (0 == (n % i) || 0 == (n % (i + 2))) return 0;

  return 1; /* found a prime! */
}

int main() {
  const char* file = "./fifoChannel";
  int fd = open(file, O_RDONLY);
  if (fd < 0) return -1; /* no point in continuing */
  unsigned count = 0, total = 0, primes_count = 0;

  while (1) {
    int next;
    int i;

    ssize_t count = read(fd, &next, sizeof(int));
    if (0 == count) break;                  /* end of stream */
    else if (count == sizeof(int)) {        /* read a 4-byte int value */
      total++;
      if (is_prime(next)) primes_count++;
    }
  }

  close(fd);       /* close pipe from read end */
  unlink(file);    /* unlink from the underlying file */
  printf("Received ints: %u, primes: %u\n", total, primes_count);

  return 0;
}

fifoReader výše uvedený program lze shrnout následovně:

  • Protože fifoWriter vytvoří pojmenovaný kanál, fifoReader potřebuje pouze standardní hovor otevřený pro přístup k rouře přes záložní soubor:
    const char* file = "./fifoChannel";
    int fd = open(file, O_RDONLY);

    Soubor se otevře jako pouze pro čtení.

  • Program poté přejde do potenciálně nekonečné smyčky a pokusí se přečíst 4bajtový blok v každé iteraci. čtení call:
    ssize_t count = read(fd, &next, sizeof(int));

    vrátí 0 pro označení konce streamu, v takovém případě fifoReader vytrhne se ze smyčky, zavře pojmenovanou rouru a před ukončením odpojí záložní soubor.

  • Po přečtení 4bajtového celého čísla se fifoReader kontroluje, zda je číslo prvočíslo. To představuje obchodní logiku, kterou může produkční čtečka provádět s přijatými bajty. Při vzorovém běhu bylo mezi 768 000 přijatými celými čísly 37 682 prvočísel.

Při opakovaných vzorových spuštěních fifoReader úspěšně přečíst všechny bajty, které fifoWriter napsal. To není překvapivé. Tyto dva procesy se provádějí na stejném hostiteli, čímž se problémy se sítí vylučují z rovnice. Pojmenované trubky jsou vysoce spolehlivým a účinným mechanismem IPC, a proto mají široké použití.

Zde je výstup ze dvou programů, každý spuštěný ze samostatného terminálu, ale se stejným pracovním adresářem:

% ./fifoWriter
768000 ints sent to the pipe.
###
% ./fifoReader
Received ints: 768000, primes: 37682

Fronty zpráv

Pipe mají striktní chování FIFO:první zapsaný bajt je první bajt přečtený, druhý zapsaný bajt je druhý přečtený bajt a tak dále. Fronty zpráv se mohou chovat stejným způsobem, ale jsou dostatečně flexibilní, aby bylo možné získávat bloky bajtů mimo pořadí FIFO.

Jak název napovídá, fronta zpráv je sekvence zpráv, z nichž každá má dvě části:

  • Datové zatížení, což je pole bajtů (char v C)
  • Typ zadaný jako kladné celé číslo; typy kategorizují zprávy pro flexibilní vyhledávání

Zvažte následující znázornění fronty zpráv, přičemž každá zpráva je označena typem celého čísla:

          +-+    +-+    +-+    +-+
sender--->|3|--->|2|--->|2|--->|1|--->receiver
          +-+    +-+    +-+    +-+

Ze čtyř zobrazených zpráv je ta označená 1 na přední straně, tj. nejblíže k přijímači. Dále následují dvě zprávy se štítkem 2 a nakonec zpráva s označením 3 vzadu. Pokud by bylo ve hře striktní chování FIFO, pak by zprávy byly přijímány v pořadí 1-2-2-3. Fronta zpráv však umožňuje další příkazy k načtení. Zprávy mohou být například načteny příjemcem v pořadí 3-2-1-2.

mqueue příklad se skládá ze dvou programů, odesílatel který zapisuje do fronty zpráv a příjemce který čte z této fronty. Oba programy obsahují hlavičkový soubor queue.h zobrazeno níže:

Příklad 4. Soubor záhlaví queue.h

#define ProjectId 123
#define PathName  "queue.h" /* any existing, accessible file would do */
#define MsgLen    4
#define MsgCount  6

typedef struct {
  long type;                 /* must be of type long */
  char payload[MsgLen + 1];  /* bytes in the message */
} queuedMessage;

Soubor záhlaví definuje typ struktury s názvem queuedMessage , s užitnou zátěží (bajtové pole) a typ (celočíselná) pole. Tento soubor také definuje symbolické konstanty (#define příkazy), z nichž první dva se používají ke generování klíče, který se zase používá k získání ID fronty zpráv. ID projektu může být jakákoli kladná celočíselná hodnota a Název cesty musí jít o existující, přístupný soubor – v tomto případě soubor queue.h . Prohlášení o nastavení v odesílateli a přijímač programy jsou:

key_t key = ftok(PathName, ProjectId);   /* generate key */
int qid = msgget(key, 0666 | IPC_CREAT); /* use key to get queue id */

ID qid je ve skutečnosti protějškem deskriptoru souboru pro fronty zpráv.

Příklad 5. odesílatel zprávy program

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include "queue.h"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1); /* EXIT_FAILURE */
}

int main() {
  key_t key = ftok(PathName, ProjectId);
  if (key < 0) report_and_exit("couldn't get key...");

  int qid = msgget(key, 0666 | IPC_CREAT);
  if (qid < 0) report_and_exit("couldn't get queue id...");

  char* payloads[] = {"msg1", "msg2", "msg3", "msg4", "msg5", "msg6"};
  int types[] = {1, 1, 2, 2, 3, 3}; /* each must be > 0 */
  int i;
  for (i = 0; i < MsgCount; i++) {
    /* build the message */
    queuedMessage msg;
    msg.type = types[i];
    strcpy(msg.payload, payloads[i]);

    /* send the message */
    msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT); /* don't block */
    printf("%s sent as type %i\n", msg.payload, (int) msg.type);
  }
  return 0;
}

odesílatel výše uvedený program odešle šest zpráv, každá dvě zadaného typu:první zprávy jsou typu 1, další dvě jsou typu 2 a poslední dvě jsou typu 3. Příkaz k odeslání:

msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT);

je nakonfigurován jako neblokující (příznak IPC_NOWAIT ), protože zprávy jsou tak malé. Jediným nebezpečím je, že plná fronta, což je v tomto příkladu nepravděpodobné, povede k selhání odesílání. Přijímač níže uvedený program také přijímá zprávy pomocí IPC_NOWAIT vlajka.

Příklad 6. příjemce zprávy program

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include "queue.h"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1); /* EXIT_FAILURE */
}

int main() {
  key_t key= ftok(PathName, ProjectId); /* key to identify the queue */
  if (key < 0) report_and_exit("key not gotten...");

  int qid = msgget(key, 0666 | IPC_CREAT); /* access if created already */
  if (qid < 0) report_and_exit("no access to queue...");

  int types[] = {3, 1, 2, 1, 3, 2}; /* different than in sender */
  int i;
  for (i = 0; i < MsgCount; i++) {
    queuedMessage msg; /* defined in queue.h */
    if (msgrcv(qid, &msg, sizeof(msg), types[i], MSG_NOERROR | IPC_NOWAIT) < 0)
      puts("msgrcv trouble...");
    printf("%s received as type %i\n", msg.payload, (int) msg.type);
  }

  /** remove the queue **/
  if (msgctl(qid, IPC_RMID, NULL) < 0)  /* NULL = 'no flags' */
    report_and_exit("trouble removing queue...");

  return 0;
}

Přijímač program nevytváří frontu zpráv, ačkoli API to naznačuje. V přijímači , volání:

int qid = msgget(key, 0666 | IPC_CREAT);

je zavádějící kvůli IPC_CREAT příznak, ale tento příznak ve skutečnosti znamená vytvořit v případě potřeby, jinak přístup . odesílatel volání programu msgsnd k odesílání zpráv, zatímco příjemce volání msgrcv získat je. V tomto příkladu odesílatel odesílá zprávy v pořadí 1-1-2-2-3-3, ale příjemce pak je načte v pořadí 3-1-2-1-3-2, což ukazuje, že fronty zpráv nejsou vázány na striktní chování FIFO:

% ./sender
msg1 sent as type 1
msg2 sent as type 1
msg3 sent as type 2
msg4 sent as type 2
msg5 sent as type 3
msg6 sent as type 3

% ./receiver
msg5 received as type 3
msg1 received as type 1
msg3 received as type 2
msg2 received as type 1
msg6 received as type 3
msg4 received as type 2

Výše uvedený výstup ukazuje, že odesílatel a přijímač lze spustit ze stejného terminálu. Výstup také ukazuje, že fronta zpráv přetrvává i po odesílateli proces vytvoří frontu, zapíše do ní a ukončí se. Fronta zmizí až za přijímačem proces jej explicitně odstraní voláním msgctl :

if (msgctl(qid, IPC_RMID, NULL) < 0) /* remove queue */

Zabalení

Rozhraní API pro kanály a fronty zpráv jsou v zásadě jednosměrné :jeden proces zapisuje a druhý čte. Existují implementace obousměrných pojmenovaných rour, ale moje dva centy jsou, že tento mechanismus IPC je nejlepší, když je nejjednodušší. Jak již bylo zmíněno dříve, popularita front zpráv klesla – ale bez dobrého důvodu; tyto fronty jsou dalším nástrojem v sadě nástrojů IPC. Část 3 dokončuje tuto rychlou prohlídku sady nástrojů IPC s příklady kódu IPC přes zásuvky a signály.


Linux
  1. Meziprocesová komunikace v Linuxu:Sokety a signály

  2. Pipes a přesměrování v Linuxu – vysvětleno!

  3. Jak zkontrolovat OS a verzi pomocí příkazu Linux

  1. Představení průvodce meziprocesovou komunikací v Linuxu

  2. Jak spustit příkaz Linux na pozadí a odpojit proces v terminálu

  3. Jak zabít proces v Linuxu pomocí příkazu?

  1. Úvod do rour a pojmenovaných rour v Linuxu

  2. Jak přidat a odstranit statickou trasu v Linuxu pomocí příkazu IP

  3. Naučte se příkazy Pgrep a Pkill s příklady v Linuxu