Hallo,
ich schreibe an einem eigenen Daemon für Linux, welcher unter anderem in
einem Thread (pthread) die serielle Schnittstelle und in einem weiterem
Thread ein Netzwerksocket bedient. Lasse ich das Programm auf
herkömmliche Art und Weise laufen läuft alles normal. Aber wenn ich mein
Programm mit
./test &
starte laufen die anderen Threads nicht.
Dann habe ich die Mainschleife um ein Fork erweitert:
1
pid_t pid, sid;
2
/* fork off */
3
pid = fork();
4
5
if (pid < 0)
6
exit(EXIT_FAILURE);
7
8
if (pid > 0)
9
exit(EXIT_SUCCESS);
10
11
sid = setsid();
12
if (sid < 0)
13
exit(EXIT_FAILURE);*/
14
15
cout << endl << "Starting daemon..." << endl;
16
close(STDIN_FILENO);
17
18
com = new Com;
19
com->Init();
Das Phänomen ist das gleiche, das Programm an sich läuft, nur die
Threads nicht. Was habe ich nicht beachtet?
Kann man sich das forken sparen wenn man im Startscript den Daemon
einfach mit "&" aufruft?
Stefan
Zum Thema Daemon: http://linux.die.net/man/1/daemonize
Letztlich landest du bei Unix Network Programming, W. Richard Stevens.
Threads ist das aber egal, da muss was anderes im Argen liegen.
Hallo Stefan,
warum willst Du denn das Programm mit './test &' aufrufen? Durch fork()
sollte das Programm in den Hintergrund starten. Wenn es das nicht tut,
dann stimmt etwas nicht.
Ich wechsele immer in ein vorhandenes Verzeichnis und schließe immer
alle terminal files:
// Change working directory
if ((chdir("/")) < 0)
exit(EXIT_FAILURE);
// Detach from terminal
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
Frank
Frank Werner schrieb:> Ich wechsele immer in ein vorhandenes Verzeichnis und schließe immer> alle terminal files:
Ein bissi mehr ist für ein sauberes 'daemonize' schon nötig:
Step 1: fork() so that the parent can exit
Step 2: setsid() to become a process group and session group leader
Step 3: fork() again so the parent (the session group leader) can exit
Step 4: chdir("/") to ensure that our process doesn't keep any directory
in use
Step 5: umask(0) so that we have complete control over the permissions
of anything we write
Step 6: Establish new open descriptors for stdin, stdout and stderr
Michael Reinelt schrieb:> Ein bissi mehr ist für ein sauberes 'daemonize' schon nötig:
was zum glück in Zeiten von SystemD nicht mehr notwendig ist. Da kann
man einfach ein normales Programm schreiben und es als "Service"
einrichten.
Michael Reinelt schrieb:> Step 1: fork() so that the parent can exit> Step 2: setsid() to become a process group and session group leader> Step 3: fork() again so the parent (the session group leader) can exit> Step 4: chdir("/") to ensure that our process doesn't keep any directory> in use> Step 5: umask(0) so that we have complete control over the permissions> of anything we write> Step 6: Establish new open descriptors for stdin, stdout and stderr
Step 5 habe ich unterschlagen, stimmt. Das mache ich auch immer. Step 6
halte ich für überflüssig, weil mein Deamon keine Verbindung zum
terminal benötigt.
Aber Step 3 mußt Du mir erklären!
Stefan schrieb:> ...> starte laufen die anderen Threads nicht.>> Dann habe ich die Mainschleife um ein Fork erweitert:> ...
Habe ich das richtig verstanden?
- dein Programm läuft nicht
- dann hast du es erweitert
- es läuft immer noch nicht
- du willst wissen, warum es nicht läuft
Wieso zeigst du dann nicht das, was nicht läuft (anstatt einer
Erweiterung, die nichts verändert)?
Frank Werner schrieb:> Step 5 habe ich unterschlagen, stimmt. Das mache ich auch immer. Step 6> halte ich für überflüssig, weil mein Deamon keine Verbindung zum> terminal benötigt.
Manche libs haben die eigenheit, mal auf stderr was auzugeben, und es
ist einfach "nett" einen offen fd (und seis /dev/null) dafür zu haben.
> Aber Step 3 mußt Du mir erklären!http://www.unixguide.net/unix/programming/1.7.shtml
mit setsid() mach ich mich zu einem session group leader, der kein
controlling term hat (sich aber eins besorgen könnte). mit dem zweiten
fork nehme ich mir diese Möglichkeit, und damit auch allen potentiellen
Bösartigkeiten, dich ich mir (über eine lib?) eingefangen haben könnte.
Klaus Wachtler schrieb:> Habe ich das richtig verstanden?> - dein Programm läuft nicht
Also ich verstehe diesen Satz anders:
Stefan schrieb:> Lasse ich das Programm auf herkömmliche Art und Weise laufen läuft alles> normal.
Also hat er ein Programm, das ohne & läuft, aber nicht mit &.
Wieso zeigt er das dann nicht? Sondern eine fork-Erweiterung, mit der
das Programm ebenfalls ohne & läuft und nicht mit?
Es geht doch um ein Problem, das schon ohne die fork-Geschichte da ist.
>Habe ich das richtig verstanden?>- dein Programm läuft nicht>- dann hast du es erweitert>- es läuft immer noch nicht>- du willst wissen, warum es nicht läuft
Hast hast natürlich nicht richtig verstanden, würde etwas wenig Sinn
ergeben...
>Wieso zeigst du dann nicht das, was nicht läuft (anstatt einer>Erweiterung, die nichts verändert)?
Weil ich keine Idee habe welchen Teil ich posten soll, der Code ist
schon etwas größer, und wenn ich hier Seitenweise Code poste geht's
schnell nicht mehr ums Thema ;-)
Also grob: Ich habe eine Mainschleife und einige Threads laufen, die
Threads scheinen nicht zu laufen wenn das Program mit ./test &
aufgerufen wird. Deswegen ist die erste Frage ob/was es mit & zu
beachten gibt, oder ob das prinzipiell funktionieren müsste? Oder
nochmal umformuliert, was ist verboten/gefährlich, wenn ein Programm mit
& läuft?
Stefan schrieb:> Also grob: Ich habe eine Mainschleife und einige Threads laufen, die> Threads scheinen nicht zu laufen wenn das Program mit ./test &> aufgerufen wird. Deswegen ist die erste Frage ob/was es mit & zu> beachten gibt, oder ob das prinzipiell funktionieren müsste? Oder> nochmal umformuliert, was ist verboten/gefährlich, wenn ein Programm mit> & läuft?
das sollte eigentlich nichts mit einander zu tun haben.
>das sollte eigentlich nichts mit einander zu tun haben.
Ok, erstmal Danke. Bin ein Stückchen weitergekommen: Wenn ich das
Programm mit ./test & starte scheint es erstmal nichts zu tun zu haben.
Mit jobs sehe ich:
[1]+ Stopped ./test
Wenn ich das Programm mit fg1 in den Vordergrund hole läuft es. Wie
lasse ich das Programm zum laufen ohne dass es im Vordergrund ist?
Stefan, solange Du uns den entscheidenen Code vorenthälst, raten wir
hier nur rum. Kürz Dein Programm solange, daß der Fehler noch enthalten
ist und poste dann den Code hier.
Frank
Frank schrieb:> Stefan, solange Du uns den entscheidenen Code vorenthälst, raten wir> hier nur rum.
wie will man dann mit quellcode erreichen, das es im Hintergrund
gestoppt wird?
> [1]+ Stopped
das scheint mehr auf eine merkwürdige Umgebung hinzudeuten.
Stefan schrieb:> mit bg %1 kommt>> [1]+ Stopped ./test
Dann versucht dein Programm höchstwahrscheinlich mit dem Terminal zu
interagieren (insbesondere von der Standardeingabe zu lesen). Das
solltest Du vermeiden, dann klappt's auch im Hintergrund.
Georg
Peter II schrieb:> wie will man dann mit quellcode erreichen, das es im Hintergrund> gestoppt wird?
z.B.:
int kill (pid_t pid, int sig_nr);
Ich gebe zu, sehr unwahrscheinlich, aber nicht unmöglich :-)
Georgs Theroie klingt plausibler.
Georg Gast 1 schrieb:> Dann versucht dein Programm höchstwahrscheinlich mit dem Terminal zu> interagieren (insbesondere von der Standardeingabe zu lesen). Das> solltest Du vermeiden, dann klappt's auch im Hintergrund.
Deshalb:
Frank Werner schrieb:> // Detach from terminal>> close(STDIN_FILENO);> close(STDOUT_FILENO);> close(STDERR_FILENO);Frank schrieb:> Kürz Dein Programm solange, daß der Fehler noch enthalten> ist und poste dann den Code hier.
>solange Du uns den entscheidenen Code vorenthälst
Wenn ich nur wüsste welcher das ist ;-)
Aber halt, mit Eurer Hilfe: Ich glaube ich habe die Ursache gefunden; in
der mainschleife war ein getchar()! Wenn ich das rausmache und z.B. ein
sleep(1) dafür nehme klappt alles. Ich versteht aber nicht warum auch
die anderen Threads angehalten werden...
Dann gilt es zu entscheiden, welche Methode man besser nimmt wenn das
Programm später direkt vom init.d gestartet werden soll. Im Programm
forken und im Startskript mit ./Test aufrufen, oder nicht forken und im
Startskript mit ./test & aufrufen ?
Stefan schrieb:> Aber halt, mit Eurer Hilfe: Ich glaube ich habe die Ursache gefunden; in> der mainschleife war ein getchar()! Wenn ich das rausmache und z.B. ein> sleep(1) dafür nehme klappt alles. Ich versteht aber nicht warum auch> die anderen Threads angehalten werden...
Wenn Du einen Daemon schreiben willst, dann sollest Du nicht auf
stdin/-out/-err zugreifen ... :-)
Ein Daemon hat keine Ein- oder Ausgabe, genau das macht einen Daemon
aus.
Frank
Stefan schrieb:> Dann gilt es zu entscheiden, welche Methode man besser nimmt wenn das> Programm später direkt vom init.d gestartet werden soll. Im Programm> forken und im Startskript mit ./Test aufrufen, oder nicht forken und im> Startskript mit ./test & aufrufen ?
Das ist wahrschienlich eine Glaubensfrage. Ich bin ein Fan von fork(),
und würde das empfehlen.
Frank
Frank schrieb:> Wenn Du einen Daemon schreiben willst, dann sollest Du nicht auf> stdin/-out/-err zugreifen ... :-)> Ein Daemon hat keine Ein- oder Ausgabe, genau das macht einen Daemon> aus.
naja, stderr für fehler ist nicht so verkehrt.
Peter II schrieb:> naja, stderr für fehler ist nicht so verkehrt.
Doch, in einem Daemon schon. Für Fehler oder sonstiges Log-Gerümpel
sollte man z.B. den syslog verwenden.
Frank schrieb:> Doch, in einem Daemon schon. Für Fehler oder sonstiges Log-Gerümpel> sollte man z.B. den syslog verwenden.
und wenn es dabei einen fehler gibt?
Peter II schrieb:> und wenn es dabei einen fehler gibt?
Dann sollte dieser VOR dem fork() erkannt werden. Nach dem fork() gibt
es keine Konsolenausgaben mehr. Ansonsten ist es kein Daemon.
Frank schrieb:> Dann sollte dieser VOR dem fork() erkannt werden. Nach dem fork() gibt> es keine Konsolenausgaben mehr. Ansonsten ist es kein Daemon.
dann sieht die Praxis aber anders aus, es gibt viele Daemons die auf
stderr fehler und Warnungen rausschreiben.
Peter II schrieb:> dann sieht die Praxis aber anders aus, es gibt viele Daemons die auf> stderr fehler und Warnungen rausschreiben.
Das kann schon sein. Die Praxis ist selten schwarz-weiß :-)
Ein Daemon ist dadurch spezifiert, daß er direktes Kind von init ist,
und damit hat er keine Verbindung mehr zum startenden Terminal (also
auch keine Ausgaben möglich). Nachzulesen z.B. bei Wikipedia oder
enischlägiger Linux-Literatur.
Frank
Danke Euch recht schön erst aml!
Ich denke ich werde die Fork-Methode verwenden, kommt mir aus dem Bauch
raus irgendwie "amtlicher" vor. Noch eine Frage zum stoppen: Ich habe
einen Blick in die Start/Stop Skripte geworfen, dort wird der Dienst mit
start-stop-daemon gestopt. Ich kriege ich den Stopwunsch in meiner
Mainschleife mit? Wird da ein Signal geworfen?
Frank schrieb:> Ein Daemon ist dadurch spezifiert, daß er direktes Kind von init ist,> und damit hat er keine Verbindung mehr zum startenden Terminal (also> auch keine Ausgaben möglich). Nachzulesen z.B. bei Wikipedia oder> enischlägiger Linux-Literatur.
sorgt dann nicht init dafür das stderr auf /var/log/daemon umgeleitet
wird?
Peter II schrieb:> sorgt dann nicht init dafür das stderr auf /var/log/daemon umgeleitet> wird?
Nein, das wäre mir neu. Der Daemon scheibt seine Ausgaben nach syslog
und der Log-Daemon schreibts dann in die passende Datei.
Peter II schrieb:> sorgt dann nicht init dafür das stderr auf /var/log/daemon umgeleitet> wird?
init selbst hat auch keine Ausgabe. Kann es auch gar nicht, es wird
gestartet, bevor es irgendwelche Terminals gibt.
>Jepp, typischerweise ein SIGTERM.
Und untypischerweise? ;-)
Habe mir gerade einen SIGTERM Handler geschrieben, der wird aufgerufen
wenn ich z.B: mit HTOP händisch das Signal sende, mit start-stop-daemon
nicht :-(
Stefan schrieb:> Und untypischerweise? ;-)>> Habe mir gerade einen SIGTERM Handler geschrieben, der wird aufgerufen> wenn ich z.B: mit HTOP händisch das Signal sende, mit start-stop-daemon> nicht :-(
Ich kenne mich mit dem start-stop-daemon nicht wirklich aus, aber die
man-page sagt, daß dieser standardmäßig ein SIGTERM beim Stoppen
schickt. Wenn das nicht passiert, kennt er vielleicht die pid von Deinem
Daemon nicht. Diese ändert sich beim forken nämlich.
Frank schrieb:> kennt er vielleicht die pid von Deinem> Daemon nicht. Diese ändert sich beim forken nämlich.
Deswegen schreibt ein braver daemon auch seie pid nach
/var/run/<name>.pid, und erkennt dabei gleichzeitig ob er schon/noch
läuft.
Aber auch pid-file (sauber) schreiben will gelernt sein :-)
Michael Reinelt schrieb:> Deswegen schreibt ein braver daemon auch seie pid nach> /var/run/<name>.pid, und erkennt dabei gleichzeitig ob er schon/noch> läuft.
auch so eine Krücke die man unter Linux nicht los wird. Was ist wenn man
zweimal den daemon starten will?
Zum glück soll es mit systemd etwas einfache und flexibler werden.
Michael Reinelt schrieb:> Deswegen schreibt ein braver daemon auch seie pid nach> /var/run/<name>.pid, und erkennt dabei gleichzeitig ob er schon/noch> läuft.
Ganz genau :-)
Ein guter Daemon kommt ohne das start-stop-daemon-Geraffel aus :-)
Frank
Mal a weng Offtopic:
Was soll dieses Ge-forke eigentlich?
Das ging mir in Grundlagen-Systemprogrammierung schon nie ein.
Der Prozess erzeugt an einer gewissen Stelle also einen weiteren
Prozess, der genau das gleiche macht.
So weit, so gut.
Aber, in der Praxis will man eigentlich nie dass der Prozess das gleiche
macht (sonst hätte man eher einen Thread gestartet, um meinetwegen
Berechnungen zu parallelisieren).
Deswegen sieht man fast überall, wo fork() auftaucht diese Krücke mit
1
if(pid==...)
2
...
3
else
4
...
Stattdessen will man doch meist dass der neue Prozess was völlig anderes
macht.
Sieht man ja hier auch wieder.
Wie entstand also dieses fork()?
Naheliegend wäre doch, den Syscall eher start() zu nennen und ihm das zu
startende Binary mitzugeben, dass dann als neuer Prozess läuft.
So wies eben auch die Shells (oder Shellen?) machen wenn man z.B.
./myBinary & eintippt.
Klärt mich bitte auf...
Peter II schrieb:> auch so eine Krücke die man unter Linux nicht los wird. Was ist wenn man> zweimal den daemon starten will?
Dann muß der Daemon damit umgehen können.
Haro schrieb:> Wie entstand also dieses fork()?> Naheliegend wäre doch, den Syscall eher start() zu nennen und ihm das zu> startende Binary mitzugeben, dass dann als neuer Prozess läuft.
Dann bräuchtest Du aber ein zweites Binary :-(
Frank schrieb:> Dann bräuchtest Du aber ein zweites Binary :-(
Wieso nicht?
Ist doch schöner (und auch eher Unix-Stil) ein zweites Binary
vorzuhalten als zu forken und dann überall ne Fallunterscheidung
einzubauen (sprich, zwei Programme in ein Binary pressen).
Haro schrieb:> Wie entstand also dieses fork()?> Naheliegend wäre doch, den Syscall eher start() zu nennen und ihm das zu> startende Binary mitzugeben, dass dann als neuer Prozess läuft.
Weil die Unix-Jungs mitgedacht haben, und mit möglichst wenig syscalls
auskommen wollten (im Gegensatz zu anderen Jungs, die sich gedacht
haben, je mehr syscalls, desto besser)
im originalen unix gab es in diesem bereich genau zwei Syscalls: fork
als einzige (!) Möglichkeit, einen neuen Prozess zu starten (mit
ausnahme von init der vom kernel als erster prozess gestartet wird) und
exec() welcehs den aktuellen prozess durch einen neuen austauscht. Mit
nur diesen zwei syscalls und fortschrittlichem Denken ist alles erledigt
was man so braucht.
Haro schrieb:> Naheliegend wäre doch, den Syscall eher start() zu nennen und ihm das zu> startende Binary mitzugeben, dass dann als neuer Prozess läuft.
Naheliegend für "die anderen jungs" :-)
Peter II schrieb:> Zum glück soll es mit systemd etwas einfache und flexibler werden.
Zum Glück?
Die Leute die uns systemd verschaffen, sind auch für die udev-hell
verantwortlich. Ich befürchte fürchterliches (und ich bin nicht alleine
damit)
Die Grundidee ist gut (war sie auch bei udev) was man daraus gemacht hat
/ machen wird, wird sich zeigen. ich befürchte...
Hallo Stefan,
Stefan schrieb:> Also grob: Ich habe eine Mainschleife und einige Threads laufen, die> Threads scheinen nicht zu laufen wenn das Program mit ./test &> aufgerufen wird.
Zunächst einmal ist es natürlich keine gute Idee, ein Programm genauso
zu benennen, wie ein existierendes Programm, das sowohl ein
Systemprogramm ist als auch Builtin in vielen Shells. Der Name "test"
verbietet sich aus diesem Grunde einfach immer -- wenn Dir partout
nichts besseres einfallen will, dann nimm' doch lieber "a.out" oder
meinethalben "karl".
Was Deinen Programmaufruf angeht, so kannst Du das Programm entweder in
einem Terminal-Multiplexer wie GNU screen oder mit dem Befehl "nohup"
("no hangup") starten, einfach so:
1
nohup ./a.out &
Übrigens: die Funktion daemonize(3) existiert -- jedenfalls unter Linux.
;-)
HTH,
Karl
Karl Käfer schrieb:> Übrigens: die Funktion daemonize(3) existiert -- jedenfalls unter Linux.
nicht immer. Und vor allem nicht so dass man sich darauf verlassen
könnte. Und wenn man portable programme schreiben will, schon gar nicht.
POSIX kennt die nicht.
Hallo Frank,
Frank schrieb:> Das ist wahrschienlich eine Glaubensfrage. Ich bin ein Fan von fork(),> und würde das empfehlen.
Nein, das ist eine Wissensfrage. fork() und Threads haben jeweils ihre
ganz spezifischen Eigenheiten, die man kennen und aus denen man für das
jeweilige Problem die richtige Möglichkeit auswählen muß. Pauschale
Empfehlungen sind nur ein Indiz dafür, daß die Unterschiede zwischen den
beiden Ansätzen (und die Abgrenzung zu ähnlichen Lösungsansätzen wie
asynchroner Programmierung und Mikrothreads) nicht richtig verstanden
worden sind.
Liebe Grüße,
Karl
Hallo Peter,
Peter II schrieb:> sorgt dann nicht init dafür das stderr auf /var/log/daemon umgeleitet> wird?
Wohlerzogene Daemons schreiben nicht wild in /var/log/ herum, sondern
loggen mit syslog(3) an den Syslog-Daemon, heute meist rsyslogd(8).
Im äußersten Notfall können Daemons allerdings auch auf /dev/console
direkt etwas auf die Systemkonsole schreiben -- beispielsweise den
fatalen Fehler, wenn sie den Syslog-Daemon nicht öffnen können.
Liebe Grüße,
Karl
Hallo Peter,
Peter II schrieb:> auch so eine Krücke die man unter Linux nicht los wird. Was ist wenn man> zweimal den daemon starten will?
Dann ist das ein Fehler. Üblicherweise belegen Daemons exklusive
Ressourcen wie Domain- oder IP-Sockets, um mit der Außenwelt zu
kommunizieren. Darum wird das Pidfile per Konfiguration festgelegt; wenn
man denselben Daemon zweimal starten will, braucht man daher eine zweite
Konfiguration und also auch ein zweites Pidfile.
Liebe Grüße,
Karl
Hallo Haro,
Haro schrieb:> Was soll dieses Ge-forke eigentlich?
Das dient dazu, den gestarteten Prozess sauber vom kontrollierenden TTY
abzukoppeln und ihn korrekt an den init-Prozess zu hängen.
Liebe Grüße,
Karl
Hallo Michael,
Michael Reinelt schrieb:> Karl Käfer schrieb:>> Übrigens: die Funktion daemonize(3) existiert -- jedenfalls unter Linux.>> nicht immer.
Kennst Du ein Beispiel für ein aktuelles Linux, unter dem diese Funktion
nicht verfügbar ist? Mir ist schon seit Ewigkeiten keins mehr begegnet.
> Und vor allem nicht so dass man sich darauf verlassen> könnte. Und wenn man portable programme schreiben will, schon gar nicht.> POSIX kennt die nicht.
Natürlich. Daß man sie (nicht nur deswegen) nicht benutzen will, ändert
aber trotzdem nichts daran, daß sie existiert.
Es ist eben immer eine Entscheidung, plattformunabhängig am
POSIX-Standard zu entwickeln oder die diversen Erweiterungen
verschiedener UNIX-Systeme und Bibliotheken zu benutzen. Das entscheiden
aber nicht Du oder ich, sondern der jeweilige Entwickler. ;-)
Liebe Grüße,
Karl
Karl Käfer schrieb:>>> Übrigens: die Funktion daemonize(3) existiert -- jedenfalls unter Linux.> Kennst Du ein Beispiel für ein aktuelles Linux, unter dem diese Funktion> nicht verfügbar ist? Mir ist schon seit Ewigkeiten keins mehr begegnet.
Meins (Debian). Wenn dann müsste ich irgendwelche dependencies
reinholen, welche ich u.U. nicht will. Auf die schnelle wüsste ich nicht
mal, wo ich das herkriege. man daemonize() liefert bei mir nix. Google
nach daemonize() ebenfalls nicht. So ultimativ universell dürfte es also
nicht sein.
Hallo Michael,
Michael Reinelt schrieb:> Karl Käfer schrieb:>>>> Übrigens: die Funktion daemonize(3) existiert -- jedenfalls unter>>>> Linux.>>> Kennst Du ein Beispiel für ein aktuelles Linux, unter dem diese Funktion>> nicht verfügbar ist? Mir ist schon seit Ewigkeiten keins mehr begegnet.>> Meins (Debian). Wenn dann müsste ich irgendwelche dependencies> reinholen, welche ich u.U. nicht will. Auf die schnelle wüsste ich nicht> mal, wo ich das herkriege. man daemonize() liefert bei mir nix. Google> nach daemonize() ebenfalls nicht. So ultimativ universell dürfte es also> nicht sein.
Entschuldigung, mein Fehler: die Funktion heißt in Wirklichkeit
daemon(3) und ist in der unistd.h enthalten -- zumindest unter Ubuntu
und Raspbian. Daher würde es mich sehr wundern, wenn es diese Funktion
in Debian nicht gäbe; kannst Du bitte mal schauen?
Die Funktion daemonize() gibt es in diversen Python-Paketen und unter
[1] als Userspace-Programm.
Liebe Grüße,
Karl
[1] http://software.clapper.org/daemonize/
Haro schrieb:> Mal a weng Offtopic:>> Was soll dieses Ge-forke eigentlich?> Das ging mir in Grundlagen-Systemprogrammierung schon nie ein.>> Der Prozess erzeugt an einer gewissen Stelle also einen weiteren> Prozess, der genau das gleiche macht.> So weit, so gut.> Aber, in der Praxis will man eigentlich nie dass der Prozess das gleiche> macht
Warum nicht? Beispiel Webserver: Es sollen 10 Anfragen gleichzeitig
verarbeitet werden können, und aus Sicherheitsgründen sollen die
gegeneinander geschützt sein. Also forkt sich der Webserver einfach
entsprechend oft und fertig.
> (sonst hätte man eher einen Thread gestartet, um meinetwegen> Berechnungen zu parallelisieren).
fork() gab es schon vor Threads. Es war mal der einzige Weg, Dinge zu
parallelisieren. Und auch heute sind Threads nicht immer nötig.
> Wie entstand also dieses fork()?> Naheliegend wäre doch, den Syscall eher start() zu nennen und ihm das zu> startende Binary mitzugeben, dass dann als neuer Prozess läuft.
Dann muß man aber immer dieses neue Binary angeben, und das muß erst
aufwendig gestartet werden. Ein fork() ist dagegen extrem effizient, da
es nur die Page-Table und den Eintrag im Scheduler kopieren muss und
nicht erstmal komplett ein neu gestartetes Programm dynamisch linken und
initialisieren. Wenn man das will, weil das neu auszuführende Programm
ein anderes ist, geht das immer noch mit exec(). Aber der Child-Prozess
hat vor diesem exec() noch die Möglichkeit, Änderungen vorzunehmen, z.B.
stdout an eine Pipe binden, damit die Ausgaben an den Parent-Prozess
geleitet werden und er sie über das andere Ende der Pipe lesen kann.
> So wies eben auch die Shells (oder Shellen?) machen wenn man z.B.> ./myBinary & eintippt.
Wenn du ./myBinary eintippst, passiert auch nichts anderes. Die shell
macht ein fork, der Kindprozess setzt stdin/stdout/stderr korrekt und
führt dann mit exec() das Programm ./myBinary aus. So schafft es die
Shell auch z.B. bei ./myBinary | otherBinary die beiden mit einander zu
"verpipen". Der Parent-Prozess erzeugt die Pipe, forkt und schließt die
Pipe wieder, der eine geforkte child-Prozess schließt das lesende Ende
der Pipe und verbindet das schreibende mit seinem stdout, der andere
schließt das schreibende Ende und verbindet das lesende mit seinem
stdin. Dann machen beide ein exec() und starten jeweils ihr Programm.
Die Pipe ist noch da und verbindet nun die beiden.
Michael Reinelt schrieb:> Google> nach daemonize() ebenfalls nicht.
Dein Google ist kaputt. ;-)
Gleich in der ersten Antwort auf den Eingangspost ist schon der Link
dafür.
Beitrag "Re: Linux fork und Threads?"
OK, zugegeben, das ist daemonize(1) und erst bei "See Also" wird auf
daemon(3) verlinkt. Und du hast schon recht: das Ding ist auf Debian
standardmäßig nicht installiert.
Michael Reinelt schrieb:> Mit> nur diesen zwei syscalls und fortschrittlichem Denken ist alles erledigt> was man so braucht.
Naja, über "Fortschritt" lässt sich hier schreiten.
Durch das fortschrittliche nur-2-syscalls-Design hat man eben in jedem
Programm eben diese Fallunterscheidung, um den Programmfluss im
Child-Prozess anders zu steuern wie im Parent. Und das werden wir wohl
bis zum jüngsten Tag so rumschleppen...
Aber Rolf Magnus hat es immerhin recht gut erklärt, damit kann ich leben
:-)
Haro schrieb:> Durch das fortschrittliche nur-2-syscalls-Design hat man eben in jedem> Programm eben diese Fallunterscheidung, um den Programmfluss im> Child-Prozess anders zu steuern wie im Parent. Und das werden wir wohl> bis zum jüngsten Tag so rumschleppen...
Du solltest schon unterscheiden zwischen dem (sehr klaren und einfachen)
syscall-Interface, und den darüberliegenden Schichten.
Wenn du dich so sehr an fork/exec störst, dann nimm doch einfach
system()
system() macht aber im Hintergrund nix anderes als.... fork & exec
und "rumschleppen" tut man hier nix, im Gegenteil. Aber das muss nicht
jeder verstehen...
Michael Reinelt schrieb:> Wenn du dich so sehr an fork/exec störst, dann nimm doch einfach> system()
Mei, "Stören" ist a weng übertrieben.
Interessieren würds mich, das ist alles.
Ich kenn nur die Beispiele ausm Studium, und da wurde munter rumgeforkt
und jeder Kind-Prozess hat danach was andres gemacht. Das waren also
riesige if-else Konstrukte.
Klar, damals gings darum den Mechanismus zu verstehen. Die Beispiele
hatten natürlich keine praktische Relevanz.
Da ich aber dadurch nur Beispiele kenne wo sich Eltern- und
Kind-Prozesse komplett unterschiedlich verhalten hab ich nie so den Sinn
von fork() im Vergleich zu exec() verstanden.
Michael Reinelt schrieb:> und "rumschleppen" tut man hier nix, im Gegenteil. Aber das muss nicht> jeder verstehen...
Hui, wenn ich mir deine restlichen Beiträge so ins Gedächtnis Rufe dann
bist du jetzt keiner von den Usern hier, die sich das Spucken großer
Töne leisten könnten...
Haro schrieb:> Hui, wenn ich mir deine restlichen Beiträge so ins Gedächtnis Rufe dann> bist du jetzt keiner von den Usern hier, die sich das Spucken großer> Töne leisten könnten...
Sorry wenn das als "spucken großer Töne" bei dir ankam, so war das nicht
gemeint.
trotzdem täte mich interessieren worauf du anspielst?
Michael Reinelt schrieb:> system() macht aber im Hintergrund nix anderes als.... fork & exec
naja, etwas mehr bzw. etwas anderes macht system() es schon.
Es starte tnämlich direkt ein Programm, sondern eine Umgebung
(typischerweise eine Default-Shell) und übergibt ihr die Kommandozeile.
Darin wiederum steht dann vielleicht das gewünschte Programm, oder was
ganz anderes (Alias, Shellfunktion, ...).
Ein einfacherer Ersatz für fork()+exec() wären die spawn...-Funktionen.
Klaus Wachtler schrieb:> Ein einfacherer Ersatz für fork()+exec() wären die spawn...-Funktionen.
Ja, richtig, wobei da muss man auch erstmal die richtige finden... da
gabs mal posix_spawn(), das es aber wieder nicht immer gab, usw...
fork/exec ist aber praktisch immer vorhanden und funktioniert immer
gleich.
Hallo Rolf,
Rolf Magnus schrieb:> fork() gab es schon vor Threads. Es war mal der einzige Weg, Dinge zu> parallelisieren. Und auch heute sind Threads nicht immer nötig.>> [...]>> Ein fork() ist dagegen extrem effizient,
Threading ist deutlich performanter als Forking, die Kommunikation und
Synchronisation von Threads ist einfacher und schneller. Die primären
Vorteile von Forking liegen einerseits in der höheren Portabilität und
andererseits in höherer Stabilität: wenn ein geforkter Prozess crasht,
überlebt der Elternprozeß das, wenn ein Thread abstürzt hingegen nicht.
Liebe Grüße,
Karl
Hallo Michael,
Michael Reinelt schrieb:> Karl Käfer schrieb:>> die Funktion heißt in Wirklichkeit>> daemon(3) und ist in der unistd.h enthalten>> Ja, die hab ich auch.
TY!
LG,
Karl
Karl Käfer schrieb:> Rolf Magnus schrieb:>> fork() gab es schon vor Threads. Es war mal der einzige Weg, Dinge zu>> parallelisieren. Und auch heute sind Threads nicht immer nötig.>>>> [...]>>>> Ein fork() ist dagegen extrem effizient,>> Threading ist deutlich performanter als Forking,
Zur Klarstellung: Die von Rolf beschriebene Effizienz von fork() bezog
sich auf exec(), nicht auf Threads.
Michael Reinelt schrieb:> Sorry wenn das als "spucken großer Töne" bei dir ankam, so war das nicht> gemeint.Michael Reinelt schrieb:> Aber das muss nicht> jeder verstehen...
Dieser Satz kann durchaus falsch aufgefasst werden. Hier im Forum wird
ja oft eher forsch (mum nicht zu sagen, arrogant) aufgetreten,
verwunderlich wärs also nicht.
Wenn das nicht deine Absicht war so nehm ich meine Anschuldigung gerne
zurück.
Sorry fürs Offtopic.
Hallo Michael,
hallo Rolf,
Michael Reinelt schrieb:> Karl Käfer schrieb:>> Rolf Magnus schrieb:>>> fork() gab es schon vor Threads. Es war mal der einzige Weg, Dinge zu>>> parallelisieren. Und auch heute sind Threads nicht immer nötig.>>>>>> Ein fork() ist dagegen extrem effizient,>>>> Threading ist deutlich performanter als Forking,>> Zur Klarstellung: Die von Rolf beschriebene Effizienz von fork() bezog> sich auf exec(), nicht auf Threads.
Entschuldigt bitte, aber jetzt bin ich verwirrt. Die Funktion fork(2)
und die exec*(3)-Familie machen doch völlig verschiedene Dinge, die man
nicht miteinander vergleichen kann.
Liebe Grüße,
Karl
Karl Käfer schrieb:> Entschuldigt bitte, aber jetzt bin ich verwirrt. Die Funktion fork(2)> und die exec*(3)-Familie machen doch völlig verschiedene Dinge, die man> nicht miteinander vergleichen kann.
Ja, schon richtig. Was Rolf vermutlich meinte: wenn ich einen neuen
Prozess starten will, brauche ich das Pärchen fork & exec, da es keinen
eigenen syscall "start" gibt; was aber egal ist, da fork() extrem
effizient ist.
Hallo Michael,
Michael Reinelt schrieb:> Karl Käfer schrieb:>> Entschuldigt bitte, aber jetzt bin ich verwirrt. Die Funktion fork(2)>> und die exec*(3)-Familie machen doch völlig verschiedene Dinge, die man>> nicht miteinander vergleichen kann.>> Ja, schon richtig. Was Rolf vermutlich meinte: wenn ich einen neuen> Prozess starten will, brauche ich das Pärchen fork & exec,
Wenn Du einen neuen Prozeß starten willst, brauchst Du fork(2). Das
erzeugt einen neuen Adressraum, einen neuen Instruktionszeiger etc.,
mithin: einen neuen Prozeß. Das ist billig, weil der eigene Adressraum
wiederverwendet, und erst beim Wiederbeschreiben kopiert wird
(Copy-On-Write).
Die exec*(3)-Familie ersetzt hingegen das aufrufende Prozeßabbild durch
ein anderes. Das neue Abbild wird in den vorhandenen Adreßraum geladen,
und der vorhandene Instruktionszeiger auf die Startadresse gesetzt.
> da es keinen eigenen syscall "start" gibt; was aber egal ist, da fork()> extrem effizient ist.
Ein Systembefehl "start" müßte im Endeffekt dieselben teuren Operationen
durchführen wie fork(2) und die exec*(3)-Familie zusammen, vor allem das
Laden eines neuen Prozeß-Images. Deswegen wäre solch ein Systembefehl
auch nicht schneller als fork + exec. Und warum drei Systembefehle
pflegen, wo zwei ausreichen und zusammen gut funktionieren?
Liebe Grüße,
Karl
Karl Käfer schrieb:> Und warum drei Systembefehle> pflegen, wo zwei ausreichen und zusammen gut funktionieren?Mir musst du das ohnehin nicht erklären :-)
@Stefan
naja, das mit dem Verhalten ist wohl geklärt (getchar()) ... , aber was
ich mich frage, brauchst Du wirklich die zwei Threads für 'seriell' und
'network' und dann hast Du noch was von einer 'mainloop' geschrieben.
Sind also drei Threads. Ich kenne zwar deinen Code bzw. was Du damit
erreichen möchtest nicht, aber ich tippe mal darauf, dass Du darauf
verzichten kannst. Nimm statt dessen (e)poll
http://man7.org/linux/man-pages/man2/poll.2.html