C: fifo lesen

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

Re: C: fifo lesen

Beitrag von bluestar » 22.08.2018 08:50:29

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
21.08.2018 22:28:20

Code: Alles auswählen

...
	char name[BUFSIZ+1];
...
	for (;;) {
...
			memset (name, 0, sizeof(name));
			ssize_t got = read(fd, name, sizeof(name));
---
}
Achtung an der Stelle read(fd,name,sizeof(name)) hast du wieder das Problem, dass du bis zum Ende des Puffers liest und somit dein \0 End-Of-String Marker flöten geht... Also bitte sizeof(name) - 1 als Anzahl nehmen oder auf auf die funktions fgets(...) ausweichen, da erledigt die Funktion das \0 Handling für dich.

Das ist übrigens ein typisches Beispiel für die höhrere Fehleranfälligkeit bei der Low-Level Code Programmierung, im Low Level Bereich musst du dich um solch lästigen Kleinkrams kümmern.

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 22.08.2018 11:53:13

@bluestar: Mist, schon wieder übersehen. Ich wusste doch, dass da noch was war... Danke!

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
21.08.2018 22:28:20
BTW: Das mit "printf("EOF\\n");" habe ich aus @bluestars Link. Ich werde das nochmal nachschauen...
Die Überprüfung des read-Rückgabewertes ist richtig:
man 2 read hat geschrieben: On success, the number of bytes read is returned (zero indicates end of file), [...].
On error, -1 is returned [...].
schorsch_76 hat geschrieben: ↑ zum Beitrag ↑
22.08.2018 07:57:07
würde ich auf jeden Fall dafür sorgen das beim beenden des Programms ein XCloseDisplay erfolgt.
Alles klar, ich überlege, wie ich das machen kann.

Meillo hat geschrieben: ↑ zum Beitrag ↑
22.08.2018 06:57:36
Erschreckend, was fuer ein verzerrtes Verstaendnis von Unix und Suckless da angekommen ist. :shock:
:oops:
Ich hatte das immer so verstanden:
- Code soll elegant sein: lesbar, aber auch effizient
- im Zweifelsfall zugunsten der Geschwindigkeit die etwas weniger lesbare Variante nehmen
- je mehr "low-level" der Code ist, desto definierter ist er
- je definierter der Code ist, desto besser ist er
Aber vielleicht habe ich mir da Falsches gedacht, weil ich bisher auf praktischer Ebene haupstächlich mit Shell-Scripten und Perl gearbeitet habe.

Mal ein einfaches Beispiel für mein Verständnis:
- Lesen einer Datei in einem dash-Script (wo also nicht $(< datei) zu Verfügung steht):
statt

Code: Alles auswählen

tmp_var=$(cat /proc/net/wireless)
dbm1="${tmp_var%.*}"
dbm="${dbm1##*-}"

Code: Alles auswählen

while read -r line; do
	case $line in
		"wlp2s0"*)
			dbm1="${line#*-}";;
	esac
done < /proc/net/wireless
dbm="${dbm1%.*}"
Ist einfach schneller, vor allem im Vergleich zu sed:

Code: Alles auswählen

dbm=$(sed -n 's/^wlp2s0[^-]*\-//;s/\..*//p' /proc/net/wireless)
Die drei Scripte mit dash-Shebang und "echo $dbm" am Schluss in bash ausgeführt (in der obigen Reihenfolge):

Code: Alles auswählen

/opt/scripts$ time ./test1.sh
69

real	0m0,005s
user	0m0,000s
sys	0m0,000s
/opt/scripts$ time ./test2.sh
69

real	0m0,003s
user	0m0,000s
sys	0m0,000s
/opt/scripts$ time ./test3.sh
69

real	0m0,006s
user	0m0,000s
sys	0m0,000s
"Low-level" ist hier quasi die builtin-Ebene. Die ist einfach immer schneller, auch wenn die Parameter Substitutions nicht so einfach lesbar sind.

Perl finde ich auch elegant (für meine aktuellen Verwendung, also Scripte von 100-300 Zeilen Länge). Hier ist zwar die Startzeit etwas mehr, aber die Ausführungszeit schön kurz, da die Scripte vom Perl-Interpreter kompiliert werden. So habe ich das jedenfalls verstanden und gemessen.
Außerdem kann man Perl sehr lesbar schreiben - z.B. so ähnlich wie C.

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

Re: C: fifo lesen

Beitrag von Meillo » 22.08.2018 12:28:22

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
22.08.2018 11:53:13
Meillo hat geschrieben: ↑ zum Beitrag ↑
22.08.2018 06:57:36
Erschreckend, was fuer ein verzerrtes Verstaendnis von Unix und Suckless da angekommen ist. :shock:
:oops:
Ich hatte das immer so verstanden:
- Code soll elegant sein: lesbar, aber auch effizient
Ja.
- im Zweifelsfall zugunsten der Geschwindigkeit die etwas weniger lesbare Variante nehmen
Genau anders rum! :facepalm: ;-)

Bei vierzig Jahre altem Code ist eine gute Lesbarkeit noch immer vorteilhaft, waehrend die Geschwindigkeitsoptimierungen von damals heute voellig irrelevant sind und sehr wahrscheinlich seine Protabilitaet negativ beeinflusst haben ... weswegen es ihn vermutlich gar nicht mehr gibt. Wenn er unlesbar (wenn auch blitzschnell) gewesen waere, dann haette man sich die Pflege irgendwann auch gespart und ihn von Grund auf neu geschrieben. Fuer diese Anzeichen muss man normalerweise keine vierzig Jahre warten, es zeigt sich bereits nach wenigen Jahren ...


Unix wurde 1973 (IIRC) von hauptsaechlich Assembler auf hauptsaechlich C portiert. Manche waren der Meinung, dass das fuer ein Betriebssystem viel zu ineffizient sei. Rueckblickend war es einer der wertvollsten Schritte in der Unix-Geschichte.

MH (ein Mailclient der mehr der Unix Philosphie entspricht als jeder andere) wurde 1978 (IIRC) als Prototyp entwickelt, entgegen allen Befuerchtungen, dass das viel zu langsam und damit unbenutzbar sei. In den 80er Jahren war er die Basis fuer die erste MIME-Implementierung in einem MUA. Noch heute lebt sein Code!

Und ueberhaupt, der ganze Fokus auf die Shell in Unix ist fuer die Performance-Optimierer eine Katastrophe, aber aus Produktivitaetssicht ist die Unix-Toolchest (also Menge der ganzen Kommandozeilenprogramme: cat, tr, cut, sort, uniq, ...) so ziemlich das Grossartigste was es gibt. Sie lebt noch immer, obwohl sie ja so furchtbar ineffizient ist.


Am besten du vergisst alle Gedanken an Geschwindigkeitsoptimierungen und optimierst nur noch auf Lesbarkeit. So uebervereinfacht das auch ist, ich bin mir sicher, dass das zu besserer Software fuehrt. (Meist ist der am besten verstaendliche Code sowieso schon eine sehr schnelle Umsetzung ... und wenn er es heute noch nicht ist, dann naechstes Jahr.)
Ken Thompson hat geschrieben: One of my most productive days was throwing away 1,000 lines of code.
Doug McIlroy hat geschrieben: The real hero of programming is the one who writes negative code.
Kurz ist meist gut. Kurz ist (wenn man es nicht uebertreibt) oft besser verstaendlich.

Auf dem Cover von ``The Practice of Programming'' von Kernighan und Pike stehen die Worte:

- Simplicity
- Clarity
- Generality

Man darf die als die Kernziele beim Programmieren betrachten (in dieser Reihenfolge).


... das alles nur als ein paar Schlaglichter. Wenn du wirklich verstehen willst, dann musst du die Buecher von diesen Unix-Typen lesen. (Oder dir meine Vortraege anschauen. ;-) )


Zum Schluss noch der Hinweis auf diese, sehr bekannten Hinweise zum Programmieren in C: https://www.lysator.liu.se/c/pikestyle.html
Use ed once in a while!

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 » 22.08.2018 13:08:17

Das mit dem C Fifo das passt schon so. Du hast auf die Posix API Programmiert. Das läuft auf praktisch jedem OS. Selbst VxWorks, Qnx und allen anderen.

Mach dir da keinen zu großen Kopf! Du lernst! Da kommt nicht immer 100%iger Code raus der 100 Jahre halten muss. Das muss nur deinen Ansprüchen genügen!

:THX:

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 22.08.2018 13:44:13

Vielen Dank für euren philosophischen Input! :THX:

Der Absatz über Comments in Deinem Link, @meillo, hat mich an etwas erinnert, was ich dazu mal von Linus Torvalds gelesen habe:
I'm not even going to start talking about the people who prefer to
"box in" their comments, and line up both ends and have fancy boxes of
stars around the whole thing. I'm sure that looks really nice if you
are out of your mind on LSD, and have nothing better to do than to
worry about the right alignment of the asterisks.
:wink:
Quelle: https://lkml.org/lkml/2016/7/8/625

Dann habe ich auf Deine Anregung noch gefunden: http://marmaro.de/docs/studium/unix-phil/unix-phil.pdf
Das meintest Du doch, oder? Werde ich mal reinlesen. (Das Schöne an IT ist, dass man nebenbei auch seine Englisch-Kenntnisse verbessern kann.)

schorsch_76 hat geschrieben: ↑ zum Beitrag ↑
22.08.2018 13:08:17
Das muss nur deinen Ansprüchen genügen!
Das ist der Grund, weshalb ich lerne... :) (Schließlich ist Programmieren aktuell (noch?) "nur" ein Hobby von mir.)

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

Re: C: fifo lesen

Beitrag von Meillo » 22.08.2018 15:21:38

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
22.08.2018 13:44:13
Dann habe ich auf Deine Anregung noch gefunden: http://marmaro.de/docs/studium/unix-phil/unix-phil.pdf
Das meintest Du doch, oder?
... und das: http://marmaro.de/docs/chaosseminar/

Und textuell noch das: http://marmaro.de/docs/master/schnalke-mmh.pdf ... falls du dir das geben willst. ;-)
Use ed once in a while!

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 22.08.2018 15:46:39

Oh, da hast Du ja ganz schön viel zu dem Thema verfasst. :THX:

Übrigens, die Videos in dem Link http://ulm.ccc.de/ChaosSeminar/2010/03_UnixPhil gibt's nicht mehr...

BTW - nochmal zu meinen shell-builtins: Damit kein falscher Eindruck entsteht, wollte ich nochmal erwähnen, dass die nur in bestimmten Fällen Performance-Vorteile bringen. Die Datei /proc/net/wireless z.B. ist drei Zeilen lang. Wenn ich jetzt aber z.B. die Namen aller Dateien in /usr/share/applications/ möchte, die "NoDisplay=true" beinhalten, lande ich mit builtins von der Performance her auf der Nase und nehme grep. Insofern sind die Unix-Tools im Allgemeinen schon auch von der Performance her zu bevorzugen, denn wenn sie auch bei kleinen Datenmengen ein klein wenig langsamer sein mögen, so sieht es bei größeren Datenmengen um Gößenordnungen anders aus.

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 22.08.2018 21:26:39

Uff, signal handling geschafft... :D

Erst wollte ich einfach zusätzlich noch auf stdin lauschen - mit select geht das ja. Doch nachdem ich das implementiert hatte, habe ich gemerkt und ist mir wieder eingefallen, dass man nicht nach stdin eines im Terminal laufenden Processes mittels "echo /proc/pid/fd/0" pipen kann, weil diese fds immer zu denen des jeweiligen /dev/pts umgeleitet sind... :evil:
Aber eigentlich scheint es mir so jetzt auch eleganter. Ich nutze sigaction und etabliere vor dem loop eine Aktion, die beim Empfangen von SIGTERM (also "kill -15 pid") ausgeführt wird. Diese Aktion ist einfach das Umschalten einer toggle-Variable (ich mag toggle-Variablen :D ), auf deren Ursprungszustand der while-loop basiert. Wird sie also umgeschaltet, beendet sich der loop und dann werden die unter dem Loop stehenden Aufräumarbeiten ausgeführt. Man kann den Vorgang mit eingeschobenen printfs verifizieren.
(Das einzig Doofe ist, dass man die Variable signum benutzen muss, weil sich sonst gcc beklagt...)

Das kommt dann also bei "kill -15" heraus:

Code: Alles auswählen

/opt/scripts/dev$ ./test2xd
Hello, I am the signal handler.
Hello, I am the end of the main function.
Der Code mit den Test-printfs:

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>
#include <signal.h>

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

static void
term_hdl (int signum)
{
	/* we have to use signum */
	switch (signum) {
		case SIGTERM:
			printf("Hello, I am the signal handler.\n");
			finish = 1;
	}
}

int
main(void) 
{
	char *display_name = NULL;
	char name[BUFSIZ];
	int fd;
	struct sigaction term_act;
	
	memset(&term_act, 0, sizeof(term_act));

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

	/* fd = open("/tmp/dwmblocks_fifo_intern", O_RDWR | O_NONBLOCK); */
	fd = open("/home/user/fifo", O_RDWR | O_NONBLOCK);
	if (fd < 0) {
		perror("open");
		exit(1);
	}

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

	fd_set rfds;
	int rc;

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

	while (finish == 0) {
		rc = select(fd + 1, &rfds, NULL, NULL, NULL);
		if (rc > 0) {
			memset(name, 0, sizeof(name)-1);
			ssize_t got = read(fd, name, sizeof(name)-1);
			if (got < 0) {
				perror("read");
				break;
			} else if (got == 0) {
				printf("EOF\\n");
				break;
			}
			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);
			}
		}
	}
	/* will never get here */
	close(fd);
	XCloseDisplay(dpy);
	printf("Hello, I am the end of the main function.\n");

	exit(EXIT_SUCCESS);
}
Wie findet ihr das? Jetzt könnte ich doch das Programm mittels "kill -15 pid" aus dem Parent-Wrapper-Script heraus auf jeden Fall sauber beenden.

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

Re: C: fifo lesen

Beitrag von bluestar » 22.08.2018 21:34:26

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
22.08.2018 21:26:39

Code: Alles auswählen

...
			memset(name, 0, sizeof(name)-1);
			ssize_t got = read(fd, name, sizeof(name)-1);
...
Ich muss wohl doch noch mal schimpfen :mrgreen:

1) Wenn du read(...) nutzt dann

Code: Alles auswählen

memset(name, 0, sizeof(name));                       // KEIN -1 bei der Länge
ssize_t got = read(fd, name, sizeof(name)-1);    // EIN -1 bei der Länge wobei besser im Sinne von UTF-8 wäre:  sizeof(name) - sizeof(char)
2) Wenn du fgets(...) nutzt dann

Code: Alles auswählen

memset(name, 0, sizeof(name));                     // KEIN -1 bei der Länge
ssize_t got = fgets(name, sizeof(name),fp);    // KEIN -1 bei der Länge
fgets weiß ja, das du einen String in dem Puffer haben willst, also liest es MAX (num -1) Bytes aus dem Fifo uns hängt ein \0 hinten dran....

*WINKS*

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 22.08.2018 22:15:32

Ohje! :facepalm: :oops:
Vielen Dank für Deine Geduld, @bluestar! Manchmal muss ich einfach noch konzentrierter sein...

Ich habe es korrigiert (und dabei noch das sigaction an die richtige Stelle in main() gesetzt und den letzten comment entfernt, der nicht mehr gültig ist).

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>
#include <signal.h>

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

static void
term_hdl (int signum)
{
	/* we have to use signum */
	switch (signum) {
		case SIGTERM:
			printf("Hello, I am the signal handler.\n");
			finish = 1;
	}
}

int
main(void) 
{
	char *display_name = NULL;
	char name[BUFSIZ];
	int fd;
	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 == '\0') {
		fprintf(stderr, "%s:  unable to open display '%s'\n",
				program_name, XDisplayName (display_name));
		exit(2);
	}
	root = RootWindow(dpy, screen);

	/* fd = open("/tmp/dwmblocks_fifo_intern", O_RDWR | O_NONBLOCK); */
	fd = open("/home/user/fifo", O_RDWR | O_NONBLOCK);
	if (fd < 0) {
		perror("open");
		exit(1);
	}

	fd_set rfds;
	int rc;

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

	while (finish == 0) {
		rc = select(fd + 1, &rfds, NULL, NULL, NULL);
		if (rc > 0) {
			memset(name, 0, sizeof(name));
			ssize_t got = read(fd, name, sizeof(name)-sizeof(char));
			if (got < 0) {
				perror("read");
				break;
			} else if (got == 0) {
				printf("EOF\\n");
				break;
			}
			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);
	printf("Hello, I am the end of the main function.\n");

	exit(EXIT_SUCCESS);
}
Was meint ihr übrigens zu dem neuen dpy-Test?
Also statt "if (!dpy)" "if (dpy == '\0')"? Ich habe noch auf der Suckless-Seite zusätzliche Links zu Coding-Style gesehen und in einem steht:
Don't use ‘!’ for tests unless it's a boolean [...]
Quelle: https://man.openbsd.org/style
Schien mir irgendwie Sinn zu ergeben.

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

Re: C: fifo lesen

Beitrag von Meillo » 22.08.2018 23:34:31

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
22.08.2018 22:15:32
Was meint ihr übrigens zu dem neuen dpy-Test?
Also statt "if (!dpy)" "if (dpy == '\0')"?
Das ist leider falscher. ;-)

XOpenDisplay(3) liefert einen Pointer zurueck und ...
Manpage XOpenDisplay(3) hat geschrieben: If XOpenDisplay does not succeed, it returns NULL.
Logisch korrekt ist also ``if (dpy == NULL)''. (Ich persoenlich finde ``if (!dpy)'' aber besser, weil mehr Signal und weniger Noise und damit direkter, treffender und lesbarer ... aber ich weiss, dass in dem Punkt viele da ``== NULL'' bevorzugen.)

In der Praxis laeuft's auf's gleiche hinaus, trotzdem sollte man NULL, 0 und '\0' voneinander unterscheiden und stets das Passende verwenden.

Ich habe noch auf der Suckless-Seite zusätzliche Links zu Coding-Style gesehen und in einem steht:
Don't use ‘!’ for tests unless it's a boolean [...]
Quelle: https://man.openbsd.org/style
Schien mir irgendwie Sinn zu ergeben.
Das hat durchaus seine Logik und einen Sinn. Ich selber verwende trotzdem `!' auch in anderen Situationen, immer wenn es mir so lesbarer erscheint. Bloss bei strcmp(3) verwende ich `!' nie und das aus starker Ueberzeugung: Es fuehrt einfach zu zu vielen Verstaendnisproblemen! Auch bei fork(2) halte ich `!' fuer nie sinnvoll. Insofern kann man schon sagen, dass man es nur fuer booleanartige Ausdruecke verwenden sollte. Es muss nicht nur 1 und 0 sein, es koennen schon auch viele verschiedene Werte zurueckgegeben werden (z.B. bei fopen(3)), aber wenn ich dort pruefe, ob der Aufruf fehlerhaft war, dann pruefe ich mit `!', weil ich etwas booleanartiges wissen will. Ich wuerde also sagen, dass man `!' nur verwenden sollte, wenn man eine booleanartige Pruefung macht (egal welcher Art die Daten sind).

Bei strcmp(3) macht das `!' IMO keinen Sinn, weil was soll ``if not string-compare'' bitte bedeuten? (``if not fopen'' macht dagegen viel Sinn.)

Bei fork(2) macht die Pruefung mit `!' auch keinen Sinn, weil ``!fork()'' bedeutet, dass man das Kind ist.

Entscheidend ist die Sinnhaftigkeit und das Verstaendnis beim Codeleser. Solange du dir nicht sicher bist, dass du gute Gruende hast, es auf eine bestimmte Art zu machen -- als Anfaenger hast du die fast nie --, dann mach es so wie die (guten) Styleguides es empfehlen.
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 12:55:20

Oh, alles klar, vielen Dank, @meillo! Mit dem Unterschied zwischen Null, 0 und '\0' sollte ich mich wohl mal beschäftigen...

Eigentlich sollte man ja alle Variablen in der entsprechenden Funktion deklarieren, wenn sie nicht funktionsübergreifend genutzt werden, richtig?
Und nach den breaks des loops bzw. nach einem gescheiterten open() wäre ja auch Aufräumen angesagt...

Habe das mal versucht:

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[] = "test2xd";
/* 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)
{
	char *display_name = NULL;
	char name[BUFSIZ];

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

	static Display *dpy;
	struct sigaction term_act;
	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 < 0) {
		perror("open");
		XCloseDisplay(dpy);
		exit(EXIT_FAILURE);
	}
	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);

	while (finish == 0) {
		rc = select(fd + 1, &rfds, NULL, NULL, NULL);
		if (rc > 0) {
			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[strlen(name)-1] == '\n')
					name[strlen(name)-1] = '\0';
				XStoreName(dpy, root, name);
				while (XPending(dpy))
					XNextEvent(dpy, &ev);
			}
		}
	}
	close(fd);
	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 » 23.08.2018 13:43:38

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 12:55:20
Mit dem Unterschied zwischen Null, 0 und '\0' sollte ich mich wohl mal beschäftigen...
Die Binaerdarstellung all dieser Werte sind nur Nullen. (Auch wenn der Standard das nicht erfordert, so ist es in der Realitaet in allen Implementierungen so und es macht auch Sinn.)

Die Unterschiede liegen nur in den Datentypen. Finde heraus, welche Datentypen die drei Ausdruecke haben! 0 und '\0' sind einfach, NULL ist schwieriger.



Und ja, globale Variablen sollte man moeglichst vermeiden.
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 13:52:27

Alles klar, mache ich.
Was mir übrigens noch eingefallen ist (habe ich oben vergessen): Ich kann mir beim schließen des Prozesses das "-15" als Argument für kill sparen, denn das default-Signal von kill ist eh SIGTERM:

man 1 kill
The default signal for kill is TERM.

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

Re: C: fifo lesen

Beitrag von bluestar » 23.08.2018 16:38:27

Ich bin mir nicht sicher allerdings will ich es dennoch mal loswerden:

Code: Alles auswählen

while (XPending(dpy))
	XNextEvent(dpy, &ev);
Der Code wird bei dir ja nur nach dem Empfang von Daten ausgeführt, mein Gefühl sagt mir das du hier auf kurz oder lang ein Problem bekommen wirst, wenn der EventPuffer von X voll läuft, während du "unendlich lange" auf Daten aus deinem Fifo wartest.

Ich würde die While Schleife (stark vereinfacht so gestalten):

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
bluestar
Beiträge: 2418
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: C: fifo lesen

Beitrag von bluestar » 23.08.2018 16:50:25

Und noch ne Anmerkung möchte ich gern loswerden:

Code: Alles auswählen

if (name[strlen(name)-1] == '\n')
	name[strlen(name)-1] = '\0';
Wenn strlen(name) gleich 0 ist, dann schreibst du an name[-1] = '\0' in den Speicher....

Wäre also wohl besser so:

Code: Alles auswählen

if ( (strlen(name) > 0) && (name[strlen(name)-1] == '\n') )
	name[strlen(name)-1] = '\0';

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

Re: C: fifo lesen

Beitrag von Meillo » 23.08.2018 17:44:00

bluestar hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 16:50:25
Und noch ne Anmerkung möchte ich gern loswerden:

Code: Alles auswählen

if (name[strlen(name)-1] == '\n')
	name[strlen(name)-1] = '\0';
Wenn strlen(name) gleich 0 ist, dann schreibst du an name[-1] = '\0' in den Speicher....
Gut gesehen! ... aber kann das im konkreten Fall denn passieren? Schliesslich sind wir im Else-Teil, wo immer Inhalt da sein sollte.

Ist es nicht so: Wenn wir nichts lesen ist's EOF, und wenn wir etwas lesen, dann haben wir mindestens ein Newline?

Wäre also wohl besser so:

Code: Alles auswählen

if ( (strlen(name) > 0) && (name[strlen(name)-1] == '\n') )
	name[strlen(name)-1] = '\0';
Oder:

Code: Alles auswählen

if (strlen(name) == 0) {
        continue;
}
if (name[strlen(name)-1] == '\n') {
	name[strlen(name)-1] = '\0';
}
... wobei manch ein C-Programmierer natuerlich ``if (!*name) {'' schreiben wuerde. ;-)


Fehlerfaelle nacheinander auszusortieren und am Ende die eigentliche Verarbeitung zu machen, fuehrt meist zu einfacher verstaendlicherem Code mit weniger Fehlern.

Btw: Wenn dein C-Code mal nicht mehr sinnvoll mit 8-Zeichen-Tabs eingerueckt auf 80-Zeichen-lange Zeilen passt, dann ist das ein Zeichen dafuer, dass er stilistisch verbessert werden kann. (Bei anderen Programmiersprachen sieht das teilweise anders aus, aber bei C trifft die Regel gut zu.)
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 » 23.08.2018 18:45:50

Meillo hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 17:44:00
Ist es nicht so: Wenn wir nichts lesen ist's EOF, und wenn wir etwas lesen, dann haben wir mindestens ein Newline?
Wenn wir exakt ein '\0' lesen, dann ist got = 1 und strlen(name) = 0.

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

Re: C: fifo lesen

Beitrag von Meillo » 23.08.2018 18:49:18

bluestar hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 18:45:50
Meillo hat geschrieben: ↑ zum Beitrag ↑
23.08.2018 17:44:00
Ist es nicht so: Wenn wir nichts lesen ist's EOF, und wenn wir etwas lesen, dann haben wir mindestens ein Newline?
Wenn wir exakt ein '\0' lesen, dann ist got = 1 und strlen(name) = 0.
Okay, wenn jemand Binaerdaten reinschreibt. Akzeptiert!
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 21:15:54

Vielen Dank für eure weiteren Anregungen! :THX:

Wegen dem X-Code werde ich nochmal recherchieren. Man muss da bei Manuals und so aufpassen, denn im Gegensatz zu einem Window-Manager wartet mein Programm ja nicht auf einen Xevent (also z.B. einen Klick vom User), sondern der Xserver wartet auf mein Programm. Da scheint mir das Abarbeiten des Event-Buffers am Ende des loops irgendwie logischer?

Den Test auf newline werde ich anpassen, danke!
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:


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?
So wie ich es verstehe, kann man 0 immer für ifs nutzen, denn NULL und ’\0‘ laufen auch auf den int 0 hinaus.

Benutzeravatar
bluestar
Beiträge: 2418
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: 9225
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: 9225
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: 2418
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

Antworten