Linux fork und Threads?

Gast #4090852
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
Persönliche Seite #4090892
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
(Firma: Brainit GmbH) #4090900
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
Persönliche Seite #4090906
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!
#4090908
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)?
(Firma: Brainit GmbH) #4090925
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.
Gast #4091138
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?
Gast #4091142
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.
Gast #4091163
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?
Gast #4091183
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.
Gast #4091188
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 ?
Gast #4091199
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
Gast #4091201
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
Gast #4091214
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
Gast #4091215
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?
Gast #4091216
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?
Gast #4091240
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 :-(
Gast #4091253
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.
(Firma: Brainit GmbH) #4091257
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
}
Gast #4091262
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.
Gast #4091265
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...
(Firma: Brainit GmbH) #4091304
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" :-)
(Firma: Brainit GmbH) #4091307
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...
Gast #4091367
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
Gast #4091419
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
Gast #4091423
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
Gast #4091453
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
Gast #4091496
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
(Firma: Brainit GmbH) #4091521
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.
Gast #4091561
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/
#4091679
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.
#4091736
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.
Gast #4092107
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 
:-)
(Firma: Brainit GmbH) #4092130
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...
Gast #4092148
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...
(Firma: Brainit GmbH) #4092187
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?
#4092192
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.
Gast #4092227
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
(Firma: Brainit GmbH) #4092246
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.
Gast #4092276
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.
Gast #4093398
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
(Firma: Brainit GmbH) #4093402
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.
Gast #4094749
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
Gast #4097791
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren