C: fifo lesen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
RobertDebiannutzer
Beiträge: 385
Registriert: 16.06.2017 09:52:36

C: fifo lesen

Beitrag von RobertDebiannutzer » 19.08.2018 16:31:59

Ich möchte eine fifo lesen und eventuellen Text als Name des Root Windows des Xservers setzen. Das ganze soll als while-loop laufen. Der X-related Code ist i.O., habe ich mir vom Code von xsetroot abgeschaut.
Bisher habe ich geschafft: Entweder wird
1. nur eine Zeile gelesen, nach X ausgegeben und dann beendet sich das Programm.
2. gar nichts nach X ausgegeben, aber 100% CPU.

Hier mal der Code. Auskommentierten Testcode habe ich absichtlich nicht entfernt. Aktueller Stand: 1. Variante. Das Programm soll sich aber nicht beenden, sondern weiterlaufen...
Kompiliert wurde mit: "gcc -pedantic -Wall -Werror -Wextra -lX11 -o myxd myxd.c"

Über Hilfe würde ich mich sehr freuen! Ich habe mir echt schon viel Mühe gegeben, kriege es aber einfach nicht hin... :(

Code: Alles auswählen

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

static const char program_name[] = "myxd";
static Display *dpy;
static int screen;
static Window root;

int
main(void) 
{
	char *display_name = NULL;
	char name[128];
	/* int ch; */
	/* FILE *in; */
	int in;

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

	in = open("/home/user/fifo", O_RDONLY);
	/* in = fopen("/home/user/fifo", "r"); */
	/* in = fdopen(fd, "r"); */
	/* 
	 * if (in == NULL) {
	 * perror("fopen");
	 * exit(EXIT_FAILURE);
	 * }
	 */

	/* (ch = getchar()) != '\n' && ch != EOF */
	while (read(in, name, 128) > 0) {
		/* get input */
		/* fgets(name, 128, in); */
		/* fgets(name, 128, stdin); */
		/* read(0, name, sizeof(name)); */
		/* Handle set name */
		if (name[0] != '\0' && name[0] != '\n') {
			/* 
			 * remove trailing newline - so use echo or printf '%s\n'
			 * for input
			 */
			name[strlen(name)-1] = 0;
			XStoreName(dpy, root, name);
		}
	}
	XCloseDisplay(dpy);
	/* fclose(in); */
	close(in);

	exit(EXIT_SUCCESS);
}

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

Re: C: fifo lesen

Beitrag von Meillo » 19.08.2018 18:49:33

Ich habe nur kurz drueber geschaut, will aber doch ein paar Hinweise geben.

Aus der FIFO kommt doch Text, dann lese viel leichter mit stdio (also fgets(3)) als mit Systemcalls (also read(2))!

Soweit ich das sehe, hast du eine Busy-Loop. Darum bekommst du auch 100% CPU-Last. Baue unbedingt einen Sleep ein! Es ist voellig unnoetig tausende Mails pro Sekunden zu schauen, ob was zu lesen da ist. ;-) Einmal oder zehnmal pro Sekunde reicht auch.

Was gibt denn eine FIFO zurueck, wenn gerade nichts zu lesen da ist? EOF? (Weiss das gerade nicht auswendig und bin zu faul nachzuschauen. :roll: ) Dein read() liefert bei EOF 0 zurueck, was die Schleife beendet, darum liest dein Programm nur einmal! Die selbe Situation hast du bei fgets(3) auch, bloss ist 0 da halt die Konstante ``EOF''. Entscheidend ist doch, dass dein Programm weiter laeuft, auch wenn gerade nichts gelesen werden kann. Es soll sich nur dann beenden, wenn beim Lesen ein Fehler (wie Datei nicht gefunden oder so) auftaucht. Du wirst dein Progamm also normalerweise manuell killen muessen, wenn du es beenden willst! (Oder du bringst ihm bei, dass es sich selber beenden soll, wenn du ``Hasta la vista!'' in die FIFO schreibst. ;-) )

Und dann noch eine Kleinigkeit: Dein Entfernen des Newlines am Ende des gelesenen Strings ist ein Good-Faith-Code ... du glaubst einfach, dass das letzte Zeichen schon ein Newline sein wird. Pruefe besser explizit darauf ... dann kannst du dir auch den Kommentar sparen. ;-)

Wenn du den Code grundsaetzlich am Laufen hast, dann poste ihn doch nochmal, dann kann ich auch noch mehr zum Stil sagen.

Ach und noch was ;-) : Die Puffergroesse 128 Bytes macht wenig Sinn. Verwende einfach immer die Konstante BUFSIZ (in stdio.h definiert, IIRC). Das sind typischerweise sowas wie 4096 Bytes. Wenn du in der Groesseneinheit Daten schaufelst, dann geht das meist schneller als in anderen Groesseneinheiten, weil das die natuerliche Einheit deines Systems ist.


Ich hoffe, du bekommst dein Programm zum Laufen. Der relevante Hinweis ist die Pruefung auf den Rueckgabewert der Lesefunktion im Schleifenkopf!
Use ed(1) once in a while!

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 19.08.2018 22:57:19

Vielen Dank für Deine Anregungen! :THX:
Ich konnte das Problem durch Probieren mit einem zweiten, äußeren while-loop (while (1)) nochmal dahingehend eingrenzen:
Wenn ich einen äußeren while-loop um die ganze Funktion setze (außer die Variablen und das exit), funktioniert alles wie gewünscht.
Wenn ich einen äußeren while-loop um den existierenden setze und noch open und close der fifo mitnehme, kommt es nicht zu 100% CPU, aber X kriegt nichts ab.
Wenn ich einen äußeren while-loop nur um den existierenden setze, kommt es zu 100% CPU, aber erst nach dem ersten Schreiben (also von einem anderen Terminal "echo hallo > fifo")! Auch hier kriegt X nichts ab.
Aber es kann doch nicht sein, dass ich die Verbindung zu X jedes Mal öffnen und schließen muss!? Irgendwie muss es doch mit der fifo zusammenhängen...
Ich probiere weiter. Eigentlich ist meine Idee so einfach...

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

Re: C: fifo lesen

Beitrag von Meillo » 20.08.2018 07:13:30

Du suchst doch dieses:

Code: Alles auswählen

while sleep 1; do
	xsetroot -name "`cat /path/to/fifo`"
done
... bloss in einem eigenen C-Programm, weil ... weil du es selber machen willst oder weil du denkst, dass es mit Shellmitteln nicht geht?

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
19.08.2018 22:57:19
Ich konnte das Problem durch Probieren mit einem zweiten, äußeren while-loop (while (1)) nochmal dahingehend eingrenzen:
Wenn ich einen äußeren while-loop um die ganze Funktion setze (außer die Variablen und das exit), funktioniert alles wie gewünscht.
Wenn ich einen äußeren while-loop um den existierenden setze und noch open und close der fifo mitnehme, kommt es nicht zu 100% CPU, aber X kriegt nichts ab.
100% CPU-Auslastung bekommst du nur dann wenn du eine Busy-Loop hast. Die kannst du ganz einfach mit einem kleinen Sleep verhindern. (Auf diese Anregung von mir hast du nicht reagiert.) Ansonsten stellt sich die Frage, warum die Loop busy ist, weil, wenn ich von einer FIFO lesen will in der nichts drin ist, dann blockiert mein cat und wartet bis etwas Lesbares kommt. Ich hab nicht selber rumprobiert was fgets(3) in dem Fall macht ... aber das ist letztlich ein Standardproblem. Vielleicht suchst du mal nach Tutorials wie man richtig aus einer FIFO liest, damit wir da nichts mehr falsch machen.
Wenn ich einen äußeren while-loop nur um den existierenden setze, kommt es zu 100% CPU, aber erst nach dem ersten Schreiben (also von einem anderen Terminal "echo hallo > fifo")! Auch hier kriegt X nichts ab.
Aber es kann doch nicht sein, dass ich die Verbindung zu X jedes Mal öffnen und schließen muss!? Irgendwie muss es doch mit der fifo zusammenhängen...
Wenn ich die Zeit finde, dann gehe ich das Problem gerne auch noch praktisch an und ueberlege nicht nur theoretisch. Dazu waere es aber hilfreich, wenn du noch genau formulieren wuerdest was du unter ``richtigem Verhalten'' verstehst. Was genau soll das Programm also wann machen? Was beispielsweise wenn noch keine Daten in der FIFO sind?

Ich probiere weiter. Eigentlich ist meine Idee so einfach...
Die Erfahrung sagt, dass man sich meistens nur selber im Weg steht. ;-)
Use ed(1) once in a while!

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 20.08.2018 08:20:34

Meillo hat geschrieben: ↑ zum Beitrag ↑
20.08.2018 07:13:30
Du suchst doch dieses:
Nö, es soll auf dieses hinauslaufen (nur ein Ausschnitt):

Code: Alles auswählen

while true; do
	time_cmd
	while true; do
		wifi_cmd
		battery_cmd
		xsetroot "$time | $wifi | $volume | $ethernet | $backlight | $monitor | $battery |"
		while $(timeout 10 cat /tmp/dwmblocks); do
			xsetroot "$time | $wifi | $volume | $ethernet | $backlight | $monitor | $battery |"
		done
		n=x$n
		if [ $n = "xxxxxx" ]; then
			unset n
			continue 2
		fi
	done
done
Wobei mein Programm xsetroot ersetzen soll, möglichst den ganzen 3. (also innersten) loop soll (+ vielleicht den ersten Aufruf von xsetroot, aber eins nach dem anderen).
EDIT: Also es soll nacher mindestens mal so aussehen:

Code: Alles auswählen

while true; do
	time_cmd
	while true; do
		wifi_cmd
		battery_cmd
		echo "$time | $wifi | $volume | $ethernet | $backlight | $monitor | $battery |" > fifo
		while $(timeout 10 cat /tmp/dwmblocks); do
			echo "$time | $wifi | $volume | $ethernet | $backlight | $monitor | $battery |" > fifo
		done
		n=x$n
		if [ $n = "xxxxxx" ]; then
			unset n
			continue 2
		fi
	done
done
Aus der fifo - hier /tmp/dwmblocks (in Anlehnung an i3blocks... :mrgreen: ) - kommen Befehle für die Funktionen:

Code: Alles auswählen

static const char *volumeup[] = { "/bin/sh", "-c", "echo 'volume_cmd 4' > /tmp/dwmblocks", NULL };
static const char *volumedown[] = { "/bin/sh", "-c", "echo 'volume_cmd 5' > /tmp/dwmblocks", NULL };
static const char *volumemute[] = { "/bin/sh", "-c", "echo 'volume_cmd 3' > /tmp/dwmblocks", NULL };
static const char *backlightup[] = { "/bin/sh", "-c", "echo 'backlight_cmd 4' > /tmp/dwmblocks", NULL };
static const char *backlightdown[] = { "/bin/sh", "-c", "echo 'backlight_cmd 5' > /tmp/dwmblocks", NULL };
static const char *updatebar[] = { "/bin/bash", "-c", "kill $(< /tmp/dwmblocks_pid)", NULL };
static const char *calcmd[] = { "/bin/sh", "-c", "echo 'time_cmd 3' > /tmp/dwmblocks", NULL };
Die Zahlen standen für Mouse-Events, als die Funktionen noch Scripte für i3blocks waren. Ich war einfach zu faul, das zu ändern... Letztendlich ist es ja so auch weiterhin gut. Irgendwelche Argumente müssen die Funktionen ja kriegen...

Ich werde jetzt noch die Fifo mehr studieren, wie von Dir vorgeschlagen. Einen sleep wollte ich nicht einbauen, denn cat hat den ja auch nicht. Irgendwie muss es ohne gehen. Und es geht ja auch bis zum ersten write ohne. Nur ab dem ersten write spielt mein Programm verrückt... (Ich habe einen sleep getestet, doch an dem Verhalten meines letzten Beitrags hat sich nichts geändert, außer dass CPU nicht mehr auf 100 geht. Insofern muss noch was anderes dahinterstecken.)

Ach ja: Noch zum Ziel: Im Moment rufe ich aus meinem Script 3 externe Programme auf, davon eines ggf. mehrmals. Wobei xsetroot jetzt nicht rasend schnell ist... Da ist ein einzelnes kleines Programm extra für diesen Zweck doch wesentlich eleganter. Und es wäre ja eigentlich auch ganz einfach. Ich muss das nur noch mit der fifo hinkriegen.

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

Re: C: fifo lesen

Beitrag von bluestar » 20.08.2018 12:55:27

Wenn du kein sleep verwenden möchtest, dann nutze select(...) und warte damit vor dem Lesen aus dem Fifo, bis tatsächlich Daten im Fifo anstehen.

Beispiel: https://outflux.net/blog/archives/2008/ ... on-a-fifo/

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 20.08.2018 13:52:18

Vielen Dank für die Idee! Ich habe das Nutzen von select() schon mittels Google gefunden, aber die Beispiele, die ich fand, funktionierten irgendwie nicht. Ich werde es mal mit dem Beispiel in Deinem Link probieren und das Ergebnis mit Code posten.

BTW:
Mit Perl in 5sec und einmal ganz kurz googeln funktioniert endloses Lesen von Fifo ohne 100% CPU (der Trick ist das "+" vor dem "<"):

Code: Alles auswählen

#!/usr/bin/perl
use strict;
use warnings;

open(FH, "+<", "/home/user/fifo");
while (<FH>) {
	print "$_\n";
}
close(FH);
Warum fällt mir Perl immer leicht und C ist so ein Kampf? Naja, ich kriege es noch hin!

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 20.08.2018 17:13:07

Heureka!

Code: Alles auswählen

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

int
main(void) 
{
	char name[BUFSIZ];
	int fd;

	fd = open("/home/user/fifo", O_RDWR | O_NONBLOCK);

	fd_set rfds;
	int rc;

	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);

	while (1) {
		rc = select(fd + 1, &rfds, NULL, NULL, NULL);
		if (rc > 0) {
			ssize_t got = read(fd, name, BUFSIZ);
			if (got < 0) {
				perror("read");
				return 1;
			} else if (got == 0) {
				printf("EOF\\n");
				return 1;
			}
			else {
				printf(name);
			}
		}
	}
	close(fd);

	exit(EXIT_SUCCESS);
}
Mehr später.

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 20.08.2018 17:46:35

Jetzt habe ich sogar noch den passenden X-Code ausgetüftelt. Da hat nach XStoreName noch was gefehlt. So ganz kapiere ich noch nicht, warum es nun funktioniert, aber es funktioniert immerhin mal. Nunja ... das mit dem newline-Ersetzen ergibt irgendwie noch ein komisches Kästchen nach dem String in der Statusbar...

Code: Alles auswählen

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

static const char program_name[] = "myxd";
static Display *dpy;
static int screen;
static Window root;
static XEvent ev;

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

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

	fd = open("/home/user/fifo", O_RDWR | O_NONBLOCK);

	fd_set rfds;
	int rc;

	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);

	for (;;) {
		rc = select(fd + 1, &rfds, NULL, NULL, NULL);
		if (rc > 0) {
			ssize_t got = read(fd, name, BUFSIZ);
			if (got < 0) {
				perror("read");
				/* return 1; */
				exit(EXIT_FAILURE);
			} else if (got == 0) {
				printf("EOF\\n");
				/* return 1; */
				exit(EXIT_FAILURE);
			}
			else {
				/* remove trailing newline */
				if (name[strlen(name)]-1 == '\n')
					name[strlen(name)-1] = '\0';
				XStoreName(dpy, root, name);
				while (XPending(dpy))
					XNextEvent(dpy, &ev);
			}
		}
	}
	close(fd);
	XCloseDisplay(dpy);

	exit(EXIT_SUCCESS);
}

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

Re: C: fifo lesen

Beitrag von Meillo » 20.08.2018 17:52:11

Schoen, dass es jetzt laeuft! :-)

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
20.08.2018 17:46:35
Nunja ... das mit dem newline-Ersetzen ergibt irgendwie noch ein komisches Kästchen nach dem String in der Statusbar...

Code: Alles auswählen

				/* remove trailing newline */
				if (name[strlen(name)]-1 == '\n')
					name[strlen(name)-1] = '\0';
Das `-1' muss *in* die eckigen Klammern. ;-)
Use ed(1) once in a while!

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

Re: C: fifo lesen

Beitrag von bluestar » 20.08.2018 18:02:39

Um dir Speicherfehler zu ersparen habe ich noch ein paar kleine Hinweise für dich:

Code: Alles auswählen

	char name[BUFSIZ];
aus BUFSIZE mach ein

Code: Alles auswählen

BUFSIZE + 1
und vor der Zeile

Code: Alles auswählen

			ssize_t got = read(fd, name, BUFSIZ);
nullst du deinen String sauber

Code: Alles auswählen

memset (name, 0, BUFSIZE);
und last but not least

Code: Alles auswählen

				exit(EXIT_FAILURE);
Solltest du durch ein

Code: Alles auswählen

break;
ersetzen, damit die Aufräumarbeiten nach der For-Schleife sauber ausgeführt werden können.

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 20.08.2018 22:23:04

Also erst einmal herzlichen Dank für eure Hilfe und eure Anregungen! :THX:

Ich möchte jetzt noch aufarbeiten, wo die haupstächlichen Probleme waren, und welche Lösungen ich gefunden habe.

1.
Das Problem mit der endlos-Schleife, aus der die fifo gelesen werden soll bzw. das Problem, dass aus der endlos-Schleife auch ohne 100% CPU-Auslastung und ohne sleep gelesen werden kann.
Lösung 1. Teil:
select() nutzen (gemäß @bluestars Link), und zwar mit dem timeout NULL, so wie in "man select" angegeben:
If timeout is NULL (no timeout), select() can block indefinitely.
Lösung 2. Teil:
open() mit den Parametern "O_RDWR | O_NONBLOCK" nutzen, so wie in @bluestars Link beschrieben:
In the case of a FIFO, however, “0 length read” means there are no more FIFO writers — but more could attach later and continue feeding in data! The problem with this is that select misbehaves and marks the file descriptor as “EOF” forever. Only the initial select() call blocks until there is something to read — once it hits EOF, select will always immediately return, defeating the purpose of select().

One solution is to re-open the FIFO, but you might miss writers between your 0-length read() and the next open().

The seemingly correct solution is rather simple: the FIFO reader should open the FIFO as a writer also. In this case, select() never thinks all the writers have vanished, so there is never an EOF condition, and the reader never misses any writers. So, instead of O_RDONLY, use:

fd = open(FIFO_PATHNAME, O_RDWR | O_NONBLOCK);

2.
Das Problem, dass X nicht "name" erhalten hat.
Lösung:
Direkt nach dem "XStoreName(dpy, root, name);" ein

Code: Alles auswählen

while (XPending(dpy))
	XNextEvent(dpy, &ev);
Ich weiß nicht genau, was das macht – ich hab‘s per Google gefunden – aber es funktioniert bei mir.
Die Funktionen dienen sozusagen dazu, den Xserver bereit für den nächsten event zu machen…


3.
Keine Flüchtigkeitsfehler machen! :mrgreen:


Noch eine Frage:
@bluestar:
Warum eigentlich "BUFSIZ+1"?


Jetzt überlege ich mir noch, ob ich auch mein "timeout 10 cat" in mein Programm packe, aber bis hierher bin ich auf jeden Fall schon einmal sehr zufrieden. Endlich ein "eigenes" kleines C-Programm, das sich zu meinen bash/dash-Scripten und Perl-Programmen gesellen kann… :D
Besonders zufrieden bin ich auch damit, dass ich jetzt eine Lösung gefunden habe, in C Programme miteinander kommunizieren lassen zu können.

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

Re: C: fifo lesen

Beitrag von bluestar » 20.08.2018 22:38:36

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
20.08.2018 22:23:04

@bluestar:
Warum eigentlich "BUFSIZ+1"?
Naja wenn du exakt BUFSIZE Zeichen einliest, dann ist dein String nicht mehr null-terminiert und schon läuft strlen(...) im worst case über den Speicherplatz hinaus.

Dein Puffer muss bei String ein Byte größer sein, damit das Stringende (ASCII-0) noch ans Ende dranpasst.

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

Re: C: fifo lesen

Beitrag von Meillo » 20.08.2018 23:55:15

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
20.08.2018 22:23:04
Also erst einmal herzlichen Dank für eure Hilfe und eure Anregungen! :THX:

Ich möchte jetzt noch aufarbeiten, wo die haupstächlichen Probleme waren, und welche Lösungen ich gefunden habe.

[...]
Danke fuer deine vorbildliche Aufarbeitung deines Loesungswegs. Dies sollten sich viele User zum Vorbild nehmen! :THX:



Hier mal noch eine Variante ohne Systemcalls, sondern mit stdio:

Code: Alles auswählen

#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const char program_name[] = "myxd";
static const char fifo[] = "/home/meillo/fifo";
static Display *dpy;
static int screen;
static Window root;
static XEvent ev;

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

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

        fp = fopen(fifo, "r+");
        while (fgets(name, BUFSIZ, fp)) {
                /* remove trailing newline */
                if (name[strlen(name)-1] == '\n') {
                        name[strlen(name)-1] = '\0';
                }
                XStoreName(dpy, root, name);
                while (XPending(dpy)) {
                        XNextEvent(dpy, &ev);
                }
        }

        perror("fgets");
        fclose(fp);
        XCloseDisplay(dpy);
        exit(1);
}
Scheint ebenfalls alles zu funktionieren. Statt nicht-blockierend zu oeffnen und dann zu warten bis was kommt, oeffne ich einfach normal und lasse fgets(3) blockieren bis Input da ist.

Gibt es Gruende, warum man das nicht so machen, sondern sich low-level mit select(2) und read(2) plagen sollte?



Btw: Auch in deiner Variante gibt es keinen erfolgreichen Exit. Jeder Exit im Code muss von einem Fehler verursacht sein. EOF kann auch nicht vorkommen, wenn du read/write oeffnest (siehe die von dir erwaehnte Doku).
Use ed(1) once in a while!

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

Re: C: fifo lesen

Beitrag von schorsch_76 » 21.08.2018 08:30:09

Meillo hat geschrieben:Gibt es Gruende, warum man das nicht so machen, sondern sich low-level mit select(2) und read(2) plagen sollte?
fgets blockt im read System call. SIGINT, SIGTERM. Hier ist die Default Aktion "Terminate the process". Damit wird der Fifo nicht mehr aufgeräumt und der Handle des Rootwindow. Falls man auf select verzichten will, muss man Signal Handler setzen. Hier sind auch nur eine Hand voll System calls erlaubt (im Handler). Man müsste ein volatile oder atomic flag setzen welches in der Loop abgefragt wird und dann beenden.

Siehe [1] [2]

Lass den Sender in den Fifo mal mehr Daten als BUFSIZE reinschreiben. Dann hat der Buffer kein Ende Zeichen. Du setzt dann kein \0 mehr. strlen() geht über das ende des buffer hinaus und es ist gut möglich das es einen SIGSEV gibt. Falls nicht, gibt's du das dann trotzdem an XStoreName() weiter. XStoreName() weis nichts von deiner Buffergröße....

Du gehtst auch davon aus, dass die Daten atomar rein kommen. Durch verschiedene Faktoren kann der call aber auch die Daten nicht "in einem Rutsch" rüber geben und du hast kein Endezeichen in deinem String. Gleiches Problem wie oben beschrieben.

Durch Scheduling kann es aber auch vorkommen, dass du länger schläfst als die Sender. Es stehen 2 Requests in dem Fifo. Du erkennst aber im Höchstfall nur einen Request. Zum testen, starte das Program, CTRL+Z. Sende mehrere Requests und dann in der Shell mit dem Suspend Programm... fg.

[1] http://man7.org/tlpi/
[2] https://www.amazon.de/Linux-Programming ... 1593272200

Antworten