Helsingin yliopisto Tietojenkäsittelytieteen laitos
 

Tietojenkäsittelytieteen laitos

Tietoa laitoksesta:

 
Helsingin yliopisto / Tietojenkäsittelytieteen laitos / Copyright © 2001 Jan Lindström. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

7. System V IPC


Sisältö:


7 System V IPC

                                   
#include < sys/ipc.h >
#include < sys/shm.h >          Yhteiskäyttöinen muisti
#include < sys/sem.h >          Semaforit
#include < sys/msg.h >          Sanomanvälitys

Perustuvat prosessien yhteiskäytössä olevaan muistiin prosessien pyörittävä samassa koneessa. Prosessien ei tarvitse olla 'sukua', vaan käyttöoikeudet määräytyvät tiedostojen tyyliin (u, g, o). Yhteiskäytön tunnisteena yksikäsitteiset positiiviset avaimet (key_t key). Siis asiakkaan ja palvelijan tunnettava yht. avain Avainten nimiavaruudet erillisiä muistialueille, semaforijoukoille ja sanomajonoille. avain (key) on 32 bittinen pysyvin nimi, oikeastaan vakio. Unix näyttää heksalukuna. Tunnus (id) on 32 bittinen muuttuja, jonka arvo riippuu ilmentymästä ('ajokerrasta'). 'Kahva', jonka kautta päästään käsiksi käsiteltävään asiaan. Osoite (address) on muuttuja. Olemassa vain muistialueille. KJ asettaa, kun alue kuvataan prosessin muistiin. Miten asiakas ja palvelija sopivat avaimen?

Avaimen voi luoda tiedostonimestä funktiolla key_t ftok(char *path, char id);, jossa path on jonkin olemassaolevan tiedoston polkunimi ja id on sovittu lisäliite. Ytimen ylläpitämät yhteiskäyttöön liittyvät rakenteet voivat jäädä olemaan vaikka prosessi päättyykin.

IPC-rakenteet eivät näy esim. ls-komennon tulosteissa, kuten esim. putket ja pistokkeet (käyttävät tiedostokuvaajia). Miten poistetaan 'roikkumaan' jääneet IPC-rakenteet? Katso komennot ipcs(1) ja ipcrm(1).

Select() ja poll() eivät ole käytettävissä, joten on vaikea järjestellä kommunikointi s.e. käytetään esim. useita sanomajonoja. On myös vaikeaa käyttää yhdessä tiedostokuvaajiin perustuvien menetelmien kanssa vaan joudutaan käyttämään aktiivista odotusta ('busy wait').


7.1 Yhteiskäytössä oleva muisti (shared memory)

Nopein prosessien välinen kommunikointitapa, joten sopii samassa koneessa pyöriviin asiakas-palvelin ratkaisuihin. Tarvitaan synkronointia ja poissulkemista. Eli semaforit, tiedostolukot tai signaalit (jos samaa prosessiperhettä). Systeemikutsuja lähinnä vain käsittelyä aloitettaessa, muuten yhteiskäytössä olevan muistin käyttö ei eroa prosessin data-alueen käytöstä.

shmget():  
  -        luo yhteiskäyttöinen alue, nimeä avain  
  -        haku avaimen perusteella
shmctl():
  -        parametritietojen käsittely
shmat():
  -        liitä alue (attach) omaan osoiteavaruuteen
shmdt():
  -        muistialueen vapauttaminen (detach) omasta
           osoiteavaruudesta 

Ydin ylläpitää yhteiskäyttöisestä muistista rakennetta

struct shmid_ds {
  struct ipc_perm shm_perm;   ks. alla
  struct anon_map *shm_amp;   vie ytimeen
  int      shm_segsz;         koko tavuina
  ushort   shm_lkcnt;         lukitsemiskertojen lkm
  pid_t    shm_lpid;          viim. shmop
  pid_t    shm_cpid;          luojan pid
  ulong    shm_nattch;        shmat kertojen lkm
  ulong    shm_cnattch;       shminfo käyttää tätä
  time_t   shm_atime;         viim. attach aika
  time_t   shm_dtime;         viim. detach aika
  time_t   shm_ctime;         viim. muutos aika
};


Käyttöoikeudet on kirjattu tiedostojen tapaan:

struct ipc_perm {
  uid_t  uid;                         omistajan tunnisteet 
  gid_t  gid;
  uid_t  cuid;                        luojan (creator) tunnisteet
  gid_t  cgid;
  mode_t mode;                        käyttöoikeudet
  ulong  seq;                         käytön järjestysnumero
  key_t  key;                
};

#include < sys/types.h >
#include < sys/ipc.h >
#include < sys/shm.h >
   
int shmget(key_t key, int size, int flag); palauttaa: jaetun alueen SHMID

shmget() etsii tai luo muistialueen, jonka avain on
annettu. Tarkistaa käyttöoikeudet. Jos luo alueen,
alustaa käyttöoikeudet (rakenne ipc_perms)
      
key:       alueen tunniste (yksikäs. kokonaisluku)
           IPC_PRIVATE 

size:      varattavan alueen minimikoko
           0, jos olemassaolevan alueen haku

flag:      käyttöoikeudet
           IPC_CREAT
           IPC_EXCL
           IPC_NOWAIT    

Ko. alueeseen viitataan käytettäessä funktion shmget() palauttamalla id:llä.

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

void *shmat(int shmid, void *addr, int flag);  
                 palauttaa: osoitin jaetun alueen alkuun

shmat() liittää (attach) yhteiskäytössä olevan alueen
prosessin virtuaaliavaruuteen. palauttaa osoittimen
alueen alkuun.

shmid:     alueen tunniste

addr:               addr == 0           ydin saa valita paikan
                    addr !=0            annettuun osoitteeseen
                    addr !=0, flag == shm_rnd  
                      osoitteeseen (addr - (addr mod shmlba))
                      lba: "low boundary address multiple"

flag:               shm_rnd
                    shm_rdonly

suositus: käytä addr = 0

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

int shmctl(int shmid, int cmd, struct scmid_ds *buf);

shmctl() kysyy tai asettaa yhteiskäytössä olevan alueen
parametritietoja.

cmd:   
  ipc_stat          nouda scmid_ds rakenteen tiedot
  ipc_set           aseta  shm_perm.uid,
                    shm_perm.gid, shm_perm.mode
  ipc_rmid          poista alue omasta muistista
  shm_lock          lukitse yhteiskäytössä oleva alue
                    muistiin (sallittua vain root)
  shm_unlock        vapauta lukitus (vain root)

alueen poisto muistista on sallittu vain sen luojalle. muut saavat paluuarvon -1 ja errno == eidrm. alue jää muiden senhetkisten käyttäjien käyttöön. se poistuu lopullisesti muistista, kun kaikki shmat()- kutsut on 'kumottu' kaikissa prosesseissa. vaarana 'roskien' jääminen muistiin yhteiskäytössä oleva muisti ei säily exec()-kutsussa.

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

int shmdt(void *addr);

shmat() vapauttaa (detach) yhteiskäytössä olevan alue
prosessin virtuaaliavaruudesta. alue jää silti muistiin
muiden käytettäväksi!

addr:      osoite, joka saatiin shmat()-kutsussa


7.2 Semaforit

Semafori on 'erityistä suojelua' nauttiva ei- negatiivinen kokonaisluku, jolla on alkuarvo (yl. jaossa olevien resurssien määrä) ja johon liittyy odotusjono. tarkoitettu yhteiskäytössä olevien resurssien käyttövuorojen hallintaan eli synkronointiin ja poissulkemiseen.

Prosessi voi kasvattaa semaforin arvoa milloin vain, mutta vähentää arvoa, vain jos arvo > 0, muuten prosessi odottaa kunnes vähennys on mahdollista.

Odotus päättyy, kun toinen prosessi kasvattaa semaforin arvoa. odotuksen jälkeen on semaforin arvoa tutkittava aina uudelleen, sillä odottaneista prosesseista vain joku / jotkut pääsevät jatkamaan. semaforioperaatio aloitetaan alusta uudelleen. atominen operaatio, saadaan kaikki mitä pyydetään tai sitten ei mitään. systeemikutsut:

struct semid_ds {
  struct ipc_perm sem_perm;  oikeudet
  struct sem *sem_base;      osoitin ens. 
  ushort sem_nsems;          lukumäärä
  time_t sem_otime;          milloin viim. semop
  time_t sem_ctime;          milloin viim.muutettu
};


struct sem {
  ushort semval;             semaforin arvo
  pid_t  sempid;             kuka käytti viimeeksi
  ushort semncntl;           odott. lkm, että arvo kasvaisi
  ushort semzcntl;           odott. lkm, että arvo   0
};


#include < sys/types.h >
#include < sys/ipc.h >
#include < sys/sem.h >
  
int semget(key_t key, int nsems, int flag); palauttaa: semid

semget() etsii tai luo semaforijoukon. alustaa
luotaessa rakenteen semid_ds .

key:                joukon tunniste (yksikäs. kokonaisluku)
                    ipc_private 

nsems:              semaforien lukumäärä semaforijoukossa
                    0, jos joukko jo olemassa

flag:               käyttöoikeudet
                    ipc_creat
                    ipc_excl

#include < sys/types.h >
#include < sys/ipc.h >
#include < sys/sem.h >
  
int semop(int semid, struct sembuf semoparray[], size_t nops);

argumenttina taulukko, jossa voi antaa kerralla useita semaforioperaatioita. atominen: joko tekee kaikki kutsussa annetut operaatiot, tai sitten mitään.

struct sembuf {
  ushort sem_num;   semaforin numero
  short  sem_op;    operaatio
  short  sem_flg;   ipc_nowait,sem_undo
};

sem_op > 0:         vapautettavien resurssien lkm
                    semval = semval + sem_op

sem_op < 0:         varattavien resurssien lkm
  jos semval >= sem_op niin
           semval = semval - sem_op
  muuten
           jos sem_flag == ipc_nowait niin
                    errno = eagain
           muuten
                    semncnt = semncnt + 1
                    odota, että voi varata

sem_op == 0:        
  jos semval == 0 niin return
  jos semval > 0 ja sem_flg == ipc_nowait
           errno = eagain
  muuten
           semzcnt = semzcnt + 1
           odota, että semval   0

odotus katkeaa
  jos joku poistaa semaforijoukon 
                    errno = idrm 
  jos prosessi saa signaalin          
                    semzcnt = semzcnt - 1 tai 
                    semncnt = semncnt - 1
                    errno = eintr

Jos sem_flg == sem_undo, niin ydin pitää kirjaa prosessin tekemistä varauksista, ja huolehtii niiden vapauttamisesta virhetilanteissa, ts. jos prosessi ei ole vapauttanut, tai jos prosessi kaatuu. undo-lista sopii poissulkemiseen ja muuhun resurssien jakeluun / laskemiseen.

Resurssi ei jää lukkoon vaikka prosessi kuolee kesken sen käsittelyn ei kuitenkaan takaa, että resurssi olisi tämän jälkeen eheässä tilassa (prosessilta saattoi jäädä joku päivitys kesken)

undo-lista ei sovi prosessien väliseen kommunikointiin, koska sama prosessi voi vapauttaa jatkuvasti, joten undo- laskuri vuotaa yli. kuolleen prosessin vastaanottamat sanoamt eivät 'synny uudestaan', vaan ne on pysyvästi kulutettu.

#include < sys/types.h >
#include < sys/ipc.h >
#include < sys/sem.h >
  
int semctl(int semid, int semnum, int cmd, union semun arg);
                                   palauttaa: vaihtelee cmd perusteella


semctl() noutaa / asettaa semaforijoukon parametreja.

ipc_stat   nouda rakenne semid_ds (arg.buf)
ipc_set    aseta sem_perm.uid, sem_perm.gid,sem_perm.mode
ipc_rmid   poista semaforijoukko kokonaan muistista
getval     palauta semaforin semval arvo
setval     aseta semaforin arvoksi arg.val (alustus)
getpid     palauta semaforin sempid-kenttä
getncnt    palauta semaforin semncnt-kenttä
getzcnt    palauta semaforin semzcnt-kenttä
getall     nouda kaikkien semaforien arvot taulukkoon arg.array
setall     aseta kaikkien semaforien arvot taulukosta arg.array

union semun {
  int             val;
  struct semid_ds *buf;
  ushort            *array;
};

Esimerkki (sema.c):


#include < stdio.h >
#include < sys/types.h >
#include < sys/ipc.h >
#include < sys/sem.h >
#include < sys/shm.h >
#include < stdlib.h >
#include < errno.h >
#include < string.h >

int semheld = 0;

void release(int id);
void request(int id);

int main(int argc, char *argv[]) 
{
  int id;
  union semun sunion;

  /* No arguments: "server". */
  if (argc < 2) {
    /* Request a semaphore. */
    id = semget(IPC_PRIVATE, 1, SHM_R | SHM_W);
   
    /* Initialize its resource count to 1. */
   
    sunion.val = 1;
    semctl(id, 0, SETVAL, sunion);
  } else {
    /* Open up the existing one. */
    id = atoi(argv[1]);
    printf("Using existing semaphore %d.\n", id);
  }

  if (id == -1) {
    printf("Semaphore request failed: %s.\n", strerror(errno));
    return 0;
  }

  printf("Successfully allocated semaphore id %d\n", id);
 
  while (1) {
    int selection;
    printf("\nStatus: %d resources held by this process.\n", semheld);
    printf("Menu:\n");
    printf("1. Release a resource\n");
    printf("2. Request a resource\n");
    printf("3. Exit this process\n");
    printf("Your choice: ");

    scanf("%d", &selection);

    switch(selection) {
      case 1: release(id); break;
      case 2: request(id); break;
      case 3: exit(0); break;
    }
  }

  return 0;
}

void release(int id) {
  struct sembuf sb;

  if (semheld < 1) {
    printf("I don't have any resources; nothing to release.\n");
    return;
  }

  sb.sem_num = 0;
  sb.sem_op = 1;
  sb.sem_flg = 0;
 
  semop(id, &sb, 1);
  semheld--;

  printf("Resource released.\n");
}

void request(int id) {
  struct sembuf sb;

  if (semheld > 0) {
    printf("I already hold the resource; not requesting another one.\n");
    return;
  }

  sb.sem_num = 0;
  sb.sem_op = -1;
  sb.sem_flg = 0;

  printf("Requesting resource...");
  fflush(stdout);

  semop(id, &sb, 1);
  semheld++;

  printf(" done.\n");
}

Toinen esimerkki, jossa käytetään myös jaettua muistia (sema2.c):


#include < stdio.h >
#include < sys/types.h >
#include < sys/ipc.h >
#include < sys/sem.h >
#include < sys/shm.h >
#include < stdlib.h >
#include < errno.h >
#include < string.h >
#include < signal.h >
#include < stdarg.h >

union semun {
  int val;                    /* value for SETVAL */
  struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
  unsigned short int *array;  /* array for GETALL, SETALL */
  struct seminfo *__buf;      /* buffer for IPC_INFO */
};

#define SHMDATASIZE 1000
#define BUFFERSIZE (SHMDATASIZE - sizeof(int))

#define SN_EMPTY 0
#define SN_FULL  1

int DeleteSemid = 0;

void server(void);
void client(int shmid);
void delete(void);
void sigdelete(int signum);
void locksem(int semid, int semnum);
void unlocksem(int semid, int semnum);
void waitzero(int semid, int semnum);
void clientwrite(int shmid, int semid, char *buffer);

int safesemget(key_t key, int nsems, int semflg);
int safesemctl(int semid, int semnum, int cmd, union semun arg);
int safesemop(int semid, struct sembuf *sops, unsigned nsops);
int safeshmget(key_t key, int size, int shmflg);
void *safeshmat(int shmid, const void *shmaddr, int shmflg);
int safeshmctl(int shmid, int cmd, struct shmid_ds *buf);
void HandleError(int ecode, const char *const caller,
         const char *fmt, ...);


int main(int argc, char *argv[]) {

  /* No arguments: "server". */
  if (argc < 2) {
    server();
  } else {
    client(atoi(argv[1]));
  }
  return 0;
}

void server(void) {
  union semun sunion;
  int semid, shmid;
  void *shmdata;
  char *buffer;

  /* First thing: generate the semaphore. */

  semid = safesemget(IPC_PRIVATE, 2, SHM_R | SHM_W);

  DeleteSemid = semid;

  /* Delete the semaphore when exiting. */
  atexit(&delete);
  signal(SIGINT, &sigdelete);

  /* Initially empty should be available and full should not be. */
 
  sunion.val = 1;
  safesemctl(semid, SN_EMPTY, SETVAL, sunion);
  sunion.val = 0;
  safesemctl(semid, SN_FULL, SETVAL, sunion);

  /* Now allocate a shared memory segment. */

  shmid = safeshmget(IPC_PRIVATE, SHMDATASIZE, IPC_CREAT | SHM_R | SHM_W);
 
  /* Map it into memory. */

  shmdata = safeshmat(shmid, 0, 0);

  /* Mark it to automatically delete when the last holding process exits. */

  safeshmctl(shmid, IPC_RMID, NULL);

  /* Write the semaphore id to its beginning. */
  *(int *)shmdata = semid;

  buffer = shmdata + sizeof(int);

  printf("Server is running with SHM id ** %d **\n",
         shmid);

  /*****************************************************************
   MAIN SERVER LOOP
   *****************************************************************/

  while (1) {
    printf("Waiting until full...");
    fflush(stdout);
    locksem(semid, SN_FULL);
    printf(" done.\n");

    printf("Message received: %s\n", buffer);
    unlocksem(semid, SN_EMPTY);
  }
}

void client(int shmid) {
  int semid;
  void *shmdata;
  char *buffer;

  shmdata = safeshmat(shmid, 0, 0);
 
  semid = *(int *)shmdata;
  buffer = shmdata + sizeof(int);

  printf("Client operational: shm id is %d, sem id is %d\n",
         shmid,
         semid);

  while (1) {
    char input[3];

    printf("\n\nMenu\n1. Send a message\n");
    printf("2. Exit\n");

    fgets(input, sizeof(input), stdin);

    switch(input[0]) {
      case '1': clientwrite(shmid, semid, buffer); break;
      case '2': exit(0); break;
    }
  }
 
}


void delete(void) {
  printf("\nMaster exiting; deleting semaphore %d.\n", DeleteSemid);
  if (semctl(DeleteSemid, 0, IPC_RMID, 0) == -1) {
    printf("Error releasing semaphore.\n");
  }
}

void sigdelete(int signum) {
  /* Calling exit will conveniently trigger the normal
     delete item. */

  exit(0);
}

void locksem(int semid, int semnum) {
  struct sembuf sb;

  sb.sem_num = semnum;
  sb.sem_op = -1;
  sb.sem_flg = SEM_UNDO;

  safesemop(semid, &sb, 1);
}

void unlocksem(int semid, int semnum) {
  struct sembuf sb;

  sb.sem_num = semnum;
  sb.sem_op = 1;
  sb.sem_flg = SEM_UNDO;

  safesemop(semid, &sb, 1);
}

void waitzero(int semid, int semnum) {
  struct sembuf sb;

  sb.sem_num = semnum;
  sb.sem_op = 0;
  sb.sem_flg = 0;           /* No modification so no need to undo */
 
  safesemop(semid, &sb, 1);
}

void clientwrite(int shmid, int semid, char *buffer) {
  printf("Waiting until empty...");
  fflush(stdout);
  locksem(semid, SN_EMPTY);
  printf(" done.\n");

  printf("Enter message: ");
  fgets(buffer, BUFFERSIZE, stdin);
  unlocksem(semid, SN_FULL);
}

int safesemget(key_t key, int nsems, int semflg) {
  int retval;

  retval = semget(key, nsems, semflg);
  if (retval == -1)
    HandleError(errno, "semget", "key %d, nsems %d failed", key, nsems);
  return retval;
}
 
int safesemctl(int semid, int semnum, int cmd, union semun arg) {
  int retval;

  retval = semctl(semid, semnum, cmd, arg);
  if (retval == -1)
    HandleError(errno, "semctl", "semid %d, semnum %d, cmd %d failed",
                semid, semnum, cmd);
  return retval;
}

int safesemop(int semid, struct sembuf *sops, unsigned nsops) {
  int retval;

  retval = semop(semid, sops, nsops);
  if (retval == -1)
    HandleError(errno, "semop", "semid %d (%d operations) failed",
                semid, nsops);
  return retval;
}

int safeshmget(key_t key, int size, int shmflg) {
  int retval;

  retval = shmget(key, size, shmflg);
  if (retval == -1)
    HandleError(errno, "shmget", "key %d, size %d failed", key, size);
  return retval;
}

void *safeshmat(int shmid, const void *shmaddr, int shmflg) {
  void *retval;

  retval = shmat(shmid, shmaddr, shmflg);
  if (retval == (void *) -1)
    HandleError(errno, "shmat", "shmid %d failed", shmid);
  return retval;
}

int safeshmctl(int shmid, int cmd, struct shmid_ds *buf) {
  int retval;

  retval = shmctl(shmid, cmd, buf);
  if (retval == -1)
    HandleError(errno, "shmctl", "shmid %d, cmd %d failed",
                shmid, cmd);
  return retval;
}

void HandleError(int ecode, const char *const caller,
         const char *fmt, ...) {

  va_list fmtargs;
  struct sigaction sastruct;

  /* Print the error message(s) */

  va_start(fmtargs, fmt);

  fprintf(stderr, "*** Error in %s: ", caller);
  vfprintf(stderr, fmt, fmtargs);
  va_end(fmtargs);
  fprintf(stderr, "\n");
  if (ecode) {
    fprintf(stderr, "*** Error cause: %s\n", strerror(ecode));
  }

  /* Exit if no signal handler.  Otherwise, raise a signal. */

  sigaction(SIGUSR1, NULL, &sastruct);
  if (sastruct.sa_handler != SIG_DFL) {
    raise(SIGUSR1);
  } else {
    exit(254);
  }
}


7.4 Perinteinen p ja v

Oerinteisesti semafori-operaatioista on käytetty tunnuksia p() ja v():

  p(sem)   jos sem == 0 niin
               jää odottamaan
           muuten sem = sem - 1

  v(sem)   sem = sem + 1
           herätä odottajat

sysv:n mekanismi sallii myös

  p(sem,n) jos sem > n niin
               sem = sem - n
           muuten
               jää odottamaan
  
  v(sem,n) sem = sem - n

  z(sem)   odota, kunnes sem = 0

tuottaja do tuota data p(tyhja) p(mutex) vie puskuriin v(mutex) v(täysi) while (1); kuluttaja do p(täysi) p(mutex) ota puskurista v(mutex) v(tyhja) kuluta data while (1);

Edelliseen ratkaisuun saa helposti lukkiutuman, kun vaihtaa kummassa tahansa kahden ensimmäisen p()- operaation paikkaa keskenään. yleisiä ratkaisuja:


7.5 Sanomanvälitys

Sanoma muodostuu: sanoman tyypistä, pituuskentästä ja sanoman tavuista. sanomia ei tarvitse käsitellä niiden välitysjärjestyksessä, voi noutaa myös tyypin perusteella (esim. prioriteetti). systeemikutsut:

 
struct msqid_ds {
  struct ipc_perm        shm_perm;  
  struct msg_first;      osoitin jonon ensimm. 
  struct msg_last;       osoitin jonon viimeiseen
  ulong    msg_cbytes;   tavujen lkm jonossa
  ulong    msg_qnum;     sanomien lkm jonossa
  ulong    msg_qbytes;   tavujen max lkm 
  pid_t    msg_lspid;    viim. msgsnd pid
  pid_t    msg_lrpid;    viim. msgrvc pid
  time_t   msg_atime;    viim. msgsnd aika
  time_t   msg_dtime;    viim. msgrcv aika
  time_t   msg_ctime;    viim. muutos aika
};

#include < sys/ipc.h >
#include < sys/msg.h >
  
int msgget(key_t key, int flag);     
                palauttaa: sanomajonon tunniste MSQID

msgget() etsii tai luo sanomajonon. Alustaa luotaessa rakenne msqid_ds.


key:                sanoman tunniste (yksikäs. kokonaisluku)
                    IPC_PRIVATE 

flag:               käyttöoikeudet
                    IPC_CREAT
                    IPC_EXCL

Funktion palauttaamaa arvoa MSQID käytetään kahvana muissa kutsuissa.

#include < sys/types.h >
#include < sys/ipc.h >
#include < sys/msg.h >
  
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgctl() noutaa tai asettaa sanomajonon parametritietoja.

 
IPC_STAT   Nouda msqid_ds rakenteen tiedot

IPC_SET    Aseta msg_perm.uid, msg_perm.gid,
           msg_perm.mode,  msg_qbytes

IPC_RMID   Poista sanomajono kokonaan muistista

Vain sanomajonon luoja saa käyttää komentoa IPC_RMID, muut saavat errno == EIDRM.

#include < sys/types.h >
#include < sys/ipc.h >
#include < sys/msg.h >
  
int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);

msgsnd() vie sanoman jonoon.

ptr:       osoitin alueeseen, jossa tyyppi ja sanoman
           tavut, esimerkiksi
     
           struct mymesg {
                    long mtype;          tyyppi (sovi itse)
                    char mtext[512];     data (max  pituus)
           };

nbytes:    dataosan todellinen pituus

jos jono täynnä ja flag == IPC_NOWAIT niin errno = EAGAIN
muuten 
  odota, että sanomalle on tilaa

jos joku poistaa sanomajonon, niin errno = ERMID 
jos prosessi saa signaalin, niin errno = EINTR

#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);
                                       palauttaa: montako tavua saatiin

msgrcv() hakee sanoman jonosta.

ptr:                kuten msgsnd()

nbytes:    montako tavua maksimissaan

type = 0:           palauta jonon ensimmäinen
type > 0:           palauta ens., jonka tyyppi == type
type < 0:           palauta pienin sanoma., jonka
                                      type <= abs(type)

jos jono on tyhjä ja flag == IPC_NOWAIT
  errno = ENOMSG
muuten
  odota, kunnes oikean tyyppinen haettavissa

jos joku poistaa sanomajonon, niin errno = EIDRM 
jos prosessi saa signaalin, niin errno = EINTR

Esimerkki vastaanottajasta (Esim1).


#include < sys/types.h > 			/* Receive */
#include < sys/ipc.h >
#include < sys/msg.h >
#include < stdio.h >
#include < stdlib.h >
#include < fcntl.h >
#include < errno.h >

int main(int argc, char *argv[]) /* argumenttina: tiedostonimi */
{
  key_t key;
  int id;
  long type;
  typedef struct { 
      long mtype;
      char mtext[1];
  } msg_t;

  size_t   msize;            /* paljonko tilaa varattu */
  msg_t    *mess;
  int               mlen;             /* todellinen pituus */

  if ((key = ftok(argv[1],'D')) == -1) 
     perror("msgrcv: ftok");
  if ((id = msgget(key, IPC_CREAT|0777))== -1)
     perror("msgrcv: msgget");

  /* varaa tilaa sanomalle */
  msize = 20;
  if ((mess = (msg_t *)malloc(sizeof(long) + msize + 1)) == NULL)
     perror("msgrcv: malloc");

  /* ota vastaan sanomia */
  do {
  if ((mlen=msgrcv(id, mess, msize, getpid(), 0)) == -1) {
     if (errno == EINTR) continue;
              if (errno == E2BIG) {
           free(mess);
           msize *= 2;
           printf("%d sanoman koko -> %d\n", getpid(), msize);
           if ((mess = (msg_t *)malloc(sizeof(long)+msize+1)) == NULL)
               perror("msgrcv: malloc");
           continue;
      }
      perror("msgrcv: msgrcv");
  }

  mess->mtext[mlen] = (char) 0;;
  printf("%d '%s'\n", getpid(), mess->mtext);  
    } while (mlen != 0);
  
    free(mess);

    exit(0);
}
Esimerkki lähettäjästä (Esim2).
#include < sys/types.h >                                         /* Send */
#include < sys/ipc.h >
#include < sys/msg.h >
#include < stdio.h >
#include < stdlib.h >

int main(int argc, char *argv[])
/* argumentteina saadaan: tiedostonimi, tyyppi, teksti */
{
  key_t key;
  int id;
  long type;
  typedef struct { 
           long mtype;
           char mtext[1];
  } msg_t;

  size_t mlen;
  msg_t *mess;

  /* etsi sanomajono */

  if ((key = ftok(argv[1],'D')) == -1) 
           perror("msgsnd: ftok");

  if ((id = msgget(key,0)) == -1)
           perror("msgsnd: nsgget");

  /* kokoa sanoma */

  if ((type = atol(argv[2])) <= 0)
           perror("msgsnd: tyyppi ei voi olla < 0");

  mlen = strlen(argv[3]);
  if ((mess = (msg_t *) malloc(sizeof(long) + mlen)) == NULL)
           perror("msgsnd: malloc");

  mess->mtype = type;
  memcpy(mess->mtext, argv[3], mlen);

  /* lähetä sanoma */
  
  if (msgsnd(id, mess, mlen, 0) != 0)
           perror("msgsnd: msgsnd");

  free(mess);

  exit(0);
}



Jan Lindström (Jan.Lindstrom@cs.Helsinki.FI)