C: fifo lesen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Benutzeravatar
schorsch_76
Beiträge: 2597
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: 9225
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: 9225
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: 2418
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: 9225
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: 2418
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: 3334
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: 2418
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: 9225
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: 3334
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: 9225
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: 2418
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: 9225
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!

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

Re: C: fifo lesen

Beitrag von bluestar » 28.08.2018 15:06:23

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
25.08.2018 14:21:02
Im Terminal ausgeführt und Tasten gedrückt (immer in den 5sec-Pausen zwischen "Before select" und "After select"):
Das ist ja auch absolut korrekt, da dein Select Timeout 5 Sekunden beträgt.
RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
25.08.2018 17:06:35
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
[...]
Dann solltest du auf jeden Fall mach recherchieren, ob bei deinem Programm überhaupt XEvents ankommen können, da bin ich überfragt... Falls nicht schenke dir den XPending() loop und nutze einfach XFlush(...) nach StoreName(...). In dem Falle kannst du auch von den Low-Level Funktionen "select(...)" und "read(...)" auf fopen und fgets umsteigen.

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 29.08.2018 08:59:26

Also nach meinen Experimenten erhält mein Programm keine Events von X. Wäre ja auch seltsam...
Mit XFlush funktioniert es jedenfalls bestens.
Ich habe es sogar noch geschafft, die "interrupted system call"-Meldung zu unterdrücken, wenn das Programm mit (p)kill (also SIGTERM) geschlossen wird.
Ich habe herausgefunden, dass für die Fehlermeldung der Fehler "EINTR" verantwortlich ist.
Aus "man errno":
EINTR Interrupted function call (POSIX.1); see signal(7).
Aus "man 7 signal":
If a blocked call to one of the following interfaces is interrupted by a signal handler, then the call will be automatically restarted after the signal handler returns if the SA_RESTART flag was used; otherwise the call will
fail with the error EINTR:
Nun lasse ich perror() einfach nur im Fall ausführen, dass errno nicht EINTR ist. Aber soll ich dann im Fall, dass errno gleich EINTR ist, den exit-state auf EXIT_SUCCESS setzen und nur im Fall errno != EINTR den exit-state auf EXIT_FAILURE setzen? Wie sind denn da die Konventionen?

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>
#include <errno.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;

	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) {
		if (fgets(name, sizeof(name), in) == NULL) {
			if (errno != EINTR) {
				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);
			XFlush(dpy);
		}
	}
	fclose(in);
	XCloseDisplay(dpy);
	exit(state);
}

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

Re: C: fifo lesen

Beitrag von Meillo » 29.08.2018 09:28:51

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
29.08.2018 08:59:26

Code: Alles auswählen

	while (finish == 0) {
		if (fgets(name, sizeof(name), in) == NULL) {
			if (errno != EINTR) {
				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);
			XFlush(dpy);
		}
	}
}
Ich finde, dass weniger Einrueckungen oft mit besserem Code korrelieren.

Ein gutes Schema finde ich, zuerst alle Fehlerfaelle einzeln auszusortieren und anschliessend den eigentlichen Code auszufuehren.

Jedenfalls sollte man es vermeiden, den eigentlichen Code in lauter Positiv-Bedingungen tief einzuschachteln, weil das beim Lesen sehr viel mehr ``Stack-Space'' braucht.

Darum: Je weniger tief geschachtelt, desto weniger Stack-Space im Gehirn noetig (im Klartext: Man muss sich weniger State merken), desto einfacher verstaendlicher Code, desto weniger Fehler ... und das alles laesst sich einfach ueber die Einrueckungstiefen des Codes ablesen.

(Falls man uebrigens mit breaks und continues nicht recht weiter kommt und meint, gotos zu brauchen, dann ist das ein Zeichen dafuer, dass man Codeteile in eigene Funktionen outfactoren sollte, weil man dann mit returns arbeiten kann.)


Somit also mein Vorschlag (bezogen auf diesen einen Aspekt) statt obigem Code:

Code: Alles auswählen

	while (finish == 0) {
		if (fgets(name, sizeof(name), in) == NULL) {
			if (errno != EINTR) {
				perror("fgets");
			}
			state = EXIT_FAILURE;
			break;
		}
		/* remove trailing newline */
		if ((*name) && (name[strlen(name)-1] == '\n')) {
			name[strlen(name)-1] = '\0';
		}
		XStoreName(dpy, root, name);
		XFlush(dpy);
	}
}
Use ed once in a while!

inne
Beiträge: 3289
Registriert: 29.06.2013 17:32:10
Lizenz eigener Beiträge: GNU General Public License
Kontaktdaten:

Re: C: fifo lesen

Beitrag von inne » 29.08.2018 09:50:17

Meillo hat geschrieben: ↑ zum Beitrag ↑
29.08.2018 09:28:51

Ich finde, dass weniger Einrueckungen oft mit besserem Code korrelieren.
OT: Ist das mit "Soweit srechtlinks wie möglich" Programmieren gemeint? Das las ich mal in einer Vorgabe für C-Programmierer..

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

Re: C: fifo lesen

Beitrag von Meillo » 29.08.2018 14:42:44

inne hat geschrieben: ↑ zum Beitrag ↑
29.08.2018 09:50:17
Meillo hat geschrieben: ↑ zum Beitrag ↑
29.08.2018 09:28:51

Ich finde, dass weniger Einrueckungen oft mit besserem Code korrelieren.
OT: Ist das mit "Soweit srechtlinks wie möglich" Programmieren gemeint? Das las ich mal in einer Vorgabe für C-Programmierer..
Vermutlich ist das damit gemeint.

Man darf diese Stilregeln nicht zu starr auslegen, sie helfen einem aber, zu erkennen, welche Stellen im Code man sich mal genauer anschauen sollte, weil dort vermutlich Refactoringpotenzial vorhanden ist. Darueber hinaus sind das eigentlich Wertvolle die Gedankenwege dahinter. Darum versuche ich diese bei meinem Kommentaren hier auch immer darzulegen. Zu verstehen und bewusst begruendet zu entscheiden ist das Wichtigste!
Use ed once in a while!

Antworten