Aufgabe 6 piper

Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.

Aufgabe 6 piper
Ixh hab mal ne Frage zu den Übungsfolein zur piper Aufgabe. Und zwar steht auf Folie 7-14 wo die Threads erzeugt werden als Argument (
(void* () (void)) mult irgendwie versteh ich das nicht ganz ich caste doch den Funktionspionter hier nur aber wie genau das eine (void *) steht ja für den Rückgabetyp der Funktion aber der Rest?
Und dann nochetwas: wieso ist die Parameterübergabe mit pthread_creat() nochmal problematisch, das hab ich irgendwie aus der Übung nicht mitgenommen…

Und dann noch eine Frage zum Haupt-Thread in der Aufgabe:
was soll gescheihen, wenn die Zeile länger als 100 zeichen ist, sollen dann die ersten 100 trotzdem an die Threads übergeben werden und die restliche Zeile ignoriert werden, oder soll die komplette Zeile weggelassen werden?

Und noch eine Frage zu sbuf:
bei sbuf_create soll man ja einen Puffer für Semaphoren anlege. Soll man diese dann noch gar nicht mit einer festen Größe initialisieren, also soll man hierbei nur ein Array passender Länge anlegen? Und diese Typedef von SBUF darf man die noch entsprechen anlegen, also mit Semaphorenelementen oder ,uss ich mir dazu eine andere anlegen?


[m](a (*) (b, c))[/m] ist ein Funktion, die den Typ [m]a[/m] zurückgibt und jeweils einen Parameter vom Typ [m]b[/m] und [m]c[/m] hat.

Solltest du sowas nicht besser deinen Tutor in der Übung fragen?

Das Problem ist das Casten zwischen Pointern und Nicht-Pointern. Das ist implementation-defined (also ist nicht definiert was genau passiert) und problematisch wenn die Datentypen unterschiedliche Größen haben. Der beste Weg ist immer einen Pointer zu übergeben, und nicht den Wert selbst.

Dann wird der Rest der Zeile einfach beim nächsten [m]fgets(3)[/m]-Aufruf an die Threads weitergegeben.

Hast du dir die Schnittstelle mal durchgelesen? Die Parameter von [m]sbufAddSem()[/m] zeigen was du in [m]sbufCreate()[/m] machen musst und was nicht.

Das struct SBUF darfst/musst du in der sbuf.c so anlegen, wie du es benötigst.


Danke für deine Antworten.


Jetzt hab ich nochmal eine andere Frage: Ich hab mir ein Makefile zur piper geschrieben. Wenn ich jetzt compilier bekomm ich folgende Ausgabe:
/usr/bin/ld: i386 architecture of input file `sbuf.o’ is incompatible with i386:x86-64 output (wenn ich auf einen faui04i pc bin)

wenn ich auf einen faui01 pc mich anmelde bekomm ich folgende Augabe:
i386:x86-64 architecture of input file `sem.o’ is incompatible with i386 output

Hat jemand ne Idee, was genau da falsch läuft bzw wie ich da beheb?


Die faui04i ist ein 64-Bit-System und ich vermute, dass du die sbuf.o auf der faui01 erstellt hast. Lösch einfach die sbuf.o-Datei ([m]make clean[/m]) und lass sie neu erzeugen.

Die Vorgabedatei sem.o ist für x86-64 (also 64-Bit) erzeugt worden und kann somit nur auf 64-Bit-Systemen verwendet werden. Die faui01 hat zwar einen 64-Bit-Kernel (siehe Ausgabe von [m]uname -a[/m]) aber nur ein 32Bit-Userland (siehe Ausgabe von [m]gcc -dumpmachine[/m])

Threads beenden im Fehlerfall
In der threadspezifischen Funktion gibt es bei mir einige Fehlerüberprüfungen von denen einige [m]NULL[/m] returnen aber die main noch weiterlaufen lassen.
Zu den [m]EXIT_FAILURE[/m] Kandidaten gehören bei mir das Erstellen einer Threadsemaphore und deren Hinzufügen in den sbuffer - außerdem [m]fwrite(), fflush(), fclose()[/m].
Meine Fragen:
(1) macht es Sinn die main weiterlaufen zu lassen obwohl eines der oben genannten kaputt gegangen ist, wenn ja welches?

(2) braucht man überhaupt ein [m]fclose()[/m]? Denn eigentlich wird ein Thread, wenn er mal normal läuft, von außen beendet und man kommt garnicht zu einem “sauberen” return.


Ja, es macht Sinn den Haupt-Thread weiterlaufen zu lassen. In welchen Fällen es eventuell keinen Sinn macht, das Programm weiterlaufen zu lassen, solltest du selbst entscheiden.

Das fclose() würde ich als guten Stil empfinden, ist aber in der piper nicht notwendig.
Natürlich kann man die EOF-Behandlung an die „Schreib-Threads“ weitergeben. Aber effektiv macht es keinen Unterschied zu einem exit() im Haupthread.

Vielleicht ändern wir die Aufgabe beim nächsten Durchlauf noch etwas ab und bauen eine EOF-Behandlung durch die Schreib-Threads ein :slight_smile:


Meine piper scheint bis auf (hoffentlich nur) eine Kleinigkeit zu funktionieren, aber ich weiß nicht was ich in folgender Situation tun soll:
Wenn ich mich in einer ganz normalen Benutzung (pipes bereits geöffnet) und EOF eingebe, beendet sich das Programm wie gewünscht.
Wenn ich aber EOF eingebe, BEVOR ich eine pipe mit cat öffne, hängt sich mein Programm auf. Ich bin mir ziemlich sicher, dass es an fopen() liegt, da fopen() darauf wartet, dass die pipe öffnet oder eben daran scheitert - nur leider trifft beides nicht ein. Was also kann ich in meiner main-Funktion tun, um fopen() abzubrechen, damit mein Programm sich beenden kann?


In deiner [m]main()[/m] solltest du kein [m]fopen(3)[/m] haben, nur in den Arbeiter-Threads. Sobald [m]fgets(3)[/m] NULL zurückliefert (EOF oder Fehler), sollte sich die While-Schleife in der [m]main()[/m] beenden und dein Programm danach terminieren. Dass die Arbeiter-Threads noch im [m]fopen(3)[/m] warten, ist ja kein Problem. Durch das [m]exit(3)[/m] ([m]return[/m]) in der [m]main()[/m] werden die auch beendet.


fopen() befindet sich schon in meinen Schreib-Threads. Ich dachte, dass pthread_join() darauf wartet, dass die Threads terminiert sind, aber dann muss das Problem wohl woanders liegen.
Mein Programm sieht momentan etwa so aus:

main(): Ich lege 2 Semaphoren für jeden Schreib-Thread an (aufgeteilt in 2 Gruppen, also 2 SBUFs), starte die Threads. Dann kommt eine “fgets-Schleife” wie schon bei einigen der letzten Aufgaben. Nach dem lesen teile ich den Threads mithilfe der Semaphorengruppe 1 mit, dass sie nun auf ihrer Pipe arbeiten dürfen. Anschließend warte ich mithilfe der zweiten Semaphorengruppe auf die Threads und die Schleife beginnt von vorne, solange bis fgets NULL liefert. In diesem Fall wird ein Flag gesetzt, der den Schreib-Threads anzeigt, dass sie aufhören können.
Wenn 0 Threads gerade “aktiv” sind, wird solange mit dem nächsten Schleifendurchlauf weitergemacht, bis ich eine Pipe mit cat öffne.

Meine threadRoutine(): Öffnen der Datei mit fopen(), Erhöhen eines Zählers für die aktiven Threads. Danach eine Schleife, die solange läuft, solange besagter Flag aus main() nicht gesetzt wurde. In der Schleife blockiere ich zuerst mithilfe des zugehörigen Semaphors aus Semaphorengruppe 1, bis main() die Blockade aufhebt. Dann verwende ich fflush() und teile der main() mit (Semaphor aus Gruppe 2), dass das nächste Wort eingelesen werden darf.


Das tut es auch → kein [m]pthread_join(3)[/m] verwenden. Falls du den Speicher freigegeben willst, [m]pthread_detach(3)[/m] verwenden.

Wozu brauchst du diesen Zähler? Das stellt dir SBUF schon zur Verfügung.

Du kannst das ganze auch mit nur einem SBUF lösen (dann brauchst du allerdings ein anderes Synchronisationsobjekt für die Schreibbestätigung der Threads an den Haupt-Thread.)


Danke, jetzt klappt es wie es soll - bis auf eine kleine Merkwürdigkeit: Habe gerade 3 Pipes ein paar Testzeilen ausgeben lassen und dann das Programm mit EOF beendet. EINE der Pipes gibt dann jedoch trotzdem noch sinnlose Zeichen aus: “�@D�@Dp”. Was könnte es denn damit schon wieder auf sich haben?

Das mit dem Zähler habe ich vllt. nicht gut genug beschrieben. Sobald ich cat auf die Pipe anwende wird ein “modulglobaler” (natürlich static) Zähler hochgezählt, den ich für Ausgaben wie “[piper] There are curently 0 clients” verwende.

Ich dachte mir auch erst, dass das etwas viele Semaphoren sind, aber eine elegantere Lösung ist mir nicht eingefallen. Aber die Funktionalität scheint ja so trotzdem erfüllbar zu sein.

Ansonsten habe ich noch ein Problem mit der Speicherfreigabe, das dadurch zustande kommt, dass ich in sbufCreate für die IDs und Semaphoren in meinem SBUF-struct Speicher anfordere, da sbuf.h keine remove()-Funktion o.Ä. bereitstellt. free(sbuf->ids) kann ich schließlich nicht in piper.c verwenden…


Uninitialisierter Speicher? valgrind!

Schau dir mal genau die API von SBUF an. Die x clients beziehen sich auf die aktiven Clients - denn diese melden sich ja erst nach dem [m]fopen(3)[/m] am SBUF-Modul an.

Das geht mit der API einfach nicht. Da musst du dir keine Gedanken machen.

1 Like

Soll der Aufruf der [m]sbufAddSem()[/m] Funktion in der Piper geschützt werden oder sollen wir sie schon threadsafe schreiben?


Die Funktion soll „threadsafe“ implementiert werden.


Das gilt auch fuer alle anderen Methoden des [m]SBUF[/m] (wobei man bei der [m]sbufCreate()[/m] wohl meist sehr wenig tun muss).


Also sollen wir jetzt unabhängig von der Piper im sbuf Modul Semaphoren verwenden die wir zum zugriff auf den Buffer verwenden? Ich dachte das immer nur einer auf den Buffer von sbuf dann zufreifen kann macht man in der piper…


Richtig (wobei du nicht so viele Semaphoren brauchst …). Ein SBUF soll natürlich von mehreren Threads aus parallel aufgerufen werden können. Das in der piper.c zu machen ist keine schöne Lösung, denn SBUF kann ja (theoretisch) auch in anderen Programmen genutzt werden und braucht damit eine korrekte Synchronisation.


Sollen wir den Bereich, der Synchronisiert abläuft, minimieren, oder dürfen wir den Bereich großzügig wählen :)?