C: fifo lesen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Benutzeravatar
schorsch_76
Beiträge: 2535
Registriert: 06.11.2007 16:00:42
Lizenz eigener Beiträge: MIT Lizenz

Re: C: fifo lesen

Beitrag von schorsch_76 » 21.08.2018 09:23:00

Deshalb mach ich solche asyncronen Serverprogramme mit select/poll/epoll/kqueue. Der Signalhandler wird gesetzt und macht mit den Self Pipe Trick [1] das Signal für die select Schleife verfügbar ohne den Prozess abzubrechen. Die Daten die aus dem Request kommen werden in einen Puffer oder in einen Requestparser geschubst und dann verarbeitet. Der Request Parser gibt dann bsp. einen enum von "more_info_needed,done" zurück. In einer Membervariable des Requests ist dann der geparste Request zu finden. Da die Zeichen einzeln in den Parser geschubst werden oder per buffer, sizeofbuffer und der Rückgabewert die benutzen Bytes sind, funktioniert das. Falls weniger Bytes genutzt werden als reingegeben, behandle ich den request und gib dann die anderen Bytes wieder in den Parser.

Damit hat man dann ein sauberes Handling der Signale und Requests und einen sauberen Shutdown ohne SIGSEV ;)

[1] https://lwn.net/Articles/177897/

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

Re: C: fifo lesen

Beitrag von Meillo » 21.08.2018 09:45:35

schorsch_76 hat geschrieben: ↑ zum Beitrag ↑
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.
Was meinst du mit ``FIFO aufraeumen''? Das Einzige was nicht gemacht wird, ist doch ``fclose(fp)'', was sich aber eruebrigt, wenn sich das Programm beendet, weil dann automatisch alle FDs geschlossen werden und aller Speicher freigegeben wird. Ich verstehe nicht was da nicht aufgeraeumt bleiben koennte.

Beim X-Window-Handle fehlt mir das Fach-Know-How. Beibt da etwas ueber das Programmende hinaus noch reserviert?

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.
Sicher?
Manpage fgets(3) hat geschrieben: fgets() reads in at most one less than size characters
from stream and stores them into the buffer pointed to by
s. Reading stops after an EOF or a newline. If a new‐
line is read, it is stored into the buffer. A terminat‐
ing null byte ('\0') is stored after the last character
in the buffer.
Es ist garantiert, dass der von fgets(3) gelesene String immer Null-terminiert ist.

Falls mehr reingeschrieben wird als fgets(3) auf einmal liest, dann verarbeitet es die Eingabe eben haeppchenweise. Das habe ich eben getestet.

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.
Ich hab's genau so getestet. Mein Programm hat alle Zeilen in der FIFO gelesen, eine nach der anderen. Natuerlich werden die sofort nacheinander gelesen und so schnell angezeigt, dass man nur die letzte im Rootwindow-Title sieht. Aber das hat ja nichts mit dem Verarbeiten der FIFO selbst zu tun.



Vielleicht mal grundsaetzlich: Es waere schon sehr un-unixmaessig, wenn man FIFOs nicht wie regulaere Dateien lesen koennte. Darum geht's schliesslich in Unix immer: Everything's a file! Keine Sonderbehandlungen fuer spezielle Dateitypen. Das war ein Grunddesignziel von Unix.

FIFOs sind auch alt genug, dass diese Ziele noch umgesetzt sind. (Sockets sind die grosse Ausnahme. Das Warum wird klar, wenn man ihre Entstehung betrachtet: Sie wurden von Personen implementiert, die Unix nicht verstanden hatten. Dass man Sockets auch nach Unix-Art implementieren kann demonstriert Plan 9 ... da waren es dann auch wieder die Unix-Entwickler selbst, die sie implementiert haben.)

Und weiter: Wenn man FIFOs nicht wie regulaere Dateien lesen koennte, dann koennte auch sed(1) beispielsweise keine FIFOs lesen ... ausser es haette Sondercode zum Umgang mit ihnen ... oder man wuerde in sed(1) alles IO auf Systemcall-Ebene durchfuehren, was den ganzen Sinn von stdio zunichte machen wuerde. Wer wuerde denn stdio verwenden, wenn er damit nur regulaere Dateien lesen koennte? Und was ist eigentlich mit Terminals? Die sind schliesslich auch keine regulaeren Dateien sondern Devices. Devices kann man mit stdio problemlos lesen. Sind FIFOs eher wie Devices oder eher wie regulaere Dateien? ...

Das Lesen von FIFOs darf (in Unix!) letzlich gar nicht anders sein als das Lesen regulaerer Dateien. Und wenn das stimmt, wer arbeitet dann freiwillig auf Systemcall-Ebene wenn er stdio nutzen kann?
Use ed once in a while!

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

Re: C: fifo lesen

Beitrag von schorsch_76 » 21.08.2018 10:33:08

fgets kann aber nur die angegebene Puffergröße lesen. Was passiert wenn du BUFSIZE+x Bytes (ohne 0 oder \n) in den FIFO schreibst? ;)

Ich zitiere da aus "The Linux Programming Interface"
Bild

Bild

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

Re: C: fifo lesen

Beitrag von Meillo » 21.08.2018 11:12:39

schorsch_76 hat geschrieben: ↑ zum Beitrag ↑
21.08.2018 10:33:08
fgets kann aber nur die angegebene Puffergröße lesen.
fgets(3) liest bis zur Puffergroesse oder bis zum ersten Newline, was immer frueher kommt.
Was passiert wenn du BUFSIZE+x Bytes (ohne 0 oder \n) in den FIFO schreibst? ;)
Dann liest fgets(3) sooft es ganze BUFSIZ-Stuecke lesen kann und verarbeitet die. Wenn noch ein Stueck in der FIFO steckt, das weder ein Newline enthaelt noch BUFSIZ gross ist, bleibt das stehen bis irgendwann ein Newline in die FIFO geschrieben wird oder BUFSIZ voll wird. (Es ist folglich notwendig, dass ganze Textzeilen, also mit abschliessendem Newline in die FIFO geschrieben werden.)

Probier's einfach aus! Mach ein printf(3) des Gelesenen in meine Schleife und verwende eine kleine Puffergroesse. Dann kannst du all die Faelle durchspielen.


fgets(3) terminiert seinen String immer!
Use ed once in a while!

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

Re: C: fifo lesen

Beitrag von schorsch_76 » 21.08.2018 11:50:23

@meillo: Ich wird es heut abend mal ausprobieren ;)

Zum x window Handle: Da würde ich schon schauen das ich das auf jeden Fall schließen. Die zugrunde liegende IPC zwischen dir und dem XServer hängt (so wie ich das verstehe) dazwischen. Da X eine riesige Codebase ist, würde ich mich als Programmierer schon möglichst daran halten alle Resourcen wieder zurück zu geben, da ich nicht 100% sagen kann was dort passiert (oder auch nicht passiert, falls ich es nicht mache).

Im Zweifel könnten dem xorg Prozess die File Descriptoren ausgehen.

https://www.x.org/archive/X11R7.5/doc/m ... lay.3.html
The XCloseDisplay function closes the connection to the X server for the display specified in the Display structure and destroys all windows, resource IDs (Window, Font, Pixmap, Colormap, Cursor, and GContext), or other resources that the client has created on this display, unless the close-down mode of the resource has been changed (see XSetCloseDownMode). Therefore, these windows, resource IDs, and other resources should never be referenced again or an error will be generated. Before exiting, you should call XCloseDisplay explicitly so that any pending errors are reported as XCloseDisplay performs a final XSync operation.

XCloseDisplay can generate a BadGC error.

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

Re: C: fifo lesen

Beitrag von schorsch_76 » 21.08.2018 20:45:32

So, das erste was ich direkt nach dem compilieren bekomme....

Code: Alles auswählen

georg@M4700:~/Dokumente/Entwicklung/misc/DfDeX11$ gcc set-title.c -lX11  -o set-title                                                                                                                                                                                          
georg@M4700:~/Dokumente/Entwicklung/misc/DfDeX11$ ./set-title                                                                                                                                                                                                                  
Speicherzugriffsfehler                                                                                                                                                                                                                                                         
georg@M4700:~/Dokumente/Entwicklung/misc/DfDeX11$ gcc set-title.c -O0 -g -lX11  -o set-title                                                                                                                                                                                   
georg@M4700:~/Dokumente/Entwicklung/misc/DfDeX11$ ./set-title 
Speicherzugriffsfehler                                                                                                                                                                                                                                                         
georg@M4700:~/Dokumente/Entwicklung/misc/DfDeX11$ gdb ./set-title                                                                                                                                                                                                              
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git                                                                                                                                                                                                                                    
Copyright (C) 2016 Free Software Foundation, Inc.                                                                                                                                                                                                                              
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>                                                                                                                                                                                                  
This is free software: you are free to change and redistribute it.                                                                                                                                                                                                             
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"                                                                                                                                                                                                     
and "show warranty" for details.                                                                                                                                                                                                                                               
This GDB was configured as "x86_64-linux-gnu".                                                                                                                                                                                                                                 
Type "show configuration" for configuration details.                                                                                                                                                                                                                           
For bug reporting instructions, please see:                                                                                                                                                                                                                                    
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./set-title...done.
(gdb) start
Temporary breakpoint 1 at 0xb4b: file set-title.c, line 16.
Starting program: /home/georg/Dokumente/Entwicklung/misc/DfDeX11/set-title 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 1, main () at set-title.c:16
16              char *display_name = NULL;
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
_IO_fgets (buf=0x7fffffffbfa0 "", n=8192, fp=0x0) at iofgets.c:47
47      iofgets.c: Datei oder Verzeichnis nicht gefunden.
(gdb) bt
#0  _IO_fgets (buf=0x7fffffffbfa0 "", n=8192, fp=0x0) at iofgets.c:47
#1  0x0000555555554c92 in main () at set-title.c:29
(gdb) l
42      in iofgets.c
(gdb) ls
Undefined command: "ls".  Try "help".
(gdb) bt
#0  _IO_fgets (buf=0x7fffffffbfa0 "", n=8192, fp=0x0) at iofgets.c:47
#1  0x0000555555554c92 in main () at set-title.c:29
(gdb) f 1
#1  0x0000555555554c92 in main () at set-title.c:29
29              while (fgets(name, BUFSIZ, fp)) {


fifo konnte nicht geöffnet werden, da er noch nicht existiert. Das sollte durch dieses Programm erledigt werden ;) Auch wird nicht geprüft ob der fifo geöffnet wurde. Nachdem ich den Pfad angepasst und den Fifo erstellt habe, hab ich mal die Funktion getestet.

Mit einem Programm hab ich das dann mal für 3h durchgetestet und es hat keinen Crash gegeben (nur wenn der Fifo nicht da ist). :D

SendRnd (da nopaste kaputt ist gerade)

Code: Alles auswählen

g++ --std=c++14 SendRnd.cpp -l boost_program_options -o sendrnd
./sendrnd --of /home/georg/fifo --alphabet=random --runs=1000000 --min_length=1000 --max_length=1280000


#include <array>
#include <fstream>
#include <iostream>
#include <random>
#include <string>

#include <boost/program_options.hpp>

namespace po = boost::program_options;

enum class alphabet_t
{
        alpha,
        alnum,
        print,
        rnd
};

int run(std::ostream &os, const alphabet_t alphabet, const int min_length,
                const int max_length, const int runs)
{
        // clang-format off
        // - 1 because 0 based array index
        std::uniform_int_distribution<> dis_alpha       (0,     2 * 26          - 1);
        std::uniform_int_distribution<> dis_alnum       (0,     2 * 26 + 10 - 1);
        std::uniform_int_distribution<> dis_print       (0, 95                  - 1);
        std::uniform_int_distribution<> dis_rnd         (0, 256                 - 1);

        std::array<char, 95> int2char =
        {
                'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
                'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
                'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
                'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',

                'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
                'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
                'W', 'X', 'Y', 'Z', '0', '1', '2', '3',
                '4', '5', '6', '7', '8', '9', '!', '"',

                '#', '$', '%', '&', '(', ')', '*', '+',
                ',', '-', '.', '/', ':', ';', '<', '=',
                '>', '?', '@', '[', '\\', ']', '^', '_',
                '`', '{', '|', '}', '~', '\'', ' '
        };
        // clang-format on

        std::random_device rd;
        std::mt19937_64 gen(rd());

        std::uniform_int_distribution<> dis_length(min_length, max_length);

        for (int i = 0; i < runs; ++i)
        {
                const int len = dis_length(gen);

                std::string buffer;
                buffer.reserve(len);

                for (int l = 0; l < len; ++l)
                {
                        switch (alphabet)
                        {
                                default:
                                case alphabet_t::alpha:
                                        buffer += int2char[dis_alpha(gen)];
                                        break;
                                case alphabet_t::alnum:
                                        buffer += int2char[dis_alnum(gen)];
                                        break;
                                case alphabet_t::print:
                                        buffer += int2char[dis_print(gen)];
                                        break;
                                case alphabet_t::rnd:
                                        buffer += char(dis_rnd(gen));
                                        break;
                        }
                }

                // write out buffer and the expected \n\0 ending
                os.write(buffer.data(), buffer.size());
                os.write("\n", 1);
        }
        return EXIT_SUCCESS;
}

int main(int argc, char *argv[])
{
        try
        {
                po::options_description desc("SendRnd options");

                // clang-format off
                desc.add_options()
                        ("help",                                                                        "produce help message")
                        ("alphabet",    po::value< std::string >(),     "alpha (default)|alnum|print|random (may contain additional newlines and zeros)")
                        ("min_length",  po::value< int >(),                     "minimum length between newline (Default 128k)")
                        ("max_length",  po::value< int >(),                     "maximum length between newline (Default 1024k)")
                        ("runs",                po::value< int >(),                     "number of runs")
                        ("of",                  po::value< std::string >(),     "output file (use - for stdout = default)")
                        ;
                // clang-format on

                po::variables_map vm;
                po::store(po::parse_command_line(argc, argv, desc), vm);
                po::notify(vm);

                if (vm.count("help"))
                {
                        std::cout << desc << "\n";
                        return EXIT_FAILURE;
                }

                // this values are now needed
                alphabet_t alphabeth = alphabet_t::alpha;
                if (vm.count("alphabet"))
                {
                        std::string value = vm["alphabet"].as<std::string>();
                        if (value == "alpha" || value == "default")
                        {
                                alphabeth = alphabet_t::alpha;
                        }
                        else if (value == "alnum")
                        {
                                alphabeth = alphabet_t::alnum;
                        }
                        else if (value == "print")
                        {
                                alphabeth = alphabet_t::print;
                        }
                        else if (value == "random")
                        {
                                alphabeth = alphabet_t::rnd;
                        }
                        else
                        {
                                std::cerr << "Invalid alphabet mode" << std::endl;
                                return EXIT_FAILURE;
                        }
                }

                // length of the output
                int min_length = 128 * 1024;
                if (vm.count("min_length"))
                {
                        min_length = vm["min_length"].as<int>();
                        if (min_length < 1)
                        {
                                std::cerr << "min_length must be >=1" << std::endl;
                                return EXIT_FAILURE;
                        }
                }

                int max_length = 1024 * 1024;
                if (vm.count("max_length"))
                {
                        max_length = vm["max_length"].as<int>();
                        if (max_length < 1)
                        {
                                std::cerr << "max_length must be >=1" << std::endl;
                                return EXIT_FAILURE;
                        }
                }

                if (max_length < min_length)
                {
                        std::cerr << "min_length must be smaller or equal than max_length"
                                          << std::endl;
                        return EXIT_FAILURE;
                }
                if (min_length < 1)
                {
                        std::cerr << "min_length must >= 1" << std::endl;
                        return EXIT_FAILURE;
                }
                if (max_length < 1)
                {
                        std::cerr << "max_length must >= 1" << std::endl;
                        return EXIT_FAILURE;
                }

                // runs
                int runs = -1;
                if (!vm.count("runs"))
                {
                        std::cout << desc << "\n";
                        return EXIT_FAILURE;
                }
                else
                {
                        runs = vm["runs"].as<int>();
                }
                if (runs < 1)
                {
                        std::cerr << "runs must be >= 1" << std::endl;
                        return EXIT_FAILURE;
                }

                // out
                std::string fname = "-";
                if (vm.count("of"))
                {
                        fname = vm["of"].as<std::string>();
                }
                if (fname != "-")
                {
                        std::ofstream ofs(fname.c_str(), std::ios_base::binary);
                        if (!ofs.is_open())
                        {
                                std::cerr << "Could not open output file: " << fname
                                                  << std::endl;
                                return EXIT_FAILURE;
                        }
                        return run(ofs, alphabeth, min_length, max_length, runs);
                }
                else
                {
                        return run(std::cout, alphabeth, min_length, max_length, runs);
                }
        }
        catch (const std::exception &ex)
        {
                std::cerr << ex.what() << std::endl;
        }

        return EXIT_FAILURE;
}


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

Re: C: fifo lesen

Beitrag von Meillo » 21.08.2018 21:46:37

schorsch_76 hat geschrieben: ↑ zum Beitrag ↑
21.08.2018 20:45:32
So, das erste was ich direkt nach dem compilieren bekomme....

Code: Alles auswählen

georg@M4700:~/Dokumente/Entwicklung/misc/DfDeX11$ gcc set-title.c -lX11  -o set-title                                                                                                                                                                                          
georg@M4700:~/Dokumente/Entwicklung/misc/DfDeX11$ ./set-title                                                                                                                                                                                                                  
Speicherzugriffsfehler                                                                                                                                                                                                                                                         
[...]
fifo konnte nicht geöffnet werden, da er noch nicht existiert. Das sollte durch dieses Programm erledigt werden ;) Auch wird nicht geprüft ob der fifo geöffnet wurde. Nachdem ich den Pfad angepasst und den Fifo erstellt habe, hab ich mal die Funktion getestet.
Da hast du ganz recht. Da wurde schlampig gearbeitet ... schon im Originalprogramm :-P ... ich hab's dann allerdings auch nicht besser gemacht ... war ganz auf Syscalls vs. Stdio fixiert ... hab ja noch nicht mal das ``BUFSIZ'' im fgets()-Aufruf durch ``sizeof(name)'' ersetzt, was man unbedingt tun sollte!

Lass uns diese Korrekturen und Verbesserungen in einem naechsten Schritt machen! Heute nicht mehr, aber ich liefere gerne noch eine Verbesserung meiner Version. Ihr koennt gerne solange schonmal damit anfangen.

Mit einem Programm hab ich das dann mal für 3h durchgetestet und es hat keinen Crash gegeben (nur wenn der Fifo nicht da ist). :D
Coole Sache ... deine Testreihe! :-)


Jetzt waere gut, wenn noch jemand mit Erfahrung und Wissen bestaetigen koennte, dass man FIFOs sehr wohl mit Stdio lesen und schreiben kann. Denn wenn Zufallseingaben kein Fehler erzeugen, heisst das ja noch nicht zwangslaeufig, dass der Code korrekt ist.

Bislang stehe ich noch alleine da, mit der Aussage, dass man auch FIFOs mit Stdio lesen und schreiben sollte -- kann und sollte! Ein paar mehr Wortmeldungen dazu wuerden gut tun.
Use ed once in a while!

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

Re: C: fifo lesen

Beitrag von RobertDebiannutzer » 21.08.2018 22:28:20

Ui, hier war ja heute richtig viel los! 8O
Vielen Dank für euren input und eure Mühe. Ich verstehe noch nicht alles, aber versuche mal, auf ein paar Dinge einzugehen:
Meillo hat geschrieben: ↑ zum Beitrag ↑
20.08.2018 23:55:15
Gibt es Gruende, warum man das nicht so machen, sondern sich low-level mit select(2) und read(2) plagen sollte?
Also ich tendierte zu low-level, weil ich mir dachte, dass das schneller ist und keine Nachteile hat. Mir schien das irgendwie mehr "Unix" und "suckless" zu sein, low-level zu bevorzugen. Aber Dein Code ist natürlich einfacher zu lesen.
Der entscheidende Unterschied zwischen Deinem Code und meinem früheren Code, der nicht funktioniert, ist - wenn ich das richtig verstehe -, dass auch hier die fifo rw geöffnet wird, richtig?
Meillo hat geschrieben: ↑ zum Beitrag ↑
20.08.2018 23:55:15
Auch in deiner Variante gibt es keinen erfolgreichen Exit. Jeder Exit im Code muss von einem Fehler verursacht sein.
Das stimmt, das ist doof.
Ich könnte das zum Einen mit dem aufrufenden Parent-Wrapper-Script lösen, welches im Moment so aussieht:

Code: Alles auswählen

#!/bin/sh

if [ -e /tmp/dwmblocks_fifo_intern ]; then
	if [ ! -p /tmp/dwmblocks_fifo_intern ]; then
		echo "Error: file /tmp/dwmblocks_fifo_intern exists, but is not my fifo. Please check!"
		exit 1
	fi
else
	mkfifo /tmp/dwmblocks_fifo_intern
fi

/opt/scripts/bin/myxd &
myxd_pid=$!

while true; do
	if [ -e /tmp/dwmblocks ]; then
		if [ ! -p /tmp/dwmblocks ]; then
			echo "Error: file /tmp/dwmblocks exists, but is not my fifo. Please check!"
			exit 1
		fi
	else
		mkfifo /tmp/dwmblocks
	fi
	/opt/scripts/barp.sh
done

kill $!
Wie ihr seht, prüfe ich meine fifos in diesem Wrapper-Script. Wenn die fifo für das hier diskutierte Programm, also /tmp/dwmblocks_fifo_intern (die fifo in $HOME ist nur zum Testen) nicht da ist, wird gar nichts gestartet. Dann braucht man ja auch keine Überprüfung am Anfang des Programmes, oder? Wobei, schaden kanns nicht... Was man tatsächlich prüfen müsste, ist, ob die fifo geöffnet wurde. Habe ich mal versucht - s. u.
Statt kill könnte ich hier einen spezifischen kill-string nutzen. Nachteil: Bei jedem Durchlauf muss der string, der von der fifo erhalten wurde, auf diesen spezifischen string überprüft werden. Nicht so elegant...
@schorch_76 hat jetzt dafür eine Lösung vorgestellt, aber ich verstehe leider nicht, wie das jetzt genau ablaufen soll. Könnte man nicht einfach eine Funktion implementieren, die ausgeführt wird, wenn das Programm ein bestimmtes Signal erhält?
Wobei: Was ist mit
Meillo hat geschrieben: ↑ zum Beitrag ↑
21.08.2018 09:45:35
Das Einzige was nicht gemacht wird, ist doch ``fclose(fp)'', was sich aber eruebrigt, wenn sich das Programm beendet, weil dann automatisch alle FDs geschlossen werden und aller Speicher freigegeben wird. Ich verstehe nicht was da nicht aufgeraeumt bleiben koennte.
?

Was auf jeden Fall ein Problem sein könnte, ist, dass die Verbindung zu X nicht richtig geschlossen wird. Deswegen könnte man dann vielleicht doch das Schließen des Programmes genau definieren, statt es einfach abzukillen?
schorsch_76 hat geschrieben: ↑ zum Beitrag ↑
21.08.2018 11:50:23
Zum x window Handle: Da würde ich schon schauen das ich das auf jeden Fall schließen. Die zugrunde liegende IPC zwischen dir und dem XServer hängt (so wie ich das verstehe) dazwischen.
Was genau meinst Du? Ich setze den Name des Root-Windows, dann bereite ich den nächsten Event vor. Es kann ja nicht sein, dass jedes Mal die Verbindung zu X geöffnet, geprüft und geschlossen werden muss. Das machen ja auch Window-Manager nicht...

BTW: Das mit "printf("EOF\\n");" habe ich aus @bluestars Link. Ich werde das nochmal nachschauen...
Mein Programm mit sizeof und open-check:

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+1];
	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("/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);

	for (;;) {
		rc = select(fd + 1, &rfds, NULL, NULL, NULL);
		if (rc > 0) {
			memset (name, 0, sizeof(name));
			ssize_t got = read(fd, name, sizeof(name));
			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);

	exit(1);
}

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

Re: C: fifo lesen

Beitrag von Meillo » 22.08.2018 06:57:36

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
21.08.2018 22:28:20
Meillo hat geschrieben: ↑ zum Beitrag ↑
20.08.2018 23:55:15
Gibt es Gruende, warum man das nicht so machen, sondern sich low-level mit select(2) und read(2) plagen sollte?
Also ich tendierte zu low-level, weil ich mir dachte, dass das schneller ist und keine Nachteile hat. Mir schien das irgendwie mehr "Unix" und "suckless" zu sein, low-level zu bevorzugen. Aber Dein Code ist natürlich einfacher zu lesen.
Erschreckend, was fuer ein verzerrtes Verstaendnis von Unix und Suckless da angekommen ist. :shock:

Gute Lesbarkeit von Code ist eines der wichtigen Ziele von Unix und Suckless (... halt unter Erwartung eines hohen Kenntnisstandes beim Code-Leser).


Low-Level-Code ist mehr Code, was klar dem Ziel von Suckless entgegen steht, die ja besonders explizit nach moeglichst wenig Code streben.

Low-Level-Code besteht zu grossem Teil aus Fehlerbehandlung, was der Lesbarkeit schadet.

Low-Level-Code ist fehleranfaelliger.

Im Zeitalter optimierender Compiler und optimierter Libraries ist es gar nicht unwahrscheinlich, dass eigener Low-Level-Code langsamer ist als wenn man Standardbibliotheken nutzt.


Das ganze Thema ist letztlich schon komplexer und hat viele Zusammenhaenge und Abwaegungen, aber als Faustregel kannst du nehmen: Wenn es weniger Code ist und einfacher zu lesen, dann ist es besser.

Wenn du mehr darueber lernen willst, wie man guten Code schreibt, dann lies ``The Practice of Programming'' von Kernighan und Pike.
Use ed once in a while!

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

Re: C: fifo lesen

Beitrag von schorsch_76 » 22.08.2018 07:57:07

RobertDebiannutzer hat geschrieben: Was genau meinst Du? Ich setze den Name des Root-Windows, dann bereite ich den nächsten Event vor. Es kann ja nicht sein, dass jedes Mal die Verbindung zu X geöffnet, geprüft und geschlossen werden muss. Das machen ja auch Window-Manager nicht...
Dein XOpenDisplay baut eine Verbindung zum X window Manager auf. Hier wird eine Form von IPC verwendet. Da ich nicht weis wie im XServer bzw Client das gehandhabt wird wenn ein offenes Display nicht geschlossen wird, würde ich auf jeden Fall dafür sorgen das beim beenden des Programms ein XCloseDisplay erfolgt.

Jeder Prozess hat ein per ulimit gesetztes Limit was offene File Descriptoren angeht. Das sind, Sockets, Fifos etc pp. Wird das überschritten, verweigert der Kernel dem Prozess neue FD.

Zu Low Level: Ich schreibe auf mehr auf Low Level Ebene Code und halte mich da eben an den POSIX Standard. Hierfür habe ich eben das Buch "The Linux Programming Interface" gekauft. Da hab ich mir je nach Bedarf das ein oder andere Kapitel reingezogen.

@RobertDebiannutzer: Die select schleife ist so exakt wie sie sein soll :)
Zuletzt geändert von schorsch_76 am 22.08.2018 08:52:53, insgesamt 1-mal geändert.

Benutzeravatar
bluestar
Beiträge: 2334
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: 8782
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: 2535
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: 8782
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: 2334
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: 8782
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: 8782
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: 2334
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) {
		...
	}
}

Antworten