dalsi predchozi obsah
Dalsi: Sitovani Predchozi: Meziprocesni komunikace

  Meziprocesni komunikace





Uvod

Predchozi kapitola ukazuje primitiva nutna pro rizeni procesu. Jestlize ale tyto procesy jiz bezi, je nutne zajistit mezi nimi komunikaci. Unix podporuje sirokou skalu prostredku pro komunikaci mezi procesy (IPC -- InterProcess Communination).



  Roury

Roury (pipes) jsou nejstarsim komunikacnim prostredkem v unixu. Jsou podporovany vsemi typy unixu za techto omezujicich podminek:

V kapitole "Funkce socketpair si popiseme proudove roury, ktere eliminuji prvni omezeni, a pred pojmenovanymi rourami neobstoji ani druhe omezeni, viz kap. "Funkce fifo".

Roura se vytvori vyvolanim funkce pipe.

#include <unistd.h>
int pipe (int filedes[2]);
Vraci: 0 kdyz OK, -1 pri chybe

Funkce vrati dva deskriptory pomoci argumentu filedes. Deskriptor filedes[0] je otevren pro cteni a filedes[1] pro zapis. Pro schematicke znazorneni roury muzeme pouzit dva zpusoby, jak je vidno z obr. 11.

Zde ma byt moc hezky obrazek 'pipe1', skoda, ze ho nevidite

Obrazek 11: Dva zpusoby zobrazeni unixove roury

Roura je u jednoho procesu bezvyznamna. Proto se casto pouziva volani pipe pred fork. Bezny scenar ilustruje obrazek 12.

Zde ma byt moc hezky obrazek 'pipefork', skoda, ze ho nevidite

Obrazek 12: Poloduplexni roura po fork

Procesy pak musi zabezpecit uzavreni nepouzivanych koncu. Viz obr. 13.

Zde ma byt moc hezky obrazek 'pipepch', skoda, ze ho nevidite

Obrazek 13: Roura od rodice k potomkovi

Kdyz je jeden konec roury zavreny, deje se nasledujici:

Priklad:

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);
}

Nyni jsme jiz schopni nahradit funkce TELL_WAIT, ...z kap. "Podminky zavodu" konkretni implementaci. Pro komunikaci tentokrat vyuzijeme roury.

Priklad:

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

Ackoliv se vetsinou pipe vyuziva pro meziprocesni komunikaci, lze roury s uspechem pouzit i pro I/O operace. Tyto funkce zajistuji spinavou praci -- vytvoreni pipe, fork potomka, uzavreni nadbytecnych koncu a volani shellu pro vykonani prikazu.

#include <stdio.h>
FILE *popen (const char *cmdstring, const char *type);
Vraci: ukazatel kdyz OK, NULL pri chybe
int pclose (FILE *fp);
Vraci: status ukonceni dle cmdstring nebo -1 pri chybe

Cinnost je nejlepe patrna z obrazku 14 a 15.

Zde ma byt moc hezky obrazek 'popenr', skoda, ze ho nevidite

Obrazek 14: Vysledek po fp=popen(cmdstring, "r")

Zde ma byt moc hezky obrazek 'popenw', skoda, ze ho nevidite

Obrazek 14: Vysledek po fp=popen(cmdstring, "w")

Prikazovy retezec je interpretovan shellem. Jestlize nemuze byt shell vyvolan, nastavi se navratovy kod na 127. Retezec se vlastne provadi jako:

sh -c cmdstring

Proto lze pri tomto zpracovani plne vyuzivat hvezdickovou konvenci i ostatni substituce shellu.

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

Jestlize tedy chceme posilat vystupy naseho programu do nejakeho strankovace, muzeme vyuzit IPC a napr. program more.

Priklad:

#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

Unixove filtry jsou takove programy, ktere ctou standardni vstup a zapisuji do standardniho vystupu. Filtry se normalne spojuji linearne za sebou. Filtr se stane koprocesem, kdyz nejaky program generuje jeho vstup a zaroven cte jeho vystup.

Koprocesy (spolupracujici oboustranne komunikujici procesy) nabizi take Korn shell.

Na obr. 16 je videt, jak probiha komunikace s koprocesem.

Zde ma byt moc hezky obrazek 'koproces', skoda, ze ho nevidite

Obrazek 16: Ovladani koprocesu

Celou cinnost lze take pochopit z prikladu.

Priklad:

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 nedela nic jineho, nez ze secte dvojici cisel ze vstupu a vysledek posle na vystup. Pro ilustraci koprocesu musime pridat jakysi driver pro obsluhu tohoto programu.

Priklad:

#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 schema je v unixu casto vyuzivano. Je vsak treba si dat pozor na bufferovani standardnich I/O operaci.

V nasem koprocesu jsme pouzili funkce read a write. Kdybychom prepsali program pomoci fgets a printf, tak by program nefungoval, pokud bychom nenastavili nulovy buffer pomoci:

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

Timto bychom sice vyresili problem pro nas pripad, ale co mame delat, kdyz jako koproces chceme tento program pro awk, ktery resi stejny problem?

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

Zde, protoze nemuzeme predelat zdrojovy kod awku, je jedinym resenim pripojit standardni vstup a vystup k tzv. pseudoterminalu (o nem viz literaturu [7], kapitola 19).



  Funkce fifo

Tento zpusob meziprocesni komunikace se take nekdy nazyva pojmenovane roury. Standardni roury mohou pozivat pouze procesy se shodnym predchudcem. FIFO mohou pouzivat i procesy, ktere spolu jinak nesouvisi. V kapitole o filesystemu (kap. "Soubory a adresare") jsme ukazali, ze FIFO je typ souboru. Vytvoreni FIFO je tedy obdobne vytvoreni souboru.

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo (const char pathname, mode_t mode);
Vraci: 0 kdyz OK, -1 pri chybe

Specifikace argumentu mode je shodna s funkci open (viz kap. Funkce open). Po vytvoreni FIFO jej musime otevrit funkci open. Pak uz s nim muzeme pracovat jako s kazdym jinym souborem (read, write, ...).

V unixu se standardne FIFO pouziva pro:

Priklad:

FIFO muzeme vyuzit pro duplikovani vystupniho toku dat. S pouzitim prikazu tee muzeme pak posilat vystup jednoho programu dvema dalsim. (Program tee kopiruje standardni vstup na standardni vystup a zaroven do daneho souboru.)

Zapis v shellu bude vypadat takto:

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

Vysledek muzete videt na obrazku 17.

Zde ma byt moc hezky obrazek 'teepipe', skoda, ze ho nevidite

Obrazek 17: Posilani proudu dvema procesum pomoci FIFO a tee

Priklad pouziti v sytemu client-server nam dava schema na obr. 18.

Zde ma byt moc hezky obrazek 'client', skoda, ze ho nevidite

Obrazek 18: Priklad komunikace klient-server pomoci FIFO



  System V IPC

Jednotlive prostredky pro meziprocesni komunikaci maji mnoho spolecneho. V Systemu V existuji tri zakladni typy IPC -- zpravy, fronty a semafory. Nekdy se take zahrnuje do vyctu sdilena pamet. Poprve byly tyto rysy implementovany do interni verze Unixu Columbus v sedmdesatych letech.



Identifikatory a klice

Kazda IPC struktura v jadre je referencovana pomoci nezaporneho celeho cisla. Toto cislo se nazyva identifikator. Na rozdil od popisovacu souboru unix nezajistuje alokaci nejmensiho volneho identifikatoru, ale postupne identifikatory zvysuje.

Kdykoliv vytvorime IPC strukturu, musime specifikovat klic. Klic slouzi k pristupu k dane strukture. Pro pristup k existujici strukture musi byt klic shodny s klicem zadanym pri vytvareni struktury.

Existuje nekolik variant komunikacnich scenaru:

  1. Server vytvori IPC strukturu, klic nastavi na IPC_PRIVATE a ulozi navratovy identifikator tak, aby byl pro klienta dostupny. Klic lze predat pomoci souboru nebo pomoci promenne pred provedenim fork.
  2. Klient i server se dohodnou na nejakem klici. Server pak vytvori strukturu s timto klicem. Problemy nastanou, kdyz je klic obsazen.
  3. Klient i server se dohodnou na spolecnem cisle projektu a ceste. Pouziji pak funkci ftok pro konverzi techto hodnot do klice. Funkce ftok je popsana v stdipc(3).

Tri IPC funkce maji jednoduche argumenty -- key a flag. Nova IPC struktura je vytvorena, jestlize:

  1. key je IPC_PRIVATE nebo
  2. key neni momentalne spojen s IPC strukturou daneho typu je zadan priznak IPC_CREATE.

Odkaz na klic (obvykle zajistuje klient) musi byt shodny se specifikovanym klicem a bit IPC_CREAT nemusi byt nastaven.



Vyhody a nevyhody

Zakladnim problemem IPC v Systemu V spociva v tom, ze struktury jsou volne pristupne a nemaji referencni pocitadlo. Napr. jestlize si vytvorite frontu a pak ji ukoncite, tato fronta a jeji obsah zustanou zachovany az do te doby, nez je obsah precten nebo vymazan.

Dalsim problemem je to, ze IPC struktury nejsou pod svym jmenem znamy filesystemu. Proto nemuzeme modifikovat jejich zdroje, ani na ne pouzit nastroje z predchozich kapitol.

V tabulce 14 jsou shrnuty hlavni vyhody jednotlivych typu IPC. Z tabulky neni patrna dalsi nesporna vyhoda a totiz to, ze IPC provadi unikatni spojeni pro kazdeho klienta.

 

Typ IPC bez spojeni spolehlive rizeni toku zaznamy typy zprav
ci 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: Porovnani vlastnosti ruznych typu IPC

Sokety i proudove roury patri jiz do kapitoly sitoveho programovani (viz kapitolu "Sitovani").



Fronty

Fronta je seznam zprav navzajem provazanych odkazy, ukladan pomoci jadra. Kazda fronta ma unikatni identifikator. Nova fronta se vytvori funkci msgget. Stejnou funkci se otevre i jiz existujici fronta. Informace se do fronty zapisuji funkci msgsnd a vybiraji pomoci msgrcv. Nemusime nutne vybirat zpravy metodou FIFO, ale muzeme je vybirat v zavislosti na jejich typu. Ke kazde fronte je pridelena odpovidajici struktura msqid_ds (popsana v <sys/msg.h>).

Prvni funkci zpravidla byva msgget, ktera zajisti otevreni nebo vytvoreni fronty.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget (key_t key, int flag);
Vraci: identifikator fronty kdyz OK, -1 pri chybe

Kapitola "System V IPC" popisuje zpusob konverze klice do identifikatoru. V pripade vytvoreni nove fronty jsou inicializovany nektere dulezite prvky struktury msqid_ds.

Se zalozenou frontou lze provadet nektere ridici operace. K tomu slouzi funkce msgctl.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl (int msqid, int cmd, struct msqid_ds *buf);
Vraci: 0 kdyz OK, -1 pri chybe

Parametr cmd urcuje, jaka operace se bude provadet. Tyto tri akce lze provadet i pro semafory a sdilenou pamet:

IPC_STAT Nacteni ridici struktury do buf.
IPC_SET Nastaveni nekterych poli struktury IPC.
IPC_RMID Odstraneni fronty ze systemu.

Data zapisujeme do fronty funkci 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);
Vraci: 0 kdyz OK, -1 pri chybe

Argumenty specifikuji identifikator fronty, ukazatel na zacatek fronty, velikost zpravy a priznaky. Priznak muze byt nastaven na IPC_NOWAIT, coz je vlastne ekvivalent neblokujiciho priznaku pouzivaneho u I/O funkci.

Je vyhodne zavest jakesi typy zprav (pro priority). Proto struktura nasi zpravy muze vypadat napr. takto:

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

Zpravy z fronty vybira 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);
Vraci: delku zpravy kdyz OK, -1 pri chybe

Stejne jako u msgsnd, ukazatel ptr ukazuje na long, kde je ulozen typ zpravy. Argument type muze nabyvat nasledujicich hodnot:



Semafory

Semafory nejsou pravou formou IPC, ale casto se vyuzivaji jako jednoduchy synchronizacni prostredek. Semafor obecne je vlastne jen pocitadlo urcene k pridelovani zdroju. Proto s nim lze delat tyto veci:

Pro korektni implementaci semaforu je nutne zarucit, aby operace nad semafory byly atomicke. Nejjednodussi typ semaforu je tzv. binarni semafor -- tj. semafor, ktery kontroluje pristup prave k jednomu objektu.

V Systemu V jsou semafory komplikovanejsi:

  1. Semafory nejsou jen nezaporne cislo, ale mnozina hodnot. Pri vytvareni semaforu urcime pocet techto hodnot.
  2. Vytvoreni semaforu a jeho nastaveni neni atomicka operace.
  3. Je zde undo funkce -- pro zamezeni zamrznuti semaforu v systemu.

Ke kazdemu semaforu udrzuje jadro odpovidajici strukturu semid_ds (je popsana v <sys/sem.h>).

Prvni funkci byva zpravidla vytvoreni semaforu -- semget.

type == 0 Vrati prvni zpravu z fronty.
type > 0 Vrati prvni zpravu daneho typu.
type < 0 Vrati prvni zpravu nejnizsiho typu, nizsiho, nez abs(type).
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget (key_t key, int nsems, int flag);
Vraci: identifikator semaforu kdyz OK, -1 pri chybe

Argument nsems urcuje pocet semaforu v mnozine. Pri vytvareni semaforu se musi tato polozka specifikovat. Pri odkazu na existujici semafor staci dosadit 0.

Ridici operace nad semaforem zajistuje 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);
Vraci: zavisi na operaci

Vsimnete si, ze posledni 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 nasledujicim prehledu jsou vyjmenovany nektere z ridicich operaci nad semaforem.

IPC_STAT Nacteni ridici struktury semaforu.
IPC_SET Nastaveni nekterych poli struktury IPC.
IPC_RMID Odstraneni semaforu ze systemu.
GETVAL Zjisteni hodnoty semaforu.
SETVAL Nastaveni hodnoty semaforu.
GETPID Zjisteni sempid.

Nejdulezitejsi 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);
Vraci: 0 kdyz OK, -1 pri chybe

Argument semarr je vlastne ukazatel na pole operaci nad semaforem.

  struct sembuf {
    ushort sem_num; /* cislo semaforu v sade */
    short  sem_op;  /* operace nad semaforem */
    short  sem_flg; /* IPC_NOWAIT, SEM_UNDO */
  }

  1. Nejjednodussi operace nad semaforem je v pripade, ze sem_op je kladna. K semaforu se jen pricte hodnota sem_op.
  2. Jestlize sem_op<0, znamena to, ze proces vyzaduje zdroje.

    Je-li semafor kladny a vetsi, nez absolutni hodnota sem_op, pak se odecte sem_op a proces bezi dal.

    Jinak ale

    1. Jestlize je nastaven priznak IPC_NOWAIT, je vracena chyba EAGAIN.
    2. Neni-li dan priznak IPC_NOWAIT, je proces uspan az do splneni nektere z nasledujicich podminek.
      1. Hodnota semaforu je vetsi, nez absolutni hodnota sem_op.
      2. Semafor je odstranen ze systemu (chyba ERMID).
      3. Proces dostane signal (chyba EINTR).
  3. Je-li sem_op=0, znamena to, ze chceme cekat, dokud se semafor nevynuluje.

    Jestlize je semafor 0, funkce se vrati okamzite.

    Pokud je nenulovy, pak

    1. jestlize je nastaven priznak IPC_NOWAIT, je vracena chyba EAGAIN.
    2. neni-li dan priznak IPC_NOWAIT, je proces uspan az do splneni nektere z nasledujicich podminek.
      1. Semafor se vynuluje.
      2. Semafor je odstranen ze systemu (chyba ERMID).
      3. Proces dostane signal (chyba EINTR).

Atomicka povaha funkce sem_op je zarucena.



Nastaveni semaforu pri volani exit

Zvlastni problem predstavuje definovani hodnoty semaforu pri ukonceni procesu. Existuje totiz moznost zablokovani ostatnich procesu nespravnym ukoncenim jednoho z procesu sdilejicich semafor.

Tento problem resi priznak SEM_UNDO. V pripade ukonceni procesu jadro rozpozna, kolik zdroju proces alokoval a upravi hodnotu semaforu.



Sdilena pamet

Sdilena pamet umoznuje dvema nebo i vice procesum sdilet cast adresniho prostoru. Tato forma IPC je ze vsech nejrychlejsi, protoze neni nutne kopirovat data pri komunikaci typu client-server. Jedine uskali teto metody spociva v nutnosti synchronizovani pristupu k pameti.

Proces o prideleni pameti pozada funkci shmget.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget (key_t key, int size, int flag);
Vraci: identifikator sdilene pameti kdyz OK, -1 pri chybe

Nad blokem sdilene pameti lze opet provadet ridici operace:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl (int shmid, int cmd, struct shmid_ds *buf);
Vraci: 0 kdyz OK, -1 pri chybe

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

Parametr cmd opet urcuje, jaka operace se bude provadet.

IPC_STAT Nacteni ridici struktury sdilene pameti.
IPC_SET Nastaveni nekterych poli struktury IPC.
IPC_RMID Odstraneni sdilene pameti ze systemu.
SHM_LOCK Uzamceni sdilene pameti (jen superuzivatel).
SHM_UNLOCK Odemceni sdilene pameti (jen superuzivatel).

Jakmile je jednou sdileny blok pameti vytvoren, je mozne ho pripojit do adresniho prostoru funkci shmat.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
void *shmat (int shmid, void *addr, int flag);
Vraci: ukazatel na sdilenou pamet kdyz OK, -1 pri chybe

Pripojeni segmentu je zavisle na hodnote argumentu addr a nastaveni priznaku.

Obecne mohou nastat tyto priznaky:

  1. addr == 0. Segment je pripojen na prvni dostupnou adresu v adresovem prostoru ulohy (doporucena technika).
  2. addr != 0 a SHM_RND neni specifikovan. Segment je pripojen na zadanou adresu.
  3. addr != 0 a je nastaven SHM_RND. Segment je pripojen na adresu . SHM_RND znamena zaokrouhluj a SHMLBA byva mocnina 2.

Pro ukonceni prace se sdilenou pameti ji muzeme odpojit.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt (void *addr);
Vraci: 0 kdyz OK, -1 pri chybe

Priklad:

#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);
}



Cviceni

  1. Co se stane, kdyz argument pro popen je neexistujici prikaz? Napiste maly program, ktery to otestuje.


dalsi predchozi obsah
Dalsi: Sitovani Predchozi: Meziprocesni komunikace

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