C: fifo lesen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Benutzeravatar
bluestar
Beiträge: 2334
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: C: fifo lesen

Beitrag von bluestar » 23.08.2018 21:21:17

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 21:15:54
Da scheint mir das Abarbeiten des Event-Buffers am Ende des loops irgendwie logischer?
Das Problem beseht ja darin, dass du im worst case den Event Buffer nie abarbeitest.... So lange über deinen Fifo keine Daten reinkommen, bleibt das select(....) ja blockierend stehen... Ergo stauen sich die Events auf.

Was mir auch noch aufgefallen ist, dass du zwei Commands hintereinander auch auf einen Schlag einlesen könntest. In dem Falle musst du über deinen Empfangspuffer iterieren und alle Commands nacheinander abarbeiten. strchr(...) dürfte dir da weiterhelfen.

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: C: fifo lesen

Beitrag von Meillo » 23.08.2018 22:00:27

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 21:15:54
Meillo hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 17:44:00
... wobei manch ein C-Programmierer natuerlich ``if (!*name) {'' schreiben wuerde. ;-)
Ja, das würde ich auch versuchen, denn dann hätte man einen function-Aufruf weniger. Aber man hat mir gesagt, ich solle doch im Zweifelsfall lieber die Lesbarkeit als die Performance-Optimierung wählen... :mrgreen:
Ich habe das ja gerade wegen der Lesbarkeit vorgeschlagen! :-D

Die Lesbarkeit ist allerdings nur dann besser, wenn man das Idiom kennt und gewoehnt ist. Idiome sind ein wichtiges Hilfsmittel fuer gute Lesbarkeit: Man hat also Codestuecke, die immer gleich sind, so dass man sie gar nicht genau anschauen muss, sondern auf den ersten Blick weiss, was sie bedeuten. Ein typisches Idiom sind Zaehlschleifen in C, die man immer als ``for (i=0; i<num; i++) {'' schreiben sollte. Notfalls kann man auch noch ``for (i=1; i<=num; i++) {'' verwenden. Andere Faelle sollte man vermeiden. Wenn Code so geschrieben ist, dann muss ich mir keine Zaehlschleife mehr genau anschauen, weil ich auf den ersten Blick weiss, wie oft sie laufen wird.

Man entwickelt dann auch einen Blick dafuer, wenn sie ``komisch'' aussehen, d.h. also nicht so sind wie erwartet. Das sind aber Faelle, wo Kommentare sehr hilfreich sind: Immer dann wenn etwas nicht so ist wie man es erwarten wuerde. Codelesen hat viel mit Aufmerksamkeit zu tun. Man muss die Aufmerksamkeit des Codelesers an die relevanten Stellen lenken, weil er nur ein gewisses Mass an Aufmerksamkeit hat, das man moeglichst sinnvoll verteilen sollte. Das jedenfalls sind einige Meiner Gedanken zu gutem Code ... :-)


Den Unterschied zwischen 0, NULL und ’\0’ habe ich jetzt übrigens glaube ich schon besser verstanden:
- 0 ist ein integer
- NULL ist ein pointer, d.h., dass NULL auf eine Speicher-Adresse zeigt, deren Wert 0 ist. Zugleich ist NULL als Makro des Compilers definiert.
- ’\0’ ist ein Zeichen, das nichts enthält. Also ein byte, dessen bits alle 0 sind.
Richtig so?
Ja.

0 war einfach.

'\0' ist ein `char', und enthaelt das Zeichen ASCII NUL, also das allererste (an nullter Position). Es ist korrekt, dass bei ihm alle Bits null sind.

NULL ist ein Pointer. Ein Pointer hat als Wert eine Speicheradresse. NULL hat als Wert die Adresse 0. An der Stelle sind kein Code und keine Daten zu finden. Bei virtueller Speicherverwaltung ist die nullte Page dem Prozess nicht zugeordnet. Wenn man den NULL-Pointer dereferenziert, dann erzeugt das einen Segfault. Darum sollte man auch ganz brav immer erst auf NULL pruefen, bevor man Rueckgabewerte dereferenziert. ;-)

Definiert ist NULL hier: /usr/include/linux/stddef.h

Code: Alles auswählen

#define NULL ((void *)0)
Frueher (bevor es void* gab), war NULL ein char*. Letztlich muss es halt ein Pointer sein, der sich in jeden anderen casten laesst.
So wie ich es verstehe, kann man 0 immer für ifs nutzen, denn NULL und ’\0‘ laufen auch auf den int 0 hinaus.
Ja, das wird dann automatisch gecastet. Dabei geht alles glatt weil alle drei Werte (in allen Implementierungen) jeweils nur Nullen in allen Bits haben.

Wenn du aber explizit vergleichst, solltest du moeglichst die richtigen Typen verwenden, weil das IMO Irritationen vermeidet. Gerade als Anfaenger ist es in C wichtig stets auf die Datentypen zu achten. Zumeist sind es, meiner Erfahrung nach, eher die Anfaenger, die ueberall 0 schreiben wollen (damit sie sich das Nachdenken sparen koennen), als dass die Profis sich damit ein paar Zeichen sparen wollen. Sich aber bei C das Nachdenken und das komplette Verstehen sparen zu wollen, holt einen sehr schnell ein! C ist ein scharfes Messer -- man sollte besser genau wissen was man damit tut!


Btw: Der obere und der untere Teil meiner Mail scheinen sich zu widersprechen. Teilweise tun sie das ... und zeigen damit, dass es stets um das Abwaegen verschiedener Ziele geht. Es gibt nur wenige einfache Antworten (wie, auch bei einzeiligen Bloecken immer geschweifte Klammern zu setzen ;-) ); fast immer geht es um das Verstehen der komplexen Zusammenhaenge und das Treffen begruendbarer und stimmiger Entscheidungen.
Use ed once in a while!

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: C: fifo lesen

Beitrag von Meillo » 23.08.2018 22:04:43

bluestar hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 21:21:17
Was mir auch noch aufgefallen ist, dass du zwei Commands hintereinander auch auf einen Schlag einlesen könntest. In dem Falle musst du über deinen Empfangspuffer iterieren und alle Commands nacheinander abarbeiten. strchr(...) dürfte dir da weiterhelfen.
... so findet man bei Low-Level-Loesungen oft noch diesen und jenen unbedachten Randfall.

Dieser Fall kann bei der fgets(3)-Loesung nicht vorkommen. ;-)
Use ed once in a while!

RobertDebiannutzer
Beiträge: 385
Registriert: 16.06.2017 09:52:36

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 23.08.2018 23:14:02

Meillo hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 22:00:27
Sich aber bei C das Nachdenken und das komplette Verstehen sparen zu wollen, holt einen sehr schnell ein!
Ja, das sehe ich ein. Ich habe auch den Text über 0, NULL und '\0' nicht von irgendwo aus dem Internet per Copy&Paste in meinen Beitrag eingefügt, sondern habe den Inhalt nach dem Recherchieren und Verstehen selbstständig geschrieben. Ich hatte nur Teile meines Beitrages im Schreibprogramm verfasst, nicht in der Textbox des Forums, deshalb sind die Quotes um \0 andere als sonst (nicht, dass Du Falsches vermutest).
bluestar hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 21:21:17
Was mir auch noch aufgefallen ist, dass du zwei Commands hintereinander auch auf einen Schlag einlesen könntest. In dem Falle musst du über deinen Empfangspuffer iterieren und alle Commands nacheinander abarbeiten.
Meinst Du, dass ich das als feature integrieren könnte oder ist das ein bug? Wenn letzteres, wieso kann es dazu kommen? Es ist doch so, dass nach einem write die Schleife einmal durchläuft und beim nächsten write dann wieder. Wie kann read da zwei Zeilen - also zwei writes - auf einmal einlesen?

ad btw (Latein-Englisch-Misch-Slang 8) ):
Ich könnte mir geschweifte Klammern um einzeilige Blöcke vorstellen, doch in der Praxis mache ich es dann doch immer anders (weil es auch im Suckless-Code - soweit ich gesehen habe - so gemacht wird).
Die C-for-loops kenne ich glücklicherweise schon (ziemlich ähnlich) von bash: "for ((i=1; i<=5; i++)); do echo $i; done". Wusste aber noch nicht, dass man auch "i<number" schreiben kann. In bash wäre das auch möglich (habe es gerade probiert): "for ((i=1; i<5; i++)); do echo $i; done". Gibt dann eben 1-4 aus. Somit muss man 6 schreiben, wenn man 5 will. Da ich an die erste Variante gewöhnt bin, fand ich das erstmal nicht so leserlich, ist aber im Prinzip ja egal...

So, genug für heute, die X-Problematik, die @bluestar angesprochen hat, nehme ich morgen nochmal in Angriff...

Benutzeravatar
bluestar
Beiträge: 2334
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: C: fifo lesen

Beitrag von bluestar » 23.08.2018 23:22:43

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 23:14:02
bluestar hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 21:21:17
Was mir auch noch aufgefallen ist, dass du zwei Commands hintereinander auch auf einen Schlag einlesen könntest. In dem Falle musst du über deinen Empfangspuffer iterieren und alle Commands nacheinander abarbeiten.
Meinst Du, dass ich das als feature integrieren könnte oder ist das ein bug? Wenn letzteres, wieso kann es dazu kommen? Es ist doch so, dass nach einem write die Schleife einmal durchläuft und beim nächsten write dann wieder. Wie kann read da zwei Zeilen - also zwei writes - auf einmal einlesen?
Ich würde das als Bug bezeichnen, das Problem entsteht halt, wenn du in einem Write "name1\nname2\n" sendest.
Zum Testen kannst du ja mal auf der Konsole folgendes eingeben:

Code: Alles auswählen

echo -e "name1\nname2">/home/user/fifo

Benutzeravatar
schorsch_76
Beiträge: 2535
Registriert: 06.11.2007 16:00:42
Lizenz eigener Beiträge: MIT Lizenz

Re: C: fifo lesen

Beitrag von schorsch_76 » 24.08.2018 07:33:18

bluestar hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 23:22:43
Ich würde das als Bug bezeichnen, das Problem entsteht halt, wenn du in einem Write "name1\nname2\n" sendest.
Zum Testen kannst du ja mal auf der Konsole folgendes eingeben:

Code: Alles auswählen

echo -e "name1\nname2">/home/user/fifo
Jop, das ist ein Bug. Das habe ich eben gemeint mit "Die Bytes in einen Parser schicken, consumend_bytes und den Status des Requests" zurück geben.

Wenn du das mit "name1\nname2" rein schickts ist das ein atomic write von 2 Requests. (Fifo und pipe schreiben atomic wenn die Größe kleiner als PIPE_BUF ist). Das ist auch so in POSIX spezifziert. PIPE_BUF ist auf amd64 Linux 64k.

[1] https://www.gnu.org/software/libc/manua ... -and-FIFOs

EDIT: Hier ist so ein Beispiel in Boost wie so was geht.
[2] https://www.boost.org/doc/libs/1_68_0/d ... parser.hpp
[3] https://www.boost.org/doc/libs/1_68_0/d ... parser.cpp

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: C: fifo lesen

Beitrag von Meillo » 24.08.2018 08:56:44

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 23:14:02
Meillo hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 22:00:27
Sich aber bei C das Nachdenken und das komplette Verstehen sparen zu wollen, holt einen sehr schnell ein!
Ja, das sehe ich ein. Ich habe auch den Text über 0, NULL und '\0' nicht von irgendwo aus dem Internet per Copy&Paste in meinen Beitrag eingefügt, sondern habe den Inhalt nach dem Recherchieren und Verstehen selbstständig geschrieben. Ich hatte nur Teile meines Beitrages im Schreibprogramm verfasst, nicht in der Textbox des Forums, deshalb sind die Quotes um \0 andere als sonst (nicht, dass Du Falsches vermutest).
Keine Sorge, ich hatte nichts Falsches vermutet. ;-)

ad btw (Latein-Englisch-Misch-Slang 8) ):
Ich könnte mir geschweifte Klammern um einzeilige Blöcke vorstellen, doch in der Praxis mache ich es dann doch immer anders (weil es auch im Suckless-Code - soweit ich gesehen habe - so gemacht wird).
Auch im K&R fehlen die Klammern. In Go haben die gleichen Sprachdesigner sie aber erzwungen ... weil es das bessere Design ist. Wenn du in einen Ein-Zeilen-Block ohne Klammern eine weitere Zeile hinzufuegen willst, musst du drei Zeilen veraendern. Falls du vergisst die geschweiften Klammern zu ergaenzen hast du einen Bug. Das lohnt sich IMO nicht in Kauf zu nehmen ... nur weil man sich zwei Zeichen zu tippen und eine weitere Zeile im Code sparen will. Fuer mich ist die Antwort auf diesen Kompromiss sehr eindeutig. Fuer mehr Infos kannst du auch ueber den Unterschied zwischen den Programmierstilen K&R vs. 1TBS nachlesen.

Die C-for-loops kenne ich glücklicherweise schon (ziemlich ähnlich) von bash: "for ((i=1; i<=5; i++)); do echo $i; done". Wusste aber noch nicht, dass man auch "i<number" schreiben kann. In bash wäre das auch möglich (habe es gerade probiert): "for ((i=1; i<5; i++)); do echo $i; done". Gibt dann eben 1-4 aus. Somit muss man 6 schreiben, wenn man 5 will. Da ich an die erste Variante gewöhnt bin, fand ich das erstmal nicht so leserlich, ist aber im Prinzip ja egal...
Da hast du etwas Entscheidendes an meiner Aussage noch nicht verstanden. Ich habe genau zwei Formen aufgefuehrt. Die erste beginnt bei 0 und vergleicht mit <. Die zweite beginnt bei 1 und vergleicht mit <=. Nur genau diese zwei sollten verwendet werden, keine anderen. Damit eben genau das, was du hier beschreibst, nicht passiert. Die Zahl beim Vergleich muss immer der Anzahl der Schleifendurchlaeufe entsprechen! Darum eben genau diese zwei Formen und keine anderen!

Warum der Start mit 0 besser ist als der mit 1, darueber hat Dijkstra geschrieben: http://www.cs.utexas.edu/users/EWD/tran ... WD831.html
Use ed once in a while!

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: C: fifo lesen

Beitrag von Meillo » 24.08.2018 09:01:33

bluestar hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 23:22:43
RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 23:14:02
bluestar hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 21:21:17
Was mir auch noch aufgefallen ist, dass du zwei Commands hintereinander auch auf einen Schlag einlesen könntest. In dem Falle musst du über deinen Empfangspuffer iterieren und alle Commands nacheinander abarbeiten.
Meinst Du, dass ich das als feature integrieren könnte oder ist das ein bug? Wenn letzteres, wieso kann es dazu kommen? Es ist doch so, dass nach einem write die Schleife einmal durchläuft und beim nächsten write dann wieder. Wie kann read da zwei Zeilen - also zwei writes - auf einmal einlesen?
Ich würde das als Bug bezeichnen, das Problem entsteht halt, wenn du in einem Write "name1\nname2\n" sendest.
Oder wenn dein FIFO-Leseprogramm eine Weile schlaeft und derweil mehrere verschiedene Writes in die FIFO gemacht werden.

Vgl: viewtopic.php?f=34&t=170569&start=15#p1182116
(In der Mitte des Posts meine Reaktion auf den Einwand von schorsch_76.)
Use ed once in a while!

RobertDebiannutzer
Beiträge: 385
Registriert: 16.06.2017 09:52:36

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 24.08.2018 11:12:18

Bezüglich der for-loops: Alles klar. Mit Null anfangen kann auch in bash sinnvoll sein, wenn man nämlich z.B. die loop-Variable auch als index für arrays nutzen will (welche ja bekanntlich bei 0 anfangen). Beispiel: "arr=(eins zwei drei vier fünf) && for ((i=0; i<5; i++)); do echo ${arr[$i]}; done" (Normalerweise macht man ja in bash keinen Loop, um einen array nach stdout zu schreiben, soll nur ein einfaches Beispiel sein.)

Zurück zu C:
Wenn ich es richtig verstehe, hätte ich den bug mit read nicht, wenn ich fgets nutzen würde? Dann sollte ich das vielleicht doch machen und mir mein Beispiel mit read aufheben für Anwendungszwecke, bei denen ich mehrere Zeilen aus der fifo auf einmal lesen möchte.

Dann würde das Programm so aussehen?

Code: Alles auswählen

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <X11/Xlib.h>

/* BEGIN: user configuration */
static const char program_name[] = "myxd";
/* static const char fifo[] = "/tmp/dwmblocks_fifo_intern"; */
static const char fifo[] = "/home/user/fifo";
/* END: user configuration */

static int finish;

static void
term_hdl (int signum)
{
	/* if we do not use signum, gcc complains */
	if (signum == SIGTERM) {
		finish = 1;
	}
}

int
main(void)
{
	static char *display_name = NULL;
	static char name[BUFSIZ];

	static FILE *in;

	static int screen;
	static int state = EXIT_SUCCESS;
	static Window root;

	static Display *dpy;
	static struct sigaction term_act;
	static XEvent ev;

	memset(&term_act, 0, sizeof(term_act));
	term_act.sa_handler = term_hdl;
	if (sigaction(SIGTERM, &term_act, NULL) < 0) {
		perror("sigaction");
		exit(EXIT_FAILURE);
	}

	dpy = XOpenDisplay(display_name);
	if (dpy == NULL) {
		fprintf(stderr, "%s:  unable to open display '%s'\n",
				program_name, XDisplayName(display_name));
		exit(EXIT_FAILURE);
	}
	root = RootWindow(dpy, screen);

	in = fopen(fifo, "r+");
	if (in == NULL) {
		perror("fopen");
		XCloseDisplay(dpy);
		exit(EXIT_FAILURE);
	}

	while (finish == 0) {
			while (XPending(dpy)) {
				XNextEvent(dpy, &ev);
			}
			memset(name, 0, sizeof(name));
			if (fgets(name, sizeof(name), in) == NULL) {
				/* perror("fgets");
				state = EXIT_FAILURE; */
				break;
			} else {
				/* remove trailing newline */
				if ((*name) && (name[strlen(name)-1] == '\n')) {
					name[strlen(name)-1] = '\0';
				}
				XStoreName(dpy, root, name);
			}
	}
	fclose(in);
	XCloseDisplay(dpy);
	exit(state);
}
Rückgabewert von sigaction is int, wird gegen 0 verglichen
"dpy" ist pointer, wird gegen NULL vergleichen
"in" ist pointer, wird gegen NULL vergleichen
"finish" ist int, wird gegen 0 verglichen
Rückgabewert von XPending ist int, wird von while wie boolean behandelt (ich habe das jetzt erstmal gelassen, weil ich mit den Überlegungen zum X Code noch nicht fertig bin)
Rückgabewert von fgets ist im Fehlerfall NULL:
Manpage von fgets hat geschrieben:fgets() returns s on success, and NULL on error or when end of file occurs while no characters have been read.
Leider beklagt sich fgets bei kill über "interrupted system call", wenn ich die perror-Zeile nicht kommentiere.

Und bei fgets kein "sizeof(name)-1", ich habe dran gedacht!

@meillo: Deine Anmerkung zu den geschweiften Klammern finde ich sehr sinnvoll. Ich habe das schon in patches gesehen, wie dann die if-/for-/while-Zeilen mit getauscht werden müssen, weil die Klammer angehängt werden muss. Für den Patch-Leser ist das nicht so angenehm...
Übrigens: Die geschweifte Klammer in meinem letzten if ist gerade das 80. Zeichen in der Zeile. :mrgreen:

Benutzeravatar
bluestar
Beiträge: 2334
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: C: fifo lesen

Beitrag von bluestar » 24.08.2018 11:18:03

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
24.08.2018 11:12:18

Dann würde das Programm so aussehen?

Code: Alles auswählen

...
	while (finish == 0) {
			while (XPending(dpy)) {
				XNextEvent(dpy, &ev);
			}
			memset(name, 0, sizeof(name));
			if (fgets(name, sizeof(name), in) == NULL) {
...
Da fgets ja endlos blockiert (also bis Daten im Fifo anliegen) hast du das Problem mit dem XEvent damit nicht gelöst, sie meinen Post wo ich für das Select ein Timeout von 1 bzw. 5 Sekunden vorschlage:
bluestar hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 16:38:27

Code: Alles auswählen

while (finished==0) {
	while (XPending(dpy)) {
		XNextEvent(dpy, &ev);
	}
	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);
	rc = select (.....) // mit timeout von 1 sek oder 5 sek ... 
	if (rc > 0) {
		...
	}
}

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: C: fifo lesen

Beitrag von Meillo » 24.08.2018 12:03:45

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
24.08.2018 11:12:18

Dann würde das Programm so aussehen?

Code: Alles auswählen

...
	while (finish == 0) {
			while (XPending(dpy)) {
				XNextEvent(dpy, &ev);
			}
			memset(name, 0, sizeof(name));
			if (fgets(name, sizeof(name), in) == NULL) {
...
Das memset() brauchst du nicht, wenn du fgets() verwendest.

bluestar hat geschrieben: ↑ zum Beitrag ↑
24.08.2018 11:18:03
Da fgets ja endlos blockiert (also bis Daten im Fifo anliegen) hast du das Problem mit dem XEvent damit nicht gelöst, sie meinen Post wo ich für das Select ein Timeout von 1 bzw. 5 Sekunden vorschlage:
Von X-Programmierung habe ich, zugegebenermassen, keine Ahnung.

Muss man wirklich alle paar Sekunden diese Events bearbeiten? Ich finde das irgendwie eine krasse Belastung fuer jedes X-Programm.


Man koennte es AFAIR auch noch so loesen, dass man alarm(2) verwendet, um den blockierten fgets() zu unterbrechen.


Ich finde halt, dass man zum Lesen von Textzeilen fgets() verwenden sollte und nur zum Lesen von Binaerdaten (oder Text ohne Zeilen) read()/fread(). Wenn ich nun aber zum Lesen von Textzeilen aus einer FIFO read() nehmen soll, dann kommt mir das komisch vor. Ich denke mir: So kann das ja wohl nicht gewollt sein. Da muss es doch einen besseren Weg geben.
Use ed once in a while!

RobertDebiannutzer
Beiträge: 385
Registriert: 16.06.2017 09:52:36

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 24.08.2018 16:59:27

Ich habe jetzt mal @bluestars Vorschlag probiert. Dabei wollte ich ein ganz Schlauer sein und habe in den X-Loop ein printf eingefügt. Leider kam es dazu aber gar nicht. Fünf Sekunden (= timeout von select) ist das Programm gelaufen, dann auf 100% CPU geschnellt. Eingabe in die fifo habe ich keine gemacht, Ausgaben vom Programm gab es nicht. Wenn ich innerhalb der fünf Sekunden was in die fifo reinschrieb, erschien das zwar als Titel des RootWindows, es hat aber keine Ausgabe ins Terminal gegeben und das Programm ist wieder auf 100% CPU. Habe ich was falsch gemacht?

(Die Aktionen für die drei möglichen Resulate von select habe ich mit ifs definiert und jeweils kommentiert.)
Anmerkung 2: Ich habe in dieser Probe erstmal wieder read() genommen, da sowohl read als auch select einen fd als int brauchen, während fgets einen stream möchte.

Code: Alles auswählen

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <X11/Xlib.h>

/* BEGIN: user configuration */
static const char program_name[] = "myxd";
/* static const char fifo[] = "/tmp/dwmblocks_fifo_intern"; */
static const char fifo[] = "/home/user/fifo";
/* END: user configuration */

static int finish;

static void
term_hdl (int signum)
{
	/* if we do not use signum, gcc complains */
	if (signum == SIGTERM) {
		finish = 1;
	}
}

int
main(void)
{
	static char *display_name = NULL;
	static char name[BUFSIZ];

	static int fd;
	static int rc;
	static int screen;
	static int state = EXIT_SUCCESS;
	static Window root;

	static Display *dpy;
	static struct sigaction term_act;
	static struct timeval tv;
	static XEvent ev;

	fd_set rfds; /* fixed size buffer */

	tv.tv_sec = 5;
	tv.tv_usec = 0;

	memset(&term_act, 0, sizeof(term_act));
	term_act.sa_handler = term_hdl;
	if (sigaction(SIGTERM, &term_act, NULL) < 0) {
		perror("sigaction");
		exit(EXIT_FAILURE);
	}

	dpy = XOpenDisplay(display_name);
	if (dpy == NULL) {
		fprintf(stderr, "%s:  unable to open display '%s'\n",
				program_name, XDisplayName(display_name));
		exit(EXIT_FAILURE);
	}
	root = RootWindow(dpy, screen);

	fd = open(fifo, O_RDWR | O_NONBLOCK);
	if (fd == -1) {
		perror("open");
		XCloseDisplay(dpy);
		exit(EXIT_FAILURE);
	}

	while (finish == 0) {
		while (XPending(dpy)) {
			XNextEvent(dpy, &ev);
			printf("Hello.\n");
		}
		FD_ZERO(&rfds);
		FD_SET(fd, &rfds);
		rc = select(fd + 1, &rfds, NULL, NULL, &tv);
		if (rc > 0) { /* success */
			memset(name, 0, sizeof(name));
			ssize_t got = read(fd, name, sizeof(name)-sizeof(char));
			if (got < 0) {
				perror("read");
				state = EXIT_FAILURE;
				break;
			} else if (got == 0) {
				printf("EOF\\n");
				break;
			} else {
				/* remove trailing newline */
				if ((*name) && (name[strlen(name)-1] == '\n')) {
					name[strlen(name)-1] = '\0';
				}
				XStoreName(dpy, root, name);
			}
		} else if (rc == 0) { /* timeout expired */
			continue;
		} else { /* error */
			perror("select");
			state = EXIT_FAILURE;
			break;
		}
	}
	close(fd);
	XCloseDisplay(dpy);
	exit(state);
}

Benutzeravatar
bluestar
Beiträge: 2334
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: C: fifo lesen

Beitrag von bluestar » 24.08.2018 17:35:49

Das Verhalten ist vollkommen normal, du musst die Timeout-Struktur vor jedem select()—Aufruf neu initialisieren

RobertDebiannutzer
Beiträge: 385
Registriert: 16.06.2017 09:52:36

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 24.08.2018 17:44:56

Ach so! Ja, jetzt funktioniert es. Aber ich bekomme keine Ausgabe! Ich habe im "while (XPending(dpy))"-block nun zwei printfs, eines vor XNextEvent, eines danach, aber auch nach mehreren writes bekomme ich keine Ausgabe (obwohl der Titel des RootWindows immer richtig gesetzt wird).
Ich habe es mehrmals mit "for i in {1..5}; do echo hallo$i > fifo && sleep 1; done" probiert...

Ich werde jetzt mein Programm mit printfs pflastern. :twisted: Irgendwie muss doch der Flow zu verstehen sein.

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: C: fifo lesen

Beitrag von eggy » 24.08.2018 19:10:23

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
24.08.2018 17:44:56
Ich werde jetzt mein Programm mit printfs pflastern. :twisted: Irgendwie muss doch der Flow zu verstehen sein.
Ich ja auch nen großer Freund von printf-Debugging, aber hast schon mal was von gdb gehört?

RobertDebiannutzer
Beiträge: 385
Registriert: 16.06.2017 09:52:36

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 24.08.2018 20:55:13

Hmmja, aber ich weiß nicht, was ich damit machen soll. Ich habe ja keinen Fehler in dem Sinn.

Das Bizarre ist, dass mittels printf-Debugging klar wird, dass der Inhalt des XPending-Loops nicht ausgeführt wird. Aber wenn ich den Loop auskommentiere, funktioniert das Setzen des Titels des RootWindows nicht mehr...

printf-Debugging:

Code: Alles auswählen

Before XPending.
After XPending.
Before select.
After select.
Before continue.
Before XPending.
After XPending.
Before select.
After select.
Before continue.
Before XPending.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
After XPending.
Before select.
After select.
Before continue.
Before XPending.
After XPending.
Before select.
After select.
Before continue.
Before XPending.
After XPending.
Before select.
After select.
Before error.
select: Interrupted system call
After error.
Before end.
After end.
Erst drei Leerläufe, dann fünf writes hintereinander, dann nichts mehr und dann shutdown.

Der printf-Debugging-Code:

Code: Alles auswählen

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <X11/Xlib.h>

/* BEGIN: user configuration */
static const char program_name[] = "myxd";
/* static const char fifo[] = "/tmp/dwmblocks_fifo_intern"; */
static const char fifo[] = "/home/user/fifo";
/* END: user configuration */

static int finish;

static void
term_hdl (int signum)
{
	/* if we do not use signum, gcc complains */
	if (signum == SIGTERM) {
		finish = 1;
	}
}

int
main(void)
{
	static char *display_name = NULL;
	static char name[BUFSIZ];

	static int fd;
	static int rc;
	static int screen;
	static int state = EXIT_SUCCESS;
	static Window root;

	static Display *dpy;
	static struct sigaction term_act;
	static struct timeval tv;
	static XEvent ev;

	fd_set rfds; /* fixed size buffer */

	memset(&term_act, 0, sizeof(term_act));
	term_act.sa_handler = term_hdl;
	if (sigaction(SIGTERM, &term_act, NULL) < 0) {
		perror("sigaction");
		exit(EXIT_FAILURE);
	}

	dpy = XOpenDisplay(display_name);
	if (dpy == NULL) {
		fprintf(stderr, "%s:  unable to open display '%s'\n",
				program_name, XDisplayName(display_name));
		exit(EXIT_FAILURE);
	}
	root = RootWindow(dpy, screen);

	fd = open(fifo, O_RDWR | O_NONBLOCK);
	if (fd == -1) {
		perror("open");
		XCloseDisplay(dpy);
		exit(EXIT_FAILURE);
	}

	while (finish == 0) {
		printf("Before XPending.\n");
		while (XPending(dpy)) {
			printf("Before XNextEvent.\n");
			XNextEvent(dpy, &ev);
			printf("After XNextEvent.\n");
		}
		printf("After XPending.\n");
		FD_ZERO(&rfds);
		FD_SET(fd, &rfds);
		tv.tv_sec = 5;
		tv.tv_usec = 0;
		printf("Before select.\n");
		rc = select(fd + 1, &rfds, NULL, NULL, &tv);
		printf("After select.\n");
		if (rc > 0) { /* success */
			memset(name, 0, sizeof(name));
			printf("Before read.\n");
			ssize_t got = read(fd, name, sizeof(name)-sizeof(char));
			printf("After read.\n");
			if (got < 0) {
				perror("read");
				state = EXIT_FAILURE;
				break;
			} else if (got == 0) {
				printf("EOF\\n");
				break;
			} else {
				/* remove trailing newline */
				if ((*name) && (name[strlen(name)-1] == '\n')) {
					name[strlen(name)-1] = '\0';
				}
				printf("Before XStoreName.\n");
				XStoreName(dpy, root, name);
				printf("After XStoreName.\n");
			}
		} else if (rc == 0) { /* timeout expired */
			printf("Before continue.\n");
			continue;
			printf("After continue.\n");
		} else { /* error */
			printf("Before error.\n");
			perror("select");
			state = EXIT_FAILURE;
			printf("After error.\n");
			break;
		}
	}
	printf("Before end.\n");
	close(fd);
	XCloseDisplay(dpy);
	printf("After end.\n");
	exit(state);
}

Benutzeravatar
bluestar
Beiträge: 2334
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: C: fifo lesen

Beitrag von bluestar » 25.08.2018 02:51:54

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
24.08.2018 20:55:13
Das Bizarre ist, dass mittels printf-Debugging klar wird, dass der Inhalt des XPending-Loops nicht ausgeführt wird. Aber wenn ich den Loop auskommentiere, funktioniert das Setzen des Titels des RootWindows nicht mehr..
Lies mal dort: https://www.x.org/archive/X11R7.5/doc/m ... ync.3.html
The XFlush function flushes the output buffer. Most client applications need not use this function because the output buffer is automatically flushed as needed by calls to XPending,

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: C: fifo lesen

Beitrag von Meillo » 25.08.2018 07:39:51

Das hier finde ich ein schoenes Beispiel fuer bessere Verstaendlichkeit mit `!' statt explizitem Vergleich:
RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
24.08.2018 20:55:13

Code: Alles auswählen

[...]
static int finish;

static void
term_hdl (int signum)
{
	/* if we do not use signum, gcc complains */
	if (signum == SIGTERM) {
		finish = 1;
	}
}

int
main(void)
{
[...]
	while (finish == 0) {
[...]
Wenn ich ``while finish gleich null'' lese, dann muss ich in meinem Kopf immer erst um eine Ecke denken, waehrend ``while not finish(ed)'' direkt 1:1 ausdrueckt was gemeint ist.
Use ed once in a while!

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: C: fifo lesen

Beitrag von eggy » 25.08.2018 09:09:45

Oder Variabkenname/Werte umdrehen und "while (unfinished)".

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: C: fifo lesen

Beitrag von Meillo » 25.08.2018 09:56:48

eggy hat geschrieben: ↑ zum Beitrag ↑
25.08.2018 09:09:45
Oder Variabkenname/Werte umdrehen und "while (unfinished)".
Damit muss man vorsichtig sein. Negationen sind immer schwerer zu verstehen als die poitiven Gegenstuecke. Wenn man schon mit negierten Variablennamen anfaengt, dann ist die Wahrscheinlichkeit fuer schwer verstehbare boolsche Ausdruecke hoeher. Im konkreten Fall macht es vielleicht keinen Unterschied, im vielen anderen Faellen schon.
Use ed once in a while!

RobertDebiannutzer
Beiträge: 385
Registriert: 16.06.2017 09:52:36

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 25.08.2018 13:53:51

while (please_set_this_integer_to_one_if_you_like_to_break_the_while_loop) 8)

@bluestar: Ah ok. Ich habe es mal ohne loop versucht, nur mit einem Aufruf von XPending. Funktioniert. Aber was mich verwirrt:
The XPending function returns the number of events that have been received from the X server but have not been removed from the event queue.
Quelle: https://www.x.org/archive/X11R7.5/doc/m ... ync.3.html

Dieser int ist in meinem Programm immer 0*. Wenn es aber nur um die events ginge, die von meinem Programm ausgehen, könnte ich ja das XPending auch an den Schluss des loops stellen (nach XStoreName)?

Aber wie ist das jetzt mit den aufstauenden Events? Ich blicke bei diesem X immer noch nicht durch... :?

*Mein printf-Debugging (Code ist unten):

Code: Alles auswählen

Before XPending.
value of tmp_int is 0.
After XPending.
Before select.
After select.
Before continue.
Before XPending.
value of tmp_int is 0.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
value of tmp_int is 0.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
value of tmp_int is 0.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
value of tmp_int is 0.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
value of tmp_int is 0.
After XPending.
Before select.
After select.
Before read.
After read.
Before XStoreName.
After XStoreName.
Before XPending.
value of tmp_int is 0.
After XPending.
Before select.
After select.
Before continue.
Before XPending.
value of tmp_int is 0.
After XPending.
Before select.
signal handler
After select.
Before error.
select: Interrupted system call
After error.
Before end.
After end.

Code: Alles auswählen

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <X11/Xlib.h>

/* BEGIN: user configuration */
static const char program_name[] = "myxd";
/* static const char fifo[] = "/tmp/dwmblocks_fifo_intern"; */
static const char fifo[] = "/home/user/fifo";
/* END: user configuration */

static int finish;

static void
term_hdl (int signum)
{
	/* if we do not use signum, gcc complains */
	if (signum == SIGTERM) {
		finish = 1;
		printf("signal handler\n");
	}
}

int
main(void)
{
	static char *display_name = NULL;
	static char name[BUFSIZ];

	static int fd;
	static int rc;
	static int screen;
	static int state = EXIT_SUCCESS;
	static Window root;

	static Display *dpy;
	static struct sigaction term_act;
	static struct timeval tv;
	/* static XEvent ev; */

	fd_set rfds; /* fixed size buffer */

	memset(&term_act, 0, sizeof(term_act));
	term_act.sa_handler = term_hdl;
	if (sigaction(SIGTERM, &term_act, NULL) < 0) {
		perror("sigaction");
		exit(EXIT_FAILURE);
	}

	dpy = XOpenDisplay(display_name);
	if (dpy == NULL) {
		fprintf(stderr, "%s:  unable to open display '%s'\n",
				program_name, XDisplayName(display_name));
		exit(EXIT_FAILURE);
	}
	root = RootWindow(dpy, screen);

	fd = open(fifo, O_RDWR | O_NONBLOCK);
	if (fd == -1) {
		perror("open");
		XCloseDisplay(dpy);
		exit(EXIT_FAILURE);
	}

	while (finish == 0) {
		printf("Before XPending.\n");
		int tmp_int = XPending(dpy);
		printf("value of tmp_int is %d.\n", tmp_int);
		printf("After XPending.\n");
		FD_ZERO(&rfds);
		FD_SET(fd, &rfds);
		tv.tv_sec = 5;
		tv.tv_usec = 0;
		printf("Before select.\n");
		rc = select(fd + 1, &rfds, NULL, NULL, &tv);
		printf("After select.\n");
		if (rc > 0) { /* success */
			memset(name, 0, sizeof(name));
			printf("Before read.\n");
			ssize_t got = read(fd, name, sizeof(name)-sizeof(char));
			printf("After read.\n");
			if (got < 0) {
				perror("read");
				state = EXIT_FAILURE;
				break;
			} else if (got == 0) {
				printf("EOF\\n");
				break;
			} else {
				/* remove trailing newline */
				if ((*name) && (name[strlen(name)-1] == '\n')) {
					name[strlen(name)-1] = '\0';
				}
				printf("Before XStoreName.\n");
				XStoreName(dpy, root, name);
				printf("After XStoreName.\n");
			}
		} else if (rc == 0) { /* timeout expired */
			printf("Before continue.\n");
			continue;
			printf("After continue.\n");
		} else { /* error */
			printf("Before error.\n");
			perror("select");
			state = EXIT_FAILURE;
			printf("After error.\n");
			break;
		}
	}
	printf("Before end.\n");
	close(fd);
	XCloseDisplay(dpy);
	printf("After end.\n");
	exit(state);
}

Benutzeravatar
bluestar
Beiträge: 2334
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: C: fifo lesen

Beitrag von bluestar » 25.08.2018 13:57:06

Starte mal dein Programm, setze den Fokus auf das Fenster und drück mal ne Taste, dann sollte ein Event bei dir ankommen, natürlich in der Version mit dem while(...) loop.

RobertDebiannutzer
Beiträge: 385
Registriert: 16.06.2017 09:52:36

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 25.08.2018 14:21:02

loop:

Code: Alles auswählen

		printf("Before XPending.\n");
		while (XPending(dpy)) {
			printf("Before XNextEvent.\n");
			XNextEvent(dpy, &ev);
			printf("After XNextEvent.\n");
		}
		printf("After XPending.\n");
Im Terminal ausgeführt und Tasten gedrückt (immer in den 5sec-Pausen zwischen "Before select" und "After select"):

Code: Alles auswählen

Before XPending.
After XPending.
Before select.
jifwjfiAfter select.
Before continue.
Before XPending.
After XPending.
Before select.
jiosjjioAfter select.
Before continue.
Before XPending.
After XPending.
Before select.
jiofjdsioAfter select.
Before continue.
Before XPending.
After XPending.
Before select.
jsdjoAfter select.
Before continue.
Before XPending.
After XPending.
Before select.
^C
?

RobertDebiannutzer
Beiträge: 385
Registriert: 16.06.2017 09:52:36

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 25.08.2018 17:06:35

@bluestar:
Mir ist gerade aufgefallen, dass es vielleicht ein Missverständnis gab. Du schreibst:
bluestar hat geschrieben: ↑ zum Beitrag ↑
25.08.2018 13:57:06
Starte mal dein Programm, setze den Fokus auf das Fenster [...]
Ich dachte, Du meinst mit dem Fenster das Fenster des Terminal Emulators (also in meinem Fall urxvt). Aber meintest Du vielleicht, dass mein Programm ein eigenes Fenster startet? Dem ist nicht so, denn es dient ja nur als "daemon" im Hintergrund zum Setzen des Titels des RootWindows, wobei es den entsprechenden string immer über die fifo erhält. Hintergrund ist, dass ich mittlerweile dwm einsetze (https://dwm.suckless.org). Die statusbar von dwm zeigt immer den Titel des RootWindows an:
dwm reads from the root window’s name to print arbitrary status text (like the date, load, battery charge). That’s much simpler than larsremote, wmiir and what not…
Quelle: obiger Link
Ich wollte nur nochmal darauf hinweisen, damit Du und andere Leser nochmal genau wissen, worum es geht. (@meillo kennt dwm ja eh).
Mein Programm ist quasi wie der Befehl "xsetroot -name", nur als daemon.

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: C: fifo lesen

Beitrag von Meillo » 27.08.2018 09:51:15

Update:
RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
22.08.2018 15:46:39
Übrigens, die Videos in dem Link http://ulm.ccc.de/ChaosSeminar/2010/03_UnixPhil gibt's nicht mehr...
Der Ulmer CCC hat in letzter Zeit an seinem Server rumgeschraubt, dabei ist die Videoeinbindung kaputt gegangen. Jetzt sind die Videos aber wieder da.

Und falls es mal wieder nicht mehr gehen sollte, hier finden sie sich auch: http://ftp.ccc.de/regional/ulm/chaosseminar/
Use ed once in a while!

Antworten