Interprozess-Kommunikation Interprozess- Kommunikation Tanenbaum Kap. 2.3.8 Stallings Kap. 5.5 Glatz Kap. 5.1-5.3, 5.5, 5.6.1-5.6.2 1 1
Inhalt Lehrziele Interprozesskommunikation (IPC) Message Passing Shared Memory IPC Mechanismen in Unix/Linux IPC Ressourcen Shared Memory Message Queues Signale Pipes Sockets Shared Files / Memory Mapped Files Semaphore IPC Mechanismen in Windows 2 2
Lehrziele Sie können den Begriff Interprozesskommunikation erklären und diskutieren Message Passing erklären und diskutieren Shared Memory erklären und diskutieren Unix/Linux IPC Mechanismen aufzählen, erklären, diskutieren Implementationsaspekte anhand der Unix/Linux IPC Mechanismen erklären und diskutieren 3 3
Interprozesskommunikation Prozesse arbeiten zusammen, um gemeinsam eine Aufgabe zu lösen Datenaustausch notwendig gemeinsam Ressourcen zu nutzen Gründe für Zusammenarbeit Parallelverarbeitung - Leistungssteigerung - Benutzerfreundlichkeit vereinfachte Strukturierung von Anwendungen - mehrere kooperierende, aber kleine Prozesse Daten- und Informationsaustausch - einfacher Zugriff auf gemeinsame Daten Echtzeitsysteme - Aufgaben mit unterschiedlichen Repetitionsraten P 1 P 2 P 3 4 Parallelverarbeitung - Leistungssteigerung Rechenlast kann auf mehrere CPUs verteilt werden blockierende Anfragen parallel ausführen - Benutzerfreundlichkeit mehrere Aufgaben gleichzeitig ausführen z.b. Drucken und Editieren Strukturierung von Anwendungen - Anwendungen lassen sich in kleine Einheiten unterteilen - einfachere Struktur übersichtlich geringere Fehleranfälligkeit - Beispiel: Bash Informationsaustausch - gemeinsame Datenhaltung - Client/Server Anwendungen - z.b. zentrale Datenbank für Authentifizierung (Samba), etc. Echtzeitsysteme - verschiedene Aufgaben müssen verschieden oft ausgeführt werden, aber gemeinsames Resultat notwendig - z.b. Flugdatenerfassung Frage. Wieso nicht über globale Daten (Variablen) lösen? 4
Interprozesskommunikation: IPC Beispiel zwei Prozesse möchten File drucken nur ein Prozess darf gleichzeitig drucken Lösung 1: Synchronisation Prozesssynchronisation - nur ein Prozess darf drucken - die anderen warten Lösung 2: IPC Druckprozess P p - nimmmt Daten entgegen - druckt sie dann selbständig aus Prozesse - dürfen selbst nicht drucken - können dafür weiterarbeiten P 1 P 2 Drucker P 1 P 2 IPC P P Drucker 5 Unterschied der Lösungen - Lösung 1 beide Prozesse werden synchronisiert, d.h. es wird nur Kontrollinformation ausgetauscht Streit um Ressourcen (Competition) Mutual Exclusion - Lösung 2 expliziter Datenaustausch jeder Prozess ist für Lösung "seines" Problems zuständig, Aufgabenteilung implizite Synchronisation der Prozesse verschiedene Möglichkeiten für Datenaustausch Was ist bei beiden Lösungen gleich? - in beiden Fällen werden Daten ausgetauscht - Datenmenge ist verschieden: 1 Bit bzw. 1 File - auch der Datenaustausch erfordert Synchronisation (mutual exclusion) Fragen resp. Problemstellungen in Zusammenhang mit IPC - Wie greifen Prozesse auf gemeinsame Daten zu? - Wie kommunizieren Prozesse? - Wie wird Datenkonsistenz garantiert? 5
Interprozesskommunikation Innerhalb eines Systems - im Anwenderbereich - BS Unterstützung In verteilten Systemen (zwischen Systemen) - Kommunikation über Netzwerk - BS Unterstützung 6 Zwei Varianten von Interprozesskommunikation - innerhalb eines Systems Kommunikation zwischen Prozessräumen im gleichen Speicher - zwischen verschiedenen Systemen Kommunikation zwischen Prozessen über ein Netzwerk In beiden Fällen muss Betriebssystem Mechanismen anbieten für - Datenaustausch - Synchronisation (Datenkonsistenz) 6
Kommunikationsmodelle Versuch einer Klassifizierung Message Passing - Austausch von Nachrichten P 1 P 2 Shared Objects - gemeinsamer Zugriff auf Objekte P 1 P 2 Object Streams - serieller Strom von Objekten - Objekte: ein oder mehrere Bytes P 1 P 2 7 Anmerkung: gilt nicht nur für zwei Prozesse Messages - Austausch von Nachrichten - innerhalb eines Systems oder in verteilten Systemen - Betriebssystem stellt Zugriffsfunktionen (send, receive) zur Verfügung, oft auch Mailbox Synchronisation durch Zugriffsfunktionen - Anwender verantwortlich für verpacken der Daten in Nachrichten - Beispiel: Message Queues, etc. Shared Objects (shared memeory) - shared memory, (shared) files, etc. - meist innerhalb eines Systems (Ausnahme: distributed shared memory)) - Betriebssystem: stellt gemeinsamen Speicherbereich zu Verfügung - Anwender verantwortlich für Datenstrukturierung und Synchronisation - Beispiel: shared memory, memory mapped files, etc. Object Streams - oft Sequenz von Bytes (ein bis mehrere Bytes lesen und/oder schreiben) - innerhalb eines Systems oder zwischen verteilten Systemen - Betriebssystem: stellt FIFO Queue zur Verfügung synchronisierter Zugriff auf "beide Enden" - Anwender verantwortlich für Datenstrukturierung - Beispiele: Pipes, Sockets, etc. Behandeln Messages vertieft, Shared Objects und Object Stream an Beispielen 7
slide Message Passing Sehr häufig verwendetes Verfahren zwischen Prozessen auf einem Rechner in verteilten Rechnersystemen Synchronisation implizit Grundfunktionen send(dest, message) receive(src, message) beide Funktionen können blockieren Auch geeignet für reine Prozess-Synchronisation Implementation eines Mutex Prozess P 1 do {... send(dest,msg);... } while (!finished); Prozess P 2 do {... receive(src,msg);... } while (!finished); 8 Zwei Verbindungsarten bei Message Passing - synchrone resp. verbindungsorientierte Kommunikation beide Prozess müssen der Verbindung zustimmen P1: openconnection(adr) send(msg); closeconnection(adr); P2: openconnection(adr) receive(msg); closeconnection(adr); - asynchrone resp. verbindungslose Kommunikation Sender schickt Nachricht einfach los ev. Quittierung notwendig Sache der Applikation siehe oben Übertragungssicherheit: Aufgabe der Anwendung - Verlust von Nachrichten - Authentifizierung der Destination in verteilten Systemen 8
... Message passing Asynchrone / Synchrone Kommunikation asynchron synchron 9 Nonblocking Send, blocking Receive - Sender: send() blockiert nicht kann mehrere Nachrichten nach verschiedenen Destinationen abschicken benötigt Mailbox für Zwischenspeicherung der Nachrichten erwartet oft Quittung (falls Fehler beim Empfänger) - Empfänger: receive() blockiert, wenn er auf eine Nachricht wartet normalerweise braucht der Empfänger die Nachricht, damit er weiterfahren kann kann unendlich lang blockiert bleiben...p - Gefahren wegen Fehler dauerndes Erzeugen von Nachrichten niemand überwacht den Prozess belegt Systemressourcen Anwender verantwortlich, dass Message ankommt bei Nachrichteverlust oder wenn Sender stirbt unendlich lang Warten Nonblocking Send, Non Blocking Receive Probleme mit Synchronisation erwartete und gesendete Nachricht müssen übereinstimmen Blocking Send, Blocking Receive - beide bleiben blockiert bis die Nachricht angelangt ist - strikte Synchronisation, wird auch Rendez-Vous genannt (z.b. in ADA, CSP) 9
slide Message Passing: Adressierung Direkte Adressierung P S send(procr, msg) P R receive(procs, msg) receive(msg) Indirekte Adressierung P S P R send(mbox, msg) send(port, msg) mailbox port receive(mbox, msg) receive(port, msg) 10 Direkte Adressierung - Nachricht wird direkt in den Adressraum (Speicher) des Empfängers kopiert - Adressierung explizite Adressierung die Quellenadresse wird beim Empfangen einer Nachricht angegeben z.b. Kommunikation zwischen zwei "zusammengehörigen" Prozessen implizite Adressierung die Quellenadresse kann nicht angegeben werden z.b. Druckserver: erhält von verschiedenen Prozessen Druckaufträge - Sender und Empfänger sind eng gekoppelt - Vorteil: geschützter Datenverkehr Indirekte Adressierung - die Nachricht wird nicht direkt an den Empfänger gesendet - die Nachricht wird an eine Mailbox (resp. Port) gesendet Mailbox, Port: im wesentlichen eine Queue - Sender und Empfänger sind entkoppelt - Vorteil: verschiedenste Verbindungstopologien möglich 10
Message Passing: indirekte Adressierung port mailbox one to one one to many port mailbox many to one many to many 11 Port Mailbox - one to one / Punkt zu Punkt (privates) Port zwischen einem Sender und einem Empfänger geschützter Datenaustausch -> keine fehlerhafte Interaktion durch andere Prozesse Nachrichten können direkt in den Speicherbereich des Empfänger kopiert werden - many to one Client/Server Konfiguration - one to many multicast - many to many Unix/Linux Message Queues verschiedene Nachrichtentypen möglich beim Lesen kann Typ angegeben werden 11
Message Passing: Mailbox Wem gehört das Port resp. die Mailbox? Port gehört normalerweise dem Empfänger-Prozess wird von ihm erzeugt wenn Prozess stirbt, wird Port zerstört Mailbox Mailboxen werden i.a. beim Betriebssystem angefordert - Mailbox gehört meist dem Betriebssystem - z.b. Unix Message Queues Mailbox kann aber auch Prozess gehören - wird zerstört wenn Prozess stirbt 12 12
Message Passing: Nachrichten Aufbau von Nachrichten (Datenstruktur) Header 1) - Nachrichtentyp - Ziel- und Quellenadresse - Länge - Steuerinformation Aktion bei voller Mailbox Sequenznummer Priorität Body - Daten Header Body Message Type Destination ID Source ID Message Length Control Information Message Data 1) einige Felder können optional sein 13 13
Beispiel: Mutex Beteiligte Prozesse gemeinsame Mailbox Lesen blockiert maximal eine Nachricht abgelegt Hauptprogramm initialisiert Mailbox Mailbox heisst mutex Prozess Px while (true) {... receive(mutex, msg); // lock crticalsection(); send(mutex, msg); // unlock... } 14 Funktionsweise - bei der Initialisierung wird eine Nachricht in der Mailbox abgelegt - der erste Prozess der die Meldung liest darf in den kritischen Abschnitt eintreten blockiert die Mailbox solange, bis er die Nachricht wieder zurückgeschrieben hat: blocking read für andere Prozesse 14
Beispiel: Producer-Consumer Problem Gemeinsamer endlicher Ring-Buffer outptr inptr Array buf[n] Lösung mit Message Passing Mailbox mit Namen buf Buffergrösse ist Betriebssystem Parameter Synchronisation implizit Erzeuger while (true) { item = produceitem(); send(buf, item); } Verbraucher while (true) { item = receive(buf); consumitem(item); } 15 15
Shared Memory Shared Memory Bereich des physikalischen Speichers wird in den (virtuellen) Addressraum der Prozesse abgebildet Prozess 2 virtual Address space Prozess 1 virtual address space physical memory 16 Der physikalische Bereich kann an verschiedene Orte im virtuellen Adressraum eingebunden werden. 16
Shared Memory vs. Message Passing Shared Memory muss bei Betriebssystem angefordert werden keine implizite Synchronisation - Synchronisation z.b. mit Semaphoren Zugriff sehr schnell: Speicherzugriff nur in Shared Memory Systemen verfügbar Message Passing Mailbox muss bei Betriebssystem angefordert werden implizite Synchronisation - einfach zu handhaben langsamer als Shared Memory auch in verteilten Umgebungen verfügbar - MPI: Message Passing Interface 17 Hinweis - Shared Memory bei SMP Maschinen und in Distributed Shared Memory Systemen verfügbar - Distributed Shared Memory Zugriff auf lokalen Speicher i.a. schneller als auf entfernten Speicher NUMA: non uniform memory access 17
IPC Mechanismen in Unix/Linux (POSIX 1) ) Interprozesskommunikation 18 IPC Ressourcen Shared Memory Message Queues - Funktionalität - Implementation Signale Pipes Sockets Shared Files / Memory Mapped Files Synchronisation Semaphore (Lock Files) 1) Portable Operating System Interface Im folgenden verschiedene IPC Mechanismen anhand Unix/Linux erklärt Bei Shared Memory und Message Queues wird kurz auf Implementationsaspekte (Kernel) eingegangen - Verwaltung der IPC Ressourcen in entsprechenden Tabellen 18
Unix/Linux: Shared Memory, Semaphore, Messages Posix IPC Ressourcen erzeugen und/oder öffnen shm_open(), mq_open(), sem_open() speziell für nicht verwandte Prozesse "Zugriff" über Name, z.b. fd = shm_open("/tmp_shmr1", O_CREAT O_RDWR, 0700); unterschiedliche Rückgabewerte - Shared Memory: File Deskriptor - Semaphor: Semaphor Objekt, Pointer - Message Queue: Message Queue Objekt, kein Pointer 19 POSIX IPC Ressourcen: Shared Memory, Semaphor, Message Queues Weiter Informationen sind in den man-pages zu finden - man shm_overview, man shm_open, etc - man sem_overview, man sem_open, etc. - man mq_overview, man mq_open, etc. Die neuen POSIX IPC Ressourcen sind a.a. einfacher zu benutzen als die älteren System V IPC Ressource (werden aber immer noch unterstützt) 19
Shared Memory (Posix) slide Gemeinsamer Speicherbereich kann von mehreren Prozessen gleichzeitig genutzt werden Synchronisation explizit bei Anwendungen schnellste Form der Interprozesskommunikation Neue Shared Memory Region erzeugen auf benötigte Grösse setzen in Speicherbereich abbilden void *shmr; int fd, len;... fd = shm_open("/tmp_shmr1", O_CREAT O_RDWR, 0700); rv = ftruncate(fd, len); shmr = mmap(null, len, PROT_READ PROT_WRITE, MAP_SHARED, fd, 0); 20 shm_open() - Name: "/tmp/shmr1" (muss mit / beginnen) - oflag: O_CREAT erzeugen, falls noch nicht existiert - mode: hier user = rwx, group = kein Zugriff, others = kein Zugriff ftruncate() - Filedeskriptor: fd - Länge des "Files" exakt festlagen (nach Bedarf verkleinern oder vegrössern) mmap() Rückgabewert: Pointer auf gemeinsamen Speicherbereich - addr = NULL Kernel wählt Adresse des Speicherbereichs - len Länge des Speicherbereichs - Protection kann gelesen und geschrieben werden - flags kann zwischen mehreren Prozessen "geshared" werden - Filedeskriptor fd - offset Bereich beginnt bei offset im File 20
... Shared Memory (Linux) Kernel Strukturen Shared Memory Tabelle 0 Shared Region Beschreibung 1 2 3... Prozess RegionTabelle Prozess RegionTabelle attachregion attachregion Kerneltabelle pro Shared Region pro Prozess 21 Kernel unterhält Tabelle mit Einträgen zu jeder Shared Memory Region - zu jedem dieser Einträge wird pro Prozess eine Verwaltungsstruktur beigefügt Shared Memory Tabelle - Tabelle mit Referenzen auf die einzelnen Segment Beschreibungen Segment Beschreibung - Beschreibung zu einer Shared Memory Region - Datenstruktur: shmid_ds Zugriffsrechte, Grösse, etc. Array von Page Tabelleneinträgen Anzahl der "Attaches" (von wie vielen Prozessen attched) Referenz auf die Attach Descriptors etc. Attach Deskriptoren - Datenstruktur: shm_desc - Prozess ID (Prozess der Region angehängt hat) - virtuelle Adresse der Shared Memory Region - etc. 21
... Shared Memory Memory Mapped IO z.b. ARM Prozessoren IO-Adressebereich in Speicher abbilden Code vereinfacht void *map_io(unsigned baseaddr, unsigned bsize) { // 1... mfd = open("/dev/mem", O_RDWR O_SYNC); // 2 iomem = malloc(bsize + (PAGE_SIZE-1)); // 3 pagealign = (unsigned)iomem % PAGE_SIZE; // 4 if (pagealign > 0 ) iomem += PAGE_SIZE - pagealign; } ioaddr = mmap(iomem, bsize, // 5 PROT_READ PROT_WRITE, MAP_SHARED MAP_FIXED, mfd, baseaddr ); close (mfd); // 6 return ioaddr; 22 1. Funktion map_io() Parameter: Basisadresse und Länge des IO-Bereichs!!! Basisadresse muss Vielfaches der Pagegrösse sein Rückgabewert: Zeiger auf IO-Bereich (auf Speicher abgebildet) 2. File Descriptor: Handle auf physikalischen Speicher, "File", so gross wie Speicher 3. Speicherbereich allozieren, in den der IO-Bereich abgebildet werden kann 4. Adresse im Speichernbereich so bestimmen, dass sie auf Pageanfang zu liegen komm. Pagegrösse kann mit sysconf(_sc_page_size) abgefragt werden 5. Abbildden des IO-Speicherbereich ab baseaddr in allozierten Speicherbereich Parameter: Zeiger in allozierten Speicherbereich Pages können gelesen, geschrieb werden MAP_SHARED: andere Prozesse, sehen Änderungen an diesem Mapping MAP_FIXED: iomem muss exact verwendet werden physikalischer Speicher Offset im phys. Speicher: hier startet phys. IO-Bereich Rückgabewert: Zeiger auf abgebildeten IO-Bereich 6. Speicher "File" schliessen Hinweis: es ist gefährlich, IO-Bereiche für mehrere Prozesse bzw. Anwender freizugeben: /dev/mem kann deshalb nur mit root-privilegien geöffnet werden 22
slide Message Queues (Posix) Message Queue erzeugen mqd_t q1; q1 = mq_open("/tmp_que1", O_CREAT O_RDWR, 0700, NULL); System Calls send Message rv = mq_send(q1, buf, len_s, prio); receive Message älteste Message mit höchster Priorität rv = mq_receive(q1, buf, len_r, NULL); 23 mq_open() - NULL default attributes mq_send() - len_s Länge der Meldung, muss <= als Meldungslänge sein Meldungslänge kann mit mq_getattr() gelesen werden mq_receive() - len_r gibt Gröss des Buffers buf, muss > als Meldungslänge sein 23
... Message Queues (Linux) Message Queues Organisation der Datenstrukturen im Kernel Message Queue Tabelle Messages 0 1 H 1 H 2 "Text" "Text" 2 H 3 "Text" 3 Message Header 24 Der Kernel unterhält eine Tabelle mit Einträgen für jede Message Queue - allgemein Information zu den einzelnen Message Queues - Datenstruktur: msquid_ds (Details siehe z.b. Herold) Die einzelnen Message Queues bestehen wiederum aus einer "verlinkten" Listen von Message Einträgen - Informationen zu jeder Message - Datenstruktur: msg (Details siehe z.b. Herold) - Zeiger auf den Inhalt der Meldung Der Kernel unterhält zwei weitere Queues - Queue: wwait Warteschlange für Prozesse, die in eine volle Queue schreiben möchten - Queue: rwait Warteschlange für Prozesse die von einer leeren Queue lesen möchten 24
Signale (POSIX) slide CTRL-C SIGINT Ähnlich wie HW-Interrupts keine Priorisierung Signalverarbeitung sobald Prozess in den Zustand Running geht Signal ruft entweder - Defaultaktion (meist Prozess terminieren, z.t. ignorieren) - oder Signal Handler (Benutzer-Prozedur) blockierte Signale - mehrfaches Eintreffen der Signale nur einmal gespeichert Rückkehr aus Handler Beispiel - für Prozess wie normale Rückkehr aus Prozedur Shell Befehl kill PID sendet SIGKILL an Prozess mit Prozessidentifikation PID Prozess terminiert 25 Jedes Signal wird durch einen numerischen Wert dargestellt - traditionell 16 Signale - heute abhängig von Prozessorwortbreite: 32 oder 64 Signale - jedes Signal entspricht einem Bit Beispiele POSIX.1 Signale Default Aktion SIGINT Interrupt Taste gedrückt terminiert SIGQUIT Quit Taste gedrückt terminiert mit core SIGTERM Terminieren gewünscht terminiert SIGKILL Terminieren erzwungen terminiert SIGHUP Terminalsession beendet terminiert SIGCHDL Terminierung eines Kindes ignorieren SIGALRM Ablauf eines Timers terminiert SIGPIPE write() auf Pipe ohne Reader terminiert SIGUSR1 frei definierbar terminiter SIGUSR2 frei definierbar terminiert Für weitere Informationen: siehe Literatur 25
... Signale Beispiel int flag = 2; void SignalHandler(int sig) { printf("handler called by signal %d\n", sig); flag--; } int main(void) { struct sigaction sig; sig.sa_handler = SignalHandler; sig.sa_flags = SA_RESTART; sigemptyset(&sig.sa_mask); sigaction(sigusr1, &sig, NULL); } while (flag > 0) printf("main terminates, flag = %d\n", flag); 26 sigemptyset(&sig.sa_mask); - in der sa_mask aufgeführte (gesetzte) Signale werden während der Ausführung des aktuellen Handlers blockiert - setzen von Signalen in der Maske, z.b. SIGUSR2 und SIGALRM: sig.sa_mask = sig.sa_mask SIGUSR2 SIGALRM; 26
Pipes (Posix) Pipe FIFO-Buffer mit fester Grösse vom Kernel verwaltet Datenaustausch zwischen verwandten Prozessen halbduplex Zugriff synchronisiert Reihenfolge nicht garantiert write() read() P P 1 1 Pipe P 2 fd[1] fd[0] P 4 P 3 27 Funktionalität - blocking write schreiben auf volle Pipe blockiert den schreibenden Prozess, bis ein anderer Prozess Daten gelesen hat - blocking read lesen von einer leeren Piepe blockiert Prozess, bis ein anderer Prozess in die Pipe geschrieben hat - Fehlerbehandlung falls kein Prozess die Pipe zum Lesen geöffnet hat, erhält der schreibende Prozess das Signal SIGPIPE falls kein Prozess die Pipe zum Schreiben geöffnet hat, wird eine 0 zurückgegeben - das Schreiben kann nicht unterbrochen werden Ausnahme: Pipe ist voll - Pipes arbeiten verbindungslos lesen resp. schreiben wer gerade an der Reihe ist Schreiben und Lesen der Pipe mit den System Calls write() und read() - write(fd, *buffer, num_bytes) schreibt num_bytes aus dem Buffer buffer in das File fd (hier Filedeskriptor der Pipe fd[1]) - int read(fd, *buffer, num_bytes) liest num_bytes vom File fd (hier Filedeskriptor der Pipe fd[1]) in den Buffer buffer, Rückgabewert gibt Anzahl effektiv gelesener Bytes an - die Anzahl Bytes, die geschrieben und gelesen werden müssen nicht gleich sein PIPE arbeitet wie FIFO auf Byteebene 27
slide... Pipes Einrichten einer Pipe Prozess P A fd[0] fd[1] int fd[2]; pipe(fd); Prozess P A fd[0] fd[1] pipe fork() pipe fd[0] fd[1] Kindprozess P C 28 Zwei Typen von Pipes - Pipes und Named Pipes resp. Ffos nnnamed Pipes - ein Paar von Fildeskriptoren: einer zum Lesen, einer zum Schreiben - kann nur von verwandten Prozessen verwendet werden named Pipes - ein File vom Typ FIFO wird erzeugt - Zugriff für beliebige Prozesse, weil File 28
... Pipes Beispiel (vereinfacht) 29 int main(void) { char *msg1 = "A first message"; char *msg2 = "Let's try a second time"; int fd[2]; char buffer[32]; pipe(fd); pid_t pid = fork(); if (pid > 0) { close(fd[0]); write(fd[1], msg1, strlen(msg1)+1); write(fd[1], msg2, strlen(msg2)+1); wait(null); } if (pid == 0) { close(fd[1]); read(fd[0], buffer, strlen(msg1)+1); printf("%s\n", buffer); read(fd[0], buffer, 1); while (buffer[0]!= '\0') { printf("%c", buffer[0]); read(fd[0], buffer, 1); } printf("\n"); } } Beispiel: Einrichten einer Pipe Zugriff: FIFO - synchronisiert - keine strukturierte Daten Datenstrukturierung beim Anwender Bytestrom (ganzer Block oder einzelne Bytes) schreiben und lesen können unterschiedlich sein nur Byte-Buffer: 1 bis N Bytes - Schrieben und Lesen blocking read und blocking write 29
... Named Pipe / Fifo (auch für nicht verwandte Priozesse) Beispiel (vereinfacht) int main(void) { char *msg1 = "A first message"; char *msg2 = "Let's try a second time"; char *fifo = "/tmp/myfifo"; char buffer[32]; mkfifo(fifo, 0770); int fd = open(fifo, O_RDWR); pid_t pid = fork(); if (pid > 0) { write(fd, msg1, strlen(msg1)+1); write(fd, msg2, strlen(msg2)+1); wait(null); close(fd); unlink(fifo); } if (pid == 0) { int fd = open(fifo, O_RDWR); read(fd, buffer, strlen(msg1)+1); printf("%s\n", buffer); read(fd, buffer, 1); while (buffer[0]!= '\0') { printf("%c", buffer[0]); read(fd, buffer, 1); } printf("\n"); } } 30 Einrichten eines Fifos resp. einer named Pipe Zugriff: FIFO - synchronisiert - keine strukturierte Daten Datenstrukturierung beim Anwender Bytestrom (ganzer Block oder einzelne Bytes) schreiben und lesen können unterschiedlich sein nur Byte-Buffer: 1 bis N Bytes - Schrieben und Lesen blocking read und blocking write 30
TCP/IP - Sockets Server Netzwerk host: Romeo, IP: 164.183.12.3 Client Romeo Client host: Julia, IP-Nr. 164.183.12.7 Server-Prozess 1 Port 5243 host: Caesar, IP: 164.183.12.4 Client Caesar Server-Prozess 2 Port 5555 host: Willi, IP: 164.183.12.5 Client Willi Tell 31 Sockets (Steckdosen) - Typen TCP/IP Sockets:Netzwerk Unix Domain Sockets. lokal - vor allem Client-Server Kommunikation - vielfältig und flexibel (dafür auch komplizierter) - verbindungsorientierte und verbindungslose Kommunikation Socketverbindung 4 Parameter: Server und Client IP-Adresse und Port-Nummer Protokolle - Datagramme Pakete: ohne Fehler- und Sequenzkontrolle, z.b. UDP - Streams einzelne Bytes: mit Fehler-und Sequenzkontrolle z.b. TCP/IP, grössere Bytefolgen oft zu Blöcken gruppiert - Protokolle: z.b. TCP/IP, Novell IPX, AppleTalk DDS, AX.25, NetROM IPC grundsätzlich zwischen nicht verwandten Prozessen, schliesst aber auch verwandte Prozesse ein TCP / IP Sockets: Kommunikation zwischen Prozessen auf vernetzten Rechnern - Adresse besteht aus IP Adresse und Port Nummer - Server: falls INADDR_ANY spezifiziert, nur Port Nummer von Bedeutung - Client: muss IP Adresse und Port Nummer spezifizieren Unix Domain Sockets: Kommunikation innerhalb eines (Unix) Rechners - Adresse mit Pfadname assoziiert (ähnlich wie bei Named Pipe) - Client und Server müssen auf das "File" Zugriff haben - bind() bindet den Socket an diese Adresse 31
Unix/Linux: TCP/IP-Sockets int sfd, fd, addrlen; struct sockaddr_in addr; sfd = socket(pf_inet, SOCK_STREAM, 0); j = 1; setsockopt(sfd,sol_socket,so_reuseaddr,&j,sizeof(j)); addr.sin_family = AF_INET; addr.sin_port = htons(port_number); addr.sin_addr.s_addr = INADDR_ANY; bind(sfd, (struct sockaddr *)&addr, sizeof(addr)); listen(sfd, 5); fd = accept(sfd, (struct sockaddr *) &addr, (unsigned *) &addrlen)) >= 0); write(fd, buf, LEN); read(fd, buf, LEN) int sfd, addrlen, j; struct sockaddr_in addr; struct in_addr iaddr; struct hostent *compi; compi = gethostbyname("diva.zhwin.ch"); sfd = socket(pf_inet, SOCK_STREAM, 0); write(sfd, buf, LEN); read(sfd, buf, LEN) addr.sin_family = AF_INET; addr.sin_port = htons(port_number); memcpy(&addr.sin_addr, compi->h_addr_list[0], sizeof(addr.sin_addr)); connect(sfd, (struct sockaddr *) &addr, sizeof(addr)); 32 Systems Calls für das Aufsetzen von Sockets - socket() erzeugt Socket, nicht intialisiert - bind() Socket wird an Adresse (IP Adresse, Port Nummer) gebunden INADDR_ANY: nur Port Nummer von Bedeutung - listen() Initialisiert Queue für Server, für Zwischenspeicherung von Verbindungswünsche durch Clients - accept() baut Verbindung mit Client aus Queue auf - connect() Client fordert Verbindung zu Server an resp. baut auf System Calls für den Datenaustausch - read() lesen von verbindungsorientiertem Socket recv() dito - write() schreiben auf verbindungsorientierten Socket send() dito - recvfrom() Daten von verbindungslosem Socket empfangen - sendto() Daten über verbindungslosen Socket senden Port Nummern - 1-255 reserviert: wohlbekannt, für Standardanwendungen - 1-2023 privilegiert: Superuser Prozesse für unixspezifische Anwendungen - 1024-5000 kurzlebig: automatische Vergabe an Benzuterprozesse, die nicht auf bestimmte Nummer angewiesen sind - 5001-65535 benutzerdefiniert: können von nichtprivilegierten Serverprozessen beansprucht werden und an Clients weitergegeben werden 32
Verbindungsorientierter TCP/IP Socket Server Client socket() bind() listen() Programmstruktur für verbindungsorientierten Socket parent accept() fork() child Verbindungsaufbau socket() bind() connect() read() write() Datenaustausch read() write() close() exit() close() exit() 33 Verbindungsorientierter Socket - Server - Client oft wird nach Verbindungsaufbau ein Kindprozess erzeugt, der den Datenaustausch wahrnimmt der Elternprozess wartet auf die nächste Anfrage bind() fakultativ, meist nicht von Bedeutung über welche Adresse die Anfrage geht Verbindungsloser Socket - es muss keine Verbindung aufgebaut werden 33
Shared Files (Unix/Linux) Shared Files Kommunikation über gemeinsame Files sehr einfach eher langsam keine Synchronisation Memory Mapped Files Files in Speicherbereich abbilden schneller als Shared Files keine Synchronisation Konsistenzsemantik - Anwender verantwortlich für das Rückschreiben der Daten - keine Kontrolle, wann Daten für andere verfügbar 34 34
Semaphore (Posix) Semaphor erzeugen und initialisieren named Semaphor sem_t s1; s1 = sem_open("/tmp_sem1", O_CREAT O_RDWR, 0700, value); unnamed Semaphor sem_t s1; rv = sem_open(&s1, pshared, 0700, value); System Calls Semaphor schliessen Semaphor öffnen rv = sem_wait(&s1); rv = sem_post(&s1); 35 Posix Semaphore sind im Gegensatz zu System V sehr einfach zu benutzen, dafür aber auch weniger mächtig sem_open() - setzt Semaphorwert auf Value sem_init() - pshared = 0 Semaphore in einem Prozess global definiert genutzt durch mehrere Threads - pshared >= Semaphore in Shared Memory, genutzt durch mehrere Prozesse sem_trywait() wenn Semaphore gschlossen nicht warten errno = EAGAIN sem_timedwait() nur für bestimmte Zeit warten, falls geschlossen 35
IPC Mechanismen in Windows IPC ähnliche Verfahren oft andere Namen Zugriff über Handle verschiedene Prozesse mehrere Handles auf gleiche Kernel Objekte Windows IPC Pipes, named Pipes, anonymous Pipes Shared Memory / File Mapping Mailslots (Message Queues) Winsock Sockets Remote Procedure Calls (RPC) WM_COPYDATA (Messages) 36 36
IPC: Zusammenfassung Interprozesskommunikation Message Queues Pipes - unnamed: verwandte Prozesse - named (auch) nicht verwandte Prozesse Sockets - flexibel und vielfältig (auch andere BS) - TCP / IP über Netzwerk - Unix Domain innerhalb eines Rechners (ähnlich wie Pipes) Signale Synchronisation unter Unix Semaphore Counting Semaphores Lock-Files mutex über File (z.b. bei Daemonen) 37 37