Prozess/Thread-Kommunikation unter Linux

i_hasser

Grand Admiral Special
Mitglied seit
06.06.2002
Beiträge
18.964
Renomée
85
Standort
IO 0x60
Hi

Ich such eine Möglichkeit folgendes Szenario zu realisieren:

Prozess A besteht aus mehreren Threads, Prozess B aus einem einzelnen Thread (nur das letztere ist wichtig, also 1 Thread für Prozess B). Nun sollen zwischen beiden Prozessen Daten ausgetauscht werden.

Ablaufen soll das über ein Message System. Also Prozess B kann Prozess A irgendwelche Daten senden, und umgekehrt. Eine Message kann unterschiedlich groß sein und solche Sachen eben.

Etwas delikat ist allerdings die Verarbeitung dieser Messages, da das per Event-Handler geschehen soll, in einem einzigen Thread. Code macht das denke ich am besten deutlich:


Prozess B
Code:
int main()
{
    tuwas();
    tunochwas();
    // usw.
}

void message_handler(void* message)
{
    // event-handler für eingehende messages
}

Kommt eine Message soll die Ausführung von main() unterbrochen werden, und dafür der handler ausgeführt werden. Ist der Handler fertig, geht die Ausführung von main weiter.

Hab mich im Netz durch diverse Tutorials gelesen, aber noch nix wirklich passendes gefunden. Allgemein sind Multithreading und Interprozess-Wasauchimmer Sachen, mit denen ich mich noch nicht so weitgehend beschäftigt habe.

Ein weiterer Punkt wäre, dass ich den Thread von Prozess B solange in den Ruhestand schicken muss, bis eine Message ankommt. Also nicht nur sowas wie for(...) usleep(...), sondern, dass die Ausführung gänzlich stoppt. Idealerweise könnte der Thread das selbst veranlassen, und im Message-Handler auch wieder aufheben. Vielleicht kann man das ja irgendwie über die Scheduler-Einstellungen der POSIX-Threads machen?
 
In C/C++ unter Windows könnte ich Dir das Ganze fast auswendig herunterbeten, unter Linux muss ich aber (noch) passen. Aber interessieren tut es mich schon, weshalb ich das hier auch poste. Es steht bald die Portierung einer Entwicklungsumgebung für Firmware-Projekte (2 bis n Prozesse, ein Prozess via DLL mit mehreren Threads) bei mir an.
Unter Windows läuft es mit mehreren Compilern (MS-VC++, Borland C++, Open Watcom) und seit neuestem auch als 64-Bit Version mit VC++ 8.0 Beta2.
Frage mich, ob ich zuerst Cygnus unter Windows heranziehe und dann erst den Schritt nach Linux durchziehen soll...
Idealerweise sollte alles als Gesamtpaket wahlweise unter Linux oder Windows laufen.
Eine plattformübergreifende Geschichte also. Mal schaun.

Ciao,
Ray
 
Soweit geht das erstmal mit signals, allerdings lassen sich damit keine Daten übertragen. Man könnte zwar mit Signals signalisieren, dass irgendwo neue Daten verfügbar sind, nur kann man leider die Herkunft, also welcher Prozess ein Signal geschickt hat, afaik nicht bestimmen.
Bei einer begrenzten Anzahl von infragekommenden Quellen geht das, für mein Problem passt das aber nicht (bzw. weigere ich mich, sowas unsauberes zu implementieren - nein, in Wirklichkeit bin ich zu faul was zu schreiben, was zusammengeflickt wirkt und desswegen sowieso nochmal geändert werden müsste ;)).

Die Messages kann man auch per (named) pipes senden, hab dazu ja noch einen Thread aufgemacht. Da hab ich das Problem, dass ich eben so einen handler bräuchte der aufgerufen wird, wenn was neues angekommen ist. Nun könnte man das zwar per signal implementieren, aber weis dann eben nicht, welcher Prozess das signal geschickt hat, also von welchem Prozess neue Daten zu erwarten sind.
 
Die gewünschte Funktionalität sollten dir IPC-Messages liefern. Wenn ich mich jetzt richtig erinnere, blockiert msgrcv eh, wenn die Queue keine Nachrichten bereit hat, sprich der Prozess wird schlafen gelegt.
 
Ich brauch es aber nicht nach dem poll Prinzip, sondern nach dem Interrupt-Prinzip. So wie signal-handler. Geht das auch über IPC Messages? Hab da nämlich nix passendes gefunden...

Unschön wäre es das in mehreren Threads zu realisieren (von der Performance her), aber das wäre wahrscheinlich eine Möglichkeit... unschön desswegen, weil ich dann 2 Threads brauche wovon einer nur auf eingehende Nachrichten wartet. Außerdem müsste dann der 2. Thread pollen, ob beim 1. eine Nachricht angekommen ist.

So wirklich ideal ist das alles nicht.
 
Ich bin mir jetzt nicht mehr 100%ig sicher, da es auch schon wieder ein paar Jährchen her ist, seit ich intensiv mit IPC gearbeitet habe, aber ich glaube mich zu erinnern, dass msgsnd und msgrcv blockierend arbeiten. Da brauchst du also nix pollen, die Synchronisation das OS für dich.
 
Geht das bei Messages nicht auch wie bei Signalen? Sonst muss ich für jeden Prozess mindestens 2 Threads machen, und das wäre ja Verschwendung ;). Außerdem wird so jeder Prozess multithreaded, was die Sache auch nicht übersichtlicher macht.
 
Geht das bei Messages nicht auch wie bei Signalen? Sonst muss ich für jeden Prozess mindestens 2 Threads machen, und das wäre ja Verschwendung ;). Außerdem wird so jeder Prozess multithreaded, was die Sache auch nicht übersichtlicher macht.

Ich weiß jetzt nicht, was du mit "wie bei Signalen" meinst.

Das grundsätzlich Vorgehen ist folgendes:
Du erzeugst eine message-queue, egal von welcher Seite. Diese nimmst du mit dem anderen Prozess entgegen. Dann rufst du in dem Prozess, welcher die Nachricht empfangen soll, msgrcv auf. Liegt in der queue keine Nachricht vom passenden typ vor, so blockiert der Prozess. Sobald eine passende Nachricht eingeht, wird der Prozess dann wieder aufgeweckt.
Hier mal den Auszug aus dem manual:
If no message of the requested type is available and IPC_NOWAIT isn't specified in msgflg, the calling process is blocked until one of the following conditions occurs:

A message of the desired type is placed in the queue.

The message queue is removed from the system. In this case the system call
fails with errno set to EIDRM.

The calling process catches a signal. In this case the system call fails
with errno set to EINTR.
 
Beispiel:

Code:
#include <stdio.h>     /* standard I/O functions                         */
#include <unistd.h>    /* standard unix functions, like getpid()         */
#include <sys/types.h> /* various type definitions, like pid_t           */
#include <signal.h>    /* signal name macros, and the signal() prototype */

/* first, here is the signal handler */
void catch_int(int sig_num)
{
    /* re-set the signal handler again to catch_int, for next time */
    signal(SIGINT, catch_int);
    /* and print the message */
    printf("Don't do that");
    fflush(stdout);
}

.
.
.
/* and somewhere later in the code.... */
.
.

/* set the INT (Ctrl-C) signal handler to 'catch_int' */
signal(SIGINT, catch_int);

/* now, lets get into an infinite loop of doing nothing. */
for ( ;; )
    pause();

Das Programm verzweigt automatisch zum handler, wenn ein Signal ankommt.
 
Doch, hat es. Ich habe schon meine Gründe wieso ich das mit einer Unterbrechung des Threads haben will, und nicht auf Abfrage.
 
Ok, ich glaube, jetzt weiß ich, was du meinst. Das kannst du über FIFOs oder über Messages realisieren. Messages wären wahrscheinlich besser, weil die Prozess B automatisch schlafen legen, so er auf eine Nachricht warten muss. Die Unterbrechung des aktuellen Programmfluss kannst du mit Signalen machen. SIGUSR1 und SIGUSR2 würden sich dafür anbieten.
Wenn es dir auf Geschwindigkeit ankommt, musst du mal messen. FIFOs und Messages haben bei unterschiedlichen Nachrichtengrößen verschiedenes Verhalten.
 
Na dann werd ich das so machen. Wie komm ich eigentlich an die struct ran, die die Senderpid enthält? Wenn du es nicht aus dem Kopf weist kann ich auch nachlesen, hab eine ganz brauchbare Seite zu signals gefunden.
 
ok danke, dürfte damit funktionieren.
Ja, meistens...

Bei Signalen muss man bei jedem einzelnen System- oder lib-Call checken, ob die Funktion async-signal-safe ist, das ist noch eine Stufe über mt-safe (steht auf man page). Also so viele erlaubte calls gibt es nicht, aus dem Gedächtnis gehören read/write dazu, printf nicht. Auch der eigene Code muss jederzeit unterbrechbar sein.

Also praktisch kann man im Signalhandler nicht viel mehr tun als ein Flag setzen, das im "Vordergrundprogramm" gepollt wird.

Der richtige Weg geht über Socketkommunikation, mal auf die Schnelle ein Einstiegslink:
http://www.willemer.de/informatik/unix/unprsock.htm

Das(!) Buch ist von Stevens : "UNIX Network Programming, The Sockets Networking"
http://www.amazon.de/exec/obidos/ASIN/013490012X/302-6182816-5139223

Je nachdem, wie weit man es treiben will.
 
Wieso willst du für IPC mit aller Gewalt Sockets, sprich Mechanismen zur Netzwerkkommunikation verwenden? Schonmal daran gedacht, dass das einen zusätzlichen Overhead ins Programm bringt?
 
Wieso willst du f&#252;r IPC mit aller Gewalt Sockets, sprich Mechanismen zur Netzwerkkommunikation verwenden? Schonmal daran gedacht, dass das einen zus&#228;tzlichen Overhead ins Programm bringt?
Weil man das im professionellen Bereich nun mal so macht. Wenn im Vordergrund wirklich nur pause() l&#228;uft kann man nat&#252;rlich im Signalhandler alles ausf&#252;hren, nur meist ist das nicht so.

Ein relativ einfacher und anschaulicher Weg (auf dem gleichen Rechner) ist die Kommunikation &#252;ber ein File (z.B. /tmp/myfile), ser "client" schreibt etwas hinein, der "server" liest. Wenn der Server noch etwas anderes tun soll, dann benutzt man poll oder select (oder einen zweiten Thread) zum Warten auf den read-event (nicht ausprobiert, aber sollte funktionieren).
 
Weil man das im professionellen Bereich nun mal so macht.

Das glaube ich nicht, Tim!

Wenn im Vordergrund wirklich nur pause() läuft kann man natürlich im Signalhandler alles ausführen, nur meist ist das nicht so.

Ein relativ einfacher und anschaulicher Weg (auf dem gleichen Rechner) ist die Kommunikation über ein File (z.B. /tmp/myfile), ser "client" schreibt etwas hinein, der "server" liest. Wenn der Server noch etwas anderes tun soll, dann benutzt man poll oder select (oder einen zweiten Thread) zum Warten auf den read-event (nicht ausprobiert, aber sollte funktionieren).

Klar, und wenn der Client alle paar Minuten mal was an den Server schreibt, pollt der Server munter die ganze Zeit, weil wir ja auch sonst nichts sinnvolles mit der Rechenzeit anfangen können. *buck*
 
Das glaube ich nicht, Tim!
Klar, und wenn der Client alle paar Minuten mal was an den Server schreibt, pollt der Server munter die ganze Zeit, weil wir ja auch sonst nichts sinnvolles mit der Rechenzeit anfangen k&#246;nnen. *buck*
Glaub, was du willst. Wie ich bereits geschrieben hatte, benutzt man poll() oder select() (RTFM! ;D). Wo geht da jetzt Rechenzeit verloren?

Hier ist ein interessanter Artikel zum Gebrauch von select() bei message queues:
http://linuxgazette.net/issue92/hawk.html

Falls die Funktion "msgqToFd(int msgq_id)" verf&#252;gbar ist, ist die L&#246;sung einer socketbasierten ebenb&#252;rtig, die erste Variante mit dem timeout ist "ugly".
 
Zuletzt bearbeitet:
Zurück
Oben Unten