další předchozí obsah
Další: Síťování Předchozí: Meziprocesní komunikace

  Meziprocesní komunikace





Úvod

Předchozí kapitola ukazuje primitiva nutná pro řízení procesů. Jestliže ale tyto procesy již běží, je nutné zajistit mezi nimi komunikaci. Unix podporuje širokou škálu prostředků pro komunikaci mezi procesy (IPC -- InterProcess Communination).



  Roury

Roury (pipes) jsou nejstarším komunikačním prostředkem v unixu. Jsou podporovány všemi typy unixů za těchto omezujících podmínek:

V kapitole "Funkce socketpair si popíšeme proudové roury, které eliminují první omezení, a před pojmenovanými rourami neobstojí ani druhé omezení, viz kap. "Funkce fifo".

Roura se vytvoří vyvoláním funkce pipe.

#include <unistd.h>
int pipe (int filedes[2]);
Vrací: 0 když OK, -1 při chybě

Funkce vrátí dva deskriptory pomocí argumentu filedes. Deskriptor filedes[0] je otevřen pro čtení a filedes[1] pro zápis. Pro schematické znázornění roury můžeme použít dva způsoby, jak je vidno z obr. 11.

Zde má být moc hezký obrázek 'pipe1', škoda, že ho nevidíte

Obrázek 11: Dva způsoby zobrazení unixové roury

Roura je u jednoho procesu bezvýznamná. Proto se často používá volání pipe před fork. Běžný scénář ilustruje obrázek 12.

Zde má být moc hezký obrázek 'pipefork', škoda, že ho nevidíte

Obrázek 12: Poloduplexní roura po fork

Procesy pak musí zabezpečit uzavření nepoužívaných konců. Viz obr. 13.

Zde má být moc hezký obrázek 'pipepch', škoda, že ho nevidíte

Obrázek 13: Roura od rodiče k potomkovi

Když je jeden konec roury zavřený, děje se následující:

Příklad:

int
main(void)
{
    int     n, fd[2];
    pid_t   pid;
    char    line[MAXLINE];

    if (pipe(fd) < 0)
        err_sys("pipe error");

    if ( (pid = fork()) < 0)
        err_sys("fork error");

    else if (pid > 0) {        /* parent */
        close(fd[0]);
        write(fd[1], "hello world\n", 12);

    } else {                /* child */
        close(fd[1]);
        n = read(fd[0], line, MAXLINE);
        write(STDOUT_FILENO, line, n);
    }

    exit(0);
}

Nyní jsme již schopni nahradit funkce TELL_WAIT, ...z kap. "Podmínky závodu" konkrétní implementací. Pro komunikaci tentokrát využijeme roury.

Příklad:

static int    pfd1[2], pfd2[2];

void
TELL_WAIT()
{
    if (pipe(pfd1) < 0 || pipe(pfd2) < 0)
        err_sys("pipe error");
}

void
TELL_PARENT(pid_t pid)
{
    if (write(pfd2[1], "c", 1) != 1)
        err_sys("write error");
}

void
WAIT_PARENT(void)
{
    char    c;

    if (read(pfd1[0], &c, 1) != 1)
        err_sys("read error");
    if (c != 'p')
        err_quit("WAIT_PARENT: incorrect data");
}

void
TELL_CHILD(pid_t pid)
{
    if (write(pfd1[1], "p", 1) != 1)
        err_sys("write error");
}

void
WAIT_CHILD(void)
{
    char    c;

    if (read(pfd2[0], &c, 1) != 1)
        err_sys("read error");
    if (c != 'c')
        err_quit("WAIT_CHILD: incorrect data");
}



Funkce popen a pclose

Ačkoliv se většinou pipe využívá pro meziprocesní komunikaci, lze roury s úspěchem použít i pro I/O operace. Tyto funkce zajišťují špinavou práci -- vytvoření pipe, fork potomka, uzavření nadbytečných konců a volání shellu pro vykonání příkazu.

#include <stdio.h>
FILE *popen (const char *cmdstring, const char *type);
Vrací: ukazatel když OK, NULL při chybě
int pclose (FILE *fp);
Vrací: status ukončení dle cmdstring nebo -1 při chybě

Činnost je nejlépe patrná z obrázků 14 a 15.

Zde má být moc hezký obrázek 'popenr', škoda, že ho nevidíte

Obrázek 14: Výsledek po fp=popen(cmdstring, "r")

Zde má být moc hezký obrázek 'popenw', škoda, že ho nevidíte

Obrázek 14: Výsledek po fp=popen(cmdstring, "w")

Příkazový řetězec je interpretován shellem. Jestliže nemůže být shell vyvolán, nastaví se návratový kód na 127. Řetězec se vlastně provádí jako:

sh -c cmdstring

Proto lze při tomto zpracování plně využívat hvězdičkovou konvenci i ostatní substituce shellu.

    fp = popen ("ls *.c", "r");

Jestliže tedy chceme posílat výstupy našeho programu do nějakého stránkovače, můžeme využít IPC a např. program more.

Příklad:

#include    <sys/wait.h>

#define    PAGER    "${PAGER:-more}"
           /* environment variable, or default */

int
main(int argc, char *argv[])
{
    char    line[MAXLINE];
    FILE    *fpin, *fpout;

    if (argc != 2)
        err_quit("usage: a.out <pathname>");
    if ( (fpin = fopen(argv[1], "r")) == NULL)
        err_sys("can't open %s", argv[1]);

    if ( (fpout = popen(PAGER, "w")) == NULL)
        err_sys("popen error");

        /* copy argv[1] to pager */
    while (fgets(line, MAXLINE, fpin) != NULL) {
        if (fputs(line, fpout) == EOF)
            err_sys("fputs error to pipe");
    }
    if (ferror(fpin))
        err_sys("fgets error");
    if (pclose(fpout) == -1)
        err_sys("pclose error");
    exit(0);
}



Koprocesy

Unixové filtry jsou takové programy, které čtou standardní vstup a zapisují do standardního výstupu. Filtry se normálně spojují lineárně za sebou. Filtr se stane koprocesem, když nějaký program generuje jeho vstup a zároveň čte jeho výstup.

Koprocesy (spolupracující oboustranně komunikující procesy) nabízí také Korn shell.

Na obr. 16 je vidět, jak probíhá komunikace s koprocesem.

Zde má být moc hezký obrázek 'koproces', škoda, že ho nevidíte

Obrázek 16: Ovládání koprocesu

Celou činnost lze také pochopit z příkladu.

Příklad:

int
main(void)
{
    int     n, int1, int2;
    char    line[MAXLINE];

    while ( (n = read(STDIN_FILENO, line, MAXLINE)) > 0) {
        line[n] = 0;        /* null terminate */
        if (sscanf(line, "%d%d", &int1, &int2) == 2) {
            sprintf(line, "%d\n", int1 + int2);
            n = strlen(line);
            if (write(STDOUT_FILENO, line, n) != n)
                err_sys("write error");
        } else {
            if (write(STDOUT_FILENO, "invalid args\n", 13) != 13)
                err_sys("write error");
        }
    }
    exit(0);
}

Tento program nedělá nic jiného, než že sečte dvojici čísel ze vstupu a výsledek pošle na výstup. Pro ilustraci koprocesů musíme přidat jakýsi driver pro obsluhu tohoto programu.

Příklad:

#include    <signal.h>

static void    sig_pipe(int);        /* our signal handler */

int
main(void)
{
    int     n, fd1[2], fd2[2];
    pid_t   pid;
    char    line[MAXLINE];

    if (signal(SIGPIPE, sig_pipe) == SIG_ERR)
        err_sys("signal error");

    if (pipe(fd1) < 0 || pipe(fd2) < 0)
        err_sys("pipe error");

    if ( (pid = fork()) < 0)
        err_sys("fork error");
    else if (pid > 0) {                            /* parent */
        close(fd1[0]);
        close(fd2[1]);
        while (fgets(line, MAXLINE, stdin) != NULL) {
            n = strlen(line);
            if (write(fd1[1], line, n) != n)
                err_sys("write error to pipe");
            if ( (n = read(fd2[0], line, MAXLINE)) < 0)
                err_sys("read error from pipe");
            if (n == 0) {
                err_msg("child closed pipe");
                break;
            }
            line[n] = 0;    /* null terminate */
            if (fputs(line, stdout) == EOF)
                err_sys("fputs error");
        }
        if (ferror(stdin))
            err_sys("fgets error on stdin");
        exit(0);

    } else {                                    /* child */
        close(fd1[1]);
        close(fd2[0]);
        if (fd1[0] != STDIN_FILENO) {
            if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO)
                err_sys("dup2 error to stdin");
            close(fd1[0]);
        }
        if (fd2[1] != STDOUT_FILENO) {
            if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO)
                err_sys("dup2 error to stdout");
            close(fd2[1]);
        }
        if (execl("./add2", "add2", (char *) 0) < 0)
            err_sys("execl error");
    }
}

static void
sig_pipe(int signo)
{
    printf("SIGPIPE caught\n");
    exit(1);
}

Toto schéma je v unixu často využíváno. Je však třeba si dát pozor na bufferování standardních I/O operací.

V našem koprocesu jsme použili funkce read a write. Kdybychom přepsali program pomocí fgets a printf, tak by program nefungoval, pokud bychom nenastavili nulový buffer pomocí:

  if (setvbuf(stdin, NULL, _IOLBF, 0) != 0)
      err_sys ("setvbuf error");
  if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
      err_sys ("setvbuf error");

Tímto bychom sice vyřešili problém pro náš případ, ale co máme dělat, když jako koproces chceme tento program pro awk, který řeší stejný problém?

  #! /bin/awk -f
  { print $1 + $2 }

Zde, protože nemůžeme předělat zdrojový kód awku, je jediným řešením připojit standardní vstup a výstup k tzv. pseudoterminálu (o něm viz literaturu [7], kapitola 19).



  Funkce fifo

Tento způsob meziprocesní komunikace se také někdy nazývá pojmenované roury. Standardní roury mohou požívat pouze procesy se shodným předchůdcem. FIFO mohou používat i procesy, které spolu jinak nesouvisí. V kapitole o filesystemu (kap. "Soubory a adresáře") jsme ukázali, že FIFO je typ souboru. Vytvoření FIFO je tedy obdobné vytvoření souboru.

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo (const char pathname, mode_t mode);
Vrací: 0 když OK, -1 při chybě

Specifikace argumentu mode je shodná s funkcí open (viz kap. Funkce open). Po vytvoření FIFO jej musíme otevřít funkcí open. Pak už s ním můžeme pracovat jako s každým jiným souborem (read, write, ...).

V unixu se standardně FIFO používá pro:

Příklad:

FIFO můžeme využít pro duplikování výstupního toku dat. S použitím příkazu tee můžeme pak posílat vystup jednoho programu dvěma dalším. (Program tee kopíruje standardní vstup na standardní výstup a zároveň do daného souboru.)

Zápis v shellu bude vypadat takto:

    mkfifo fifo1
    prog3 < fifo1&
    prog1 < infile | tee fifo1 | prog2

Výsledek můžete vidět na obrázku 17.

Zde má být moc hezký obrázek 'teepipe', škoda, že ho nevidíte

Obrázek 17: Posílání proudu dvěma procesům pomocí FIFO a tee

Příklad použití v sytému client-server nám dává schéma na obr. 18.

Zde má být moc hezký obrázek 'client', škoda, že ho nevidíte

Obrázek 18: Příklad komunikace klient-server pomocí FIFO



  System V IPC

Jednotlivé prostředky pro meziprocesní komunikaci mají mnoho společného. V Systemu V existují tři základní typy IPC -- zprávy, fronty a semafory. Někdy se také zahrnuje do výčtu sdílená paměť. Poprvé byly tyto rysy implementovány do interní verze Unixu Columbus v sedmdesátých letech.



Identifikátory a klíče

Každá IPC struktura v jádře je referencována pomocí nezáporného celého čísla. Toto číslo se nazývá identifikátor. Na rozdíl od popisovačů souborů unix nezajišťuje alokaci nejmenšího volného identifikátoru, ale postupně identifikátory zvyšuje.

Kdykoliv vytvoříme IPC strukturu, musíme specifikovat klíč. Klíč slouží k přístupu k dané struktuře. Pro přístup k existující struktuře musí být klíč shodný s klíčem zadaným při vytváření struktury.

Existuje několik variant komunikačních scénářů:

  1. Server vytvoří IPC strukturu, klíč nastaví na IPC_PRIVATE a uloží návratový identifikátor tak, aby byl pro klienta dostupný. Klíč lze předat pomocí souboru nebo pomocí proměnné před provedením fork.
  2. Klient i server se dohodnou na nějakém klíči. Server pak vytvoří strukturu s tímto klíčem. Problémy nastanou, když je klíč obsazen.
  3. Klient i server se dohodnou na společném čísle projektu a cestě. Použijí pak funkci ftok pro konverzi těchto hodnot do klíče. Funkce ftok je popsána v stdipc(3).

Tři IPC funkce mají jednoduché argumenty -- key a flag. Nová IPC struktura je vytvořena, jestliže:

  1. key je IPC_PRIVATE nebo
  2. key není momentálně spojen s IPC strukturou daného typu je zadán příznak IPC_CREATE.

Odkaz na klíč (obvykle zajišťuje klient) musí být shodný se specifikovaným klíčem a bit IPC_CREAT nemusí být nastaven.



Výhody a nevýhody

Základním problémem IPC v Systemu V spočívá v tom, že struktury jsou volně přístupné a nemají referenční počítadlo. Např. jestliže si vytvoříte frontu a pak ji ukončíte, tato fronta a její obsah zůstanou zachovány až do té doby, než je obsah přečten nebo vymazán.

Dalším problémem je to, že IPC struktury nejsou pod svým jménem známy filesystému. Proto nemůžeme modifikovat jejich zdroje, ani na ně použít nástroje z předchozích kapitol.

V tabulce 14 jsou shrnuty hlavní výhody jednotlivých typů IPC. Z tabulky není patrna další nesporná výhoda a totiž to, že IPC provádí unikátní spojení pro každého klienta.

 

Typ IPC bez spojení spolehlivé řízení toku záznamy typy zpráv
či priority
fronty ne ano ano ano ano
streams ne ano ano ano ano
stream socket ne ano ano ne ne
datagram socket ano ano ne ano ne
FIFO ne ano ano ne ne
Tabulka 14: Porovnání vlastností různých typů IPC

Sokety i proudové roury patří již do kapitoly síťového programování (viz kapitolu "Síťování").



Fronty

Fronta je seznam zpráv navzájem provázaných odkazy, ukládán pomocí jádra. Každá fronta má unikátní identifikátor. Nová fronta se vytvoří funkcí msgget. Stejnou funkcí se otevře i již existující fronta. Informace se do fronty zapisují funkcí msgsnd a vybírají pomocí msgrcv. Nemusíme nutně vybírat zprávy metodou FIFO, ale můžeme je vybírat v závislosti na jejich typu. Ke každé frontě je přidělena odpovídající struktura msqid_ds (popsána v <sys/msg.h>).

První funkcí zpravidla bývá msgget, která zajistí otevření nebo vytvoření fronty.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget (key_t key, int flag);
Vrací: identifikátor fronty když OK, -1 při chybě

Kapitola "System V IPC" popisuje způsob konverze klíče do identifikátoru. V případě vytvoření nové fronty jsou inicializovány některé důležité prvky struktury msqid_ds.

Se založenou frontou lze provádět některé řídící operace. K tomu slouží funkce msgctl.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl (int msqid, int cmd, struct msqid_ds *buf);
Vrací: 0 když OK, -1 při chybě

Parametr cmd určuje, jaká operace se bude provádět. Tyto tři akce lze provádět i pro semafory a sdílenou paměť:

IPC_STAT Načtení řídící struktury do buf.
IPC_SET Nastavení některých polí struktury IPC.
IPC_RMID Odstranění fronty ze systému.

Data zapisujeme do fronty funkcí msgsnd.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd (int msqid, const void *ptr, size_t nbytes, int flag);
Vrací: 0 když OK, -1 při chybě

Argumenty specifikují identifikátor fronty, ukazatel na začátek fronty, velikost zprávy a příznaky. Příznak může být nastaven na IPC_NOWAIT, což je vlastně ekvivalent neblokujícího příznaku používaného u I/O funkcí.

Je výhodné zavést jakési typy zpráv (pro priority). Proto struktura naší zprávy může vypadat např. takto:

  struct mymesg {
    long mtype;
    char mtext[512];
  }

Zprávy z fronty vybírá funkce msgrcv.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv (int msqid, void *ptr, size_t nbytes, long type, int flag);
Vrací: délku zprávy když OK, -1 při chybě

Stejně jako u msgsnd, ukazatel ptr ukazuje na long, kde je uložen typ zprávy. Argument type může nabývat následujících hodnot:



Semafory

Semafory nejsou pravou formou IPC, ale často se využívají jako jednoduchý synchronizační prostředek. Semafor obecně je vlastně jen počítadlo určené k přidělování zdrojů. Proto s ním lze dělat tyto věci:

Pro korektní implementaci semaforů je nutné zaručit, aby operace nad semafory byly atomické. Nejjednodušší typ semaforu je tzv. binární semafor -- tj. semafor, který kontroluje přístup právě k jednomu objektu.

V Systemu V jsou semafory komplikovanější:

  1. Semafory nejsou jen nezáporné číslo, ale množina hodnot. Při vytváření semaforu určíme počet těchto hodnot.
  2. Vytvoření semaforu a jeho nastavení není atomická operace.
  3. Je zde undo funkce -- pro zamezení zamrznutí semaforu v systému.

Ke každému semaforu udržuje jádro odpovídající strukturu semid_ds (je popsána v <sys/sem.h>).

První funkcí bývá zpravidla vytvoření semaforu -- semget.

type == 0 Vrátí první zprávu z fronty.
type > 0 Vrátí první zprávu daného typu.
type < 0 Vrátí první zprávu nejnižšího typu, nižšího, než abs(type).
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget (key_t key, int nsems, int flag);
Vrací: identifikátor semaforu když OK, -1 při chybě

Argument nsems určuje počet semaforů v množině. Při vytváření semaforu se musí tato položka specifikovat. Při odkazu na existující semafor stačí dosadit 0.

Řídící operace nad semaforem zajišťuje funkce semctl.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl (int semid, int semnum, int cmd, union semun arg);
Vrací: závisí na operaci

Všimněte si, že poslední argument je union, nikoli ukazatel na union.

union semun {
int                val;   /* pro SETVAL */
struct semuid_ds  *buf;   /* pro IPC_STAT a IPC_SET */
ushort            *array; /* pro GATALL a SETALL */
}

V následujícím přehledu jsou vyjmenovány některé z řídících operací nad semaforem.

IPC_STAT Načtení řídící struktury semaforu.
IPC_SET Nastavení některých polí struktury IPC.
IPC_RMID Odstranění semaforu ze systému.
GETVAL Zjištění hodnoty semaforu.
SETVAL Nastavení hodnoty semaforu.
GETPID Zjištění sempid.

Nejdůležitější jsou ale operace nad semafory -- funkce semop.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop (int semid, struct sembuf semoparray[], size_t nops);
Vrací: 0 když OK, -1 při chybě

Argument semarr je vlastně ukazatel na pole operací nad semaforem.

  struct sembuf {
    ushort sem_num; /* číslo semaforu v sadě */
    short  sem_op;  /* operace nad semaforem */
    short  sem_flg; /* IPC_NOWAIT, SEM_UNDO */
  }

  1. Nejjednodušší operace nad semaforem je v případě, že sem_op je kladná. K semaforu se jen přičte hodnota sem_op.
  2. Jestliže sem_op<0, znamená to, že proces vyžaduje zdroje.

    Je-li semafor kladný a větší, než absolutní hodnota sem_op, pak se odečte sem_op a proces běží dál.

    Jinak ale

    1. Jestliže je nastaven příznak IPC_NOWAIT, je vrácena chyba EAGAIN.
    2. Není-li dán příznak IPC_NOWAIT, je proces uspán až do splnění některé z následujících podmínek.
      1. Hodnota semaforu je větší, než absolutní hodnota sem_op.
      2. Semafor je odstraněn ze systému (chyba ERMID).
      3. Proces dostane signál (chyba EINTR).
  3. Je-li sem_op=0, znamená to, že chceme čekat, dokud se semafor nevynuluje.

    Jestliže je semafor 0, funkce se vrátí okamžitě.

    Pokud je nenulový, pak

    1. jestliže je nastaven příznak IPC_NOWAIT, je vrácena chyba EAGAIN.
    2. není-li dán příznak IPC_NOWAIT, je proces uspán až do splnění některé z následujících podmínek.
      1. Semafor se vynuluje.
      2. Semafor je odstraněn ze systému (chyba ERMID).
      3. Proces dostane signál (chyba EINTR).

Atomická povaha funkce sem_op je zaručena.



Nastavení semaforů při volání exit

Zvláštní problém představuje definování hodnoty semaforu při ukončení procesu. Existuje totiž možnost zablokování ostatních procesů nesprávným ukončením jednoho z procesů sdílejících semafor.

Tento problém řeší příznak SEM_UNDO. V případě ukončení procesu jádro rozpozná, kolik zdrojů proces alokoval a upraví hodnotu semaforu.



Sdílená paměť

Sdílená paměť umožňuje dvěma nebo i více procesům sdílet část adresního prostoru. Tato forma IPC je ze všech nejrychlejší, protože není nutné kopírovat data při komunikaci typu client-server. Jediné úskalí této metody spočívá v nutnosti synchronizování přístupu k paměti.

Proces o přidělení paměti požádá funkcí shmget.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget (key_t key, int size, int flag);
Vrací: identifikátor sdílené paměti když OK, -1 při chybě

Nad blokem sdílené paměti lze opět provádět řídící operace:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl (int shmid, int cmd, struct shmid_ds *buf);
Vrací: 0 když OK, -1 při chybě

Popis struktury shmid_ds je v <sys/shm.h>.

Parametr cmd opět určuje, jaká operace se bude provádět.

IPC_STAT Načtení řídící struktury sdílené paměti.
IPC_SET Nastavení některých polí struktury IPC.
IPC_RMID Odstranění sdílené paměti ze systému.
SHM_LOCK Uzamčení sdílené paměti (jen superuživatel).
SHM_UNLOCK Odemčení sdílené paměti (jen superuživatel).

Jakmile je jednou sdílený blok paměti vytvořen, je možné ho připojit do adresního prostoru funkcí shmat.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
void *shmat (int shmid, void *addr, int flag);
Vrací: ukazatel na sdílenou paměť když OK, -1 při chybě

Připojení segmentu je závislé na hodnotě argumentu addr a nastavení příznaku.

Obecně mohou nastat tyto příznaky:

  1. addr == 0. Segment je připojen na první dostupnou adresu v adresovém prostoru úlohy (doporučená technika).
  2. addr != 0 a SHM_RND není specifikován. Segment je připojen na zadanou adresu.
  3. addr != 0 a je nastaven SHM_RND. Segment je připojen na adresu . SHM_RND znamená zaokrouhluj a SHMLBA bývá mocnina 2.

Pro ukončení práce se sdílenou pamětí ji můžeme odpojit.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt (void *addr);
Vrací: 0 když OK, -1 při chybě

Příklad:

#include    <sys/types.h>
#include    <sys/ipc.h>
#include    <sys/shm.h>

#define    ARRAY_SIZE    40000
#define    MALLOC_SIZE   100000
#define    SHM_SIZE      100000
#define    SHM_MODE      (SHM_R | SHM_W)    /* user read/write */

char    array[ARRAY_SIZE];    /* uninitialized data = bss */

int
main(void)
{
    int        shmid;
    char    *ptr, *shmptr;

    printf("array[] from %x to %x\n", &array[0], &array[ARRAY_SIZE]);
    printf("stack around %x\n", &shmid);

    if ( (ptr = malloc(MALLOC_SIZE)) == NULL)
        err_sys("malloc error");
    printf("malloced from %x to %x\n", ptr, ptr+MALLOC_SIZE);

    if ( (shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0)
        err_sys("shmget error");
    if ( (shmptr = shmat(shmid, 0, 0)) == (void *) -1)
        err_sys("shmat error");
    printf("shared memory attached from %x to %x\n",
                shmptr, shmptr+SHM_SIZE);
    if (shmctl(shmid, IPC_RMID, 0) < 0)
        err_sys("shmctl error");

    exit(0);
}



Cvičení

  1. Co se stane, když argument pro popen je neexistující příkaz? Napište malý program, který to otestuje.


další předchozí obsah
Další: Síťování Předchozí: Meziprocesní komunikace

Ladislav Dobias
Sat Nov 1 15:38:32 MET 1997