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

[1] https://lwn.net/Articles/177897/
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.schorsch_76 hat geschrieben:21.08.2018 08:30:09fgets 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.Meillo hat geschrieben:Gibt es Gruende, warum man das nicht so machen, sondern sich low-level mit select(2) und read(2) plagen sollte?
Sicher?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.
Es ist garantiert, dass der von fgets(3) gelesene String immer Null-terminiert ist.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.
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.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.
fgets(3) liest bis zur Puffergroesse oder bis zum ersten Newline, was immer frueher kommt.schorsch_76 hat geschrieben:21.08.2018 10:33:08fgets kann aber nur die angegebene Puffergröße lesen.
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.)Was passiert wenn du BUFSIZE+x Bytes (ohne 0 oder \n) in den FIFO schreibst?![]()
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.
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)) {
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;
}
Da hast du ganz recht. Da wurde schlampig gearbeitet ... schon im Originalprogrammschorsch_76 hat geschrieben:21.08.2018 20:45:32So, das erste was ich direkt nach dem compilieren bekomme....fifo konnte nicht geöffnet werden, da er noch nicht existiert. Das sollte durch dieses Programm erledigt werdenCode: 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 [...]
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.
Coole Sache ... deine Testreihe!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).![]()
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.Meillo hat geschrieben:20.08.2018 23:55:15Gibt es Gruende, warum man das nicht so machen, sondern sich low-level mit select(2) und read(2) plagen sollte?
Das stimmt, das ist doof.Meillo hat geschrieben:20.08.2018 23:55:15Auch in deiner Variante gibt es keinen erfolgreichen Exit. Jeder Exit im Code muss von einem Fehler verursacht sein.
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 $!
?Meillo hat geschrieben:21.08.2018 09:45:35Das 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 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...schorsch_76 hat geschrieben:21.08.2018 11:50:23Zum 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.
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);
}
Erschreckend, was fuer ein verzerrtes Verstaendnis von Unix und Suckless da angekommen ist.RobertDebiannutzer hat geschrieben:21.08.2018 22:28:20Also 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.Meillo hat geschrieben:20.08.2018 23:55:15Gibt es Gruende, warum man das nicht so machen, sondern sich low-level mit select(2) und read(2) plagen sollte?
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.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...
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.RobertDebiannutzer hat geschrieben:21.08.2018 22:28:20Code: Alles auswählen
... char name[BUFSIZ+1]; ... for (;;) { ... memset (name, 0, sizeof(name)); ssize_t got = read(fd, name, sizeof(name)); --- }
Die Überprüfung des read-Rückgabewertes ist richtig:RobertDebiannutzer hat geschrieben:21.08.2018 22:28:20BTW: Das mit "printf("EOF\\n");" habe ich aus @bluestars Link. Ich werde das nochmal nachschauen...
man 2 read hat geschrieben: On success, the number of bytes read is returned (zero indicates end of file), [...].
On error, -1 is returned [...].
Alles klar, ich überlege, wie ich das machen kann.schorsch_76 hat geschrieben:22.08.2018 07:57:07würde ich auf jeden Fall dafür sorgen das beim beenden des Programms ein XCloseDisplay erfolgt.
Meillo hat geschrieben:22.08.2018 06:57:36Erschreckend, was fuer ein verzerrtes Verstaendnis von Unix und Suckless da angekommen ist.![]()
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%.*}"
Code: Alles auswählen
dbm=$(sed -n 's/^wlp2s0[^-]*\-//;s/\..*//p' /proc/net/wireless)
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
Ja.RobertDebiannutzer hat geschrieben:22.08.2018 11:53:13Meillo hat geschrieben:22.08.2018 06:57:36Erschreckend, was fuer ein verzerrtes Verstaendnis von Unix und Suckless da angekommen ist.![]()
![]()
Ich hatte das immer so verstanden:
- Code soll elegant sein: lesbar, aber auch effizient
Genau anders rum!- im Zweifelsfall zugunsten der Geschwindigkeit die etwas weniger lesbare Variante nehmen
Ken Thompson hat geschrieben: One of my most productive days was throwing away 1,000 lines of code.
Kurz ist meist gut. Kurz ist (wenn man es nicht uebertreibt) oft besser verstaendlich.Doug McIlroy hat geschrieben: The real hero of programming is the one who writes negative code.
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.
Das ist der Grund, weshalb ich lerne...