Forum: PC-Programmierung Linux fork und Threads?


von Stefan (Gast)


Lesenswert?

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

von Konrad S. (maybee)


Lesenswert?

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.

von Frank W. (wesoft) Benutzerseite


Lesenswert?

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

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

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.

von Frank W. (wesoft) Benutzerseite


Lesenswert?

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!

von Klaus W. (mfgkw)


Lesenswert?

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)?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

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.

von Klaus W. (mfgkw)


Lesenswert?

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.

: Bearbeitet durch User
von Stefan (Gast)


Lesenswert?

>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?

von Peter II (Gast)


Lesenswert?

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.

von Stefan (Gast)


Lesenswert?

>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?

von Gerhard Z. (germel)


Lesenswert?

geht bg %1 nicht?

von Stefan (Gast)


Lesenswert?

mit bg %1 kommt

[1]+  Stopped                 ./test

von Frank (Gast)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

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.

von Georg Gast 1 (Gast)


Lesenswert?

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

von Frank (Gast)


Lesenswert?

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.

von Frank (Gast)


Lesenswert?

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.

von Stefan (Gast)


Lesenswert?

>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 ?

von Frank (Gast)


Lesenswert?

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

von Frank (Gast)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

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.

von Frank (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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?

von Frank (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Frank (Gast)


Lesenswert?

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

von Stefan (Gast)


Lesenswert?

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?

von Peter II (Gast)


Lesenswert?

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?

von Frank (Gast)


Lesenswert?

Stefan schrieb:
> Wird da ein Signal geworfen?

Jepp, typischerweise ein SIGTERM.

von Frank (Gast)


Lesenswert?

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.

von Frank (Gast)


Lesenswert?

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.

von Stefan (Gast)


Lesenswert?

>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 :-(

von Frank (Gast)


Lesenswert?

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.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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 :-)
1
int pid_init(const char *pidfile)
2
{
3
    char tmpfile[256];
4
    char buffer[16];
5
    int fd, len, pid;
6
7
    qprintf(tmpfile, sizeof(tmpfile), "%s.%s", pidfile, "XXXXXX");
8
9
    if ((fd = mkstemp(tmpfile)) == -1) {
10
  error("mkstemp(%s) failed: %s", tmpfile, strerror(errno));
11
  return -1;
12
    }
13
14
    if (fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
15
  error("fchmod(%s) failed: %s", tmpfile, strerror(errno));
16
  close(fd);
17
  unlink(tmpfile);
18
  return -1;
19
    }
20
21
    qprintf(buffer, sizeof(buffer), "%d\n", (int) getpid());
22
    len = strlen(buffer);
23
    if (write(fd, buffer, len) != len) {
24
  error("write(%s) failed: %s", tmpfile, strerror(errno));
25
  close(fd);
26
  unlink(tmpfile);
27
  return -1;
28
    }
29
    close(fd);
30
31
32
    while (link(tmpfile, pidfile) == -1) {
33
34
  if (errno != EEXIST) {
35
      error("link(%s, %s) failed: %s", tmpfile, pidfile, strerror(errno));
36
      unlink(tmpfile);
37
      return -1;
38
  }
39
40
  if ((fd = open(pidfile, O_RDONLY)) == -1) {
41
      if (errno == ENOENT)
42
    continue;  /* pidfile disappared */
43
      error("open(%s) failed: %s", pidfile, strerror(errno));
44
      unlink(tmpfile);
45
      return -1;
46
  }
47
48
  len = read(fd, buffer, sizeof(buffer) - 1);
49
  if (len < 0) {
50
      error("read(%s) failed: %s", pidfile, strerror(errno));
51
      unlink(tmpfile);
52
      return -1;
53
  }
54
55
  buffer[len] = '\0';
56
  if (sscanf(buffer, "%d", &pid) != 1 || pid == 0) {
57
      error("scan(%s) failed.", pidfile);
58
      unlink(tmpfile);
59
      return -1;
60
  }
61
62
  if (pid == getpid()) {
63
      error("%s already locked by us. uh-oh...", pidfile);
64
      unlink(tmpfile);
65
      return 0;
66
  }
67
68
  if ((kill(pid, 0) == -1) && errno == ESRCH) {
69
      error("removing stale PID file %s", pidfile);
70
      if (unlink(pidfile) == -1 && errno != ENOENT) {
71
    error("unlink(%s) failed: %s", pidfile, strerror(errno));
72
    unlink(tmpfile);
73
    return pid;
74
      }
75
      continue;
76
  }
77
  unlink(tmpfile);
78
  return pid;
79
    }
80
81
    unlink(tmpfile);
82
    return 0;
83
}

von Peter II (Gast)


Lesenswert?

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.

von Frank (Gast)


Lesenswert?

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

von Haro (Gast)


Lesenswert?

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...

von Frank (Gast)


Lesenswert?

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.

von Frank (Gast)


Lesenswert?

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 :-(

von Haro (Gast)


Lesenswert?

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).

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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" :-)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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...

von Karl Käfer (Gast)


Lesenswert?

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

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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/

von Rolf M. (rmagnus)


Lesenswert?

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.

von Konrad S. (maybee)


Lesenswert?

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.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl Käfer schrieb:
> die Funktion heißt in Wirklichkeit
> daemon(3) und ist in der unistd.h enthalten

Ja, die hab ich auch.

von Haro (Gast)


Lesenswert?

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 
:-)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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...

von Haro (Gast)


Lesenswert?

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...

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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?

von Klaus W. (mfgkw)


Lesenswert?

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.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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.

von Haro (Gast)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl Käfer schrieb:
> Und warum drei Systembefehle
> pflegen, wo zwei ausreichen und zusammen gut funktionieren?

Mir musst du das ohnehin nicht erklären :-)

von mar IO (Gast)


Lesenswert?

@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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.