2013-02-19 2 views
6

Ich bin nicht an OS-nahen Programmiertechniken beteiligt, aber wenn es darum geht, in Perl etwas parallel zu machen, ist die Waffe der Wahl fork und wahrscheinlich einige nützliche Module, die darauf aufbauen. Die doc Seite für fork sagt:Was sind die Perl-Techniken, um nur einen Teil des Codes unabhängig voneinander zu trennen?

Does a fork(2) system call to create a new process running the same program at the same point. 

Als Konsequenz eine große Anwendung, die einen viel Speicher verbraucht und fork für eine kleine Aufgabe Aufruf bedeutet, dass es zwei große Perl-Prozesse sein wird, und die zweite wird Ressourcen verschwenden nur um eine einfache Arbeit zu erledigen.

Also, die Frage ist: was zu tun ist (oder wie fork zu verwenden, wenn es die einzige Methode ist), um einen freistehender Teil des Codes zu haben, läuft unabhängig und nur die Ressourcen es braucht raubend?

Nur ein sehr simpel Beispiel:

use strict; 
    use warnings; 

    my @big_array = (1 .. 2000000); # at least 80 MB memory 
    sleep 10; # to have time to inspect easely the memory usage 

    fork(); 
    sleep 10; # to have time to inspect easely the memory usage 

und das Kind Prozess verbraucht 80+ zu MB.

Um klar zu sein: Es ist nicht wichtig ist, zu diesem abgelöst Code oder zu verwenden das Ergebnis irgendwie nur möglich zu sein, zu kommunizieren "hey zu sagen, dass sie mich diese einfache Aufgabe im Hintergrund laufen und lassen Sie mich meine fortsetzen schwere Arbeit in der Zwischenzeit ... und verschwenden Sie nicht meine Ressourcen! "bei einer schweren Perl-Anwendung.

+0

Ein untergeordneter Prozess erbt alle Attribute seiner Eltern. Es gibt keinen wirklichen Weg, das mit Forking zu umgehen, denke ich nicht. – squiguy

+0

Die Verwendung von 'fork' ist nicht zwingend erforderlich. Jede Technik wäre akzeptabel, sogar "System". – ArtM

Antwort

3

fork() zu exec() ist dein Hase hier. Sie fork() um einen neuen Prozess zu erstellen (was eine ziemlich billige Operation ist, siehe unten), dann exec(), um die große perl zu ersetzen, die Sie mit etwas kleiner ausgeführt haben. Das sieht wie folgt aus:

use strict; 
use warnings; 
use 5.010; 

my @ary = (1 .. 10_000_000); 

if (my $pid = fork()) { 
    # parent 
    say "Forked $pid from $$; sleeping"; 
    sleep 1_000; 
} else { 
    # child 
    exec('perl -e sleep 1_000'); 
} 

(@ary wurde nur die Original-Prozess Gedächtnis ein wenig zu füllen verwendet.)

Ich sagte, dass fork() ing war relativ billig, obwohl es den gesamten ursprünglichen Prozess kopiert. Diese Aussagen stehen nicht in Konflikt. Die Jungs, die fork entworfen haben, bemerkten das gleiche Problem. Die Kopie ist faul, dh nur die Bits, die tatsächlich geändert wurden, werden kopiert.

Wenn Sie feststellen, dass die Prozesse miteinander kommunizieren sollen, gelangen Sie in die komplexere Domäne des IPC, über die eine Reihe von Büchern geschrieben wurde.

+0

Wont es kopieren Sie den Speicher für eine sehr kurze Zeit vor dem Aufruf von 'exec()' und dann frei den Speicher, nachdem das Kind beendet? und schließlich, wenn alles konvergiert, um system/exec aufzurufen, dann ist es nicht einmal erforderlich, fork zu verwenden :) .. OK, für 'exec' ist es erforderlich, aber' system ('... &'); 'scheint die Arbeit gut zu machen – ArtM

+2

@ArtM Wie gesagt, es ist eine faule Kopie und 'system ('... &')' * ist * 'fork()' gefolgt von 'exec()', mit einer Shell in der Mitte nur für grins und ein bisschen mehr zerbrechlichkeit zu booten Mehr als eine Möglichkeit, es zu tun, gibt es, mein Weg besser Ich mag. – darch

+1

Unter Linux wird jeder Prozess mit PID> 1 über eine Gabelung (zunächst von init) erstellt. Zum Beispiel, wenn Sie laufen etwas in Ihrem Terminal, bash (oder was auch immer) verzweigt sich, um es auszuführen. Copy-on-write-Semantik stellt sicher, dass dies effizient ist. – rjh

1

Es gibt keine Möglichkeit, nur einen Teil Ihres Prozesses Fußabdruck gabeln, so dass die üblichen Abhilfen kommen auf:

  1. fork bevor Sie speicherintensiven Code in dem übergeordneten Prozess
  2. laufen
  3. einen separaten Prozess starten mit system oder open HANDLE,'|-',.... Natürlich erbt dieser neue Prozess keine Daten von seinem Elternteil, also müssen Sie irgendwie Daten an dieses Kind weitergeben.
3

Ihr gegabelter Prozess verwendet nicht 80 MB residenten Speicher. Ein großer Teil dieses Speichers wird shared - "geliehen" aus dem übergeordneten Prozess, bis entweder das Eltern- oder Kind schreibt, an dem Punkt Kopie-auf-schreiben Semantik wird dazu führen, dass der Speicher tatsächlich kopiert werden.

Wenn Sie dieses Gepäck vollständig aufgeben möchten, führen Sie exec in Ihrer Gabel aus. Dadurch wird der untergeordnete Perl-Prozess durch eine andere ausführbare Datei ersetzt, wodurch der Speicher freigegeben wird. Es ist auch perfekt, wenn Sie nichts zurück zum Elternteil mitteilen müssen.

+0

Nicht sicher, ob lexikalische Variablen * shared * sind. Ich erhalte diese Werte unter den RES/SHR-Spalten für 'ps', während beide Prozesse laufen: 48m/1776 und 46m/156 (1M Array-Elemente). – ArtM

+0

Entschuldigung, es sollte "oben" anstelle von "ps" im obigen Kommentar sein – ArtM

1

fork() wie auf den meisten Betriebssystemen implementiert ist sehr effizient. In der Regel wird eine Technik namens Copy-on-Write verwendet, die besagt, dass Seiten zunächst freigegeben werden, bis der eine oder andere Prozess in sie schreibt. Ein großer Teil Ihres Prozessspeichers wird sowieso nur in Form von readonly gemappten Dateien sein.

Nur weil ein Prozess 80MB vor fork() verwendet, bedeutet das nicht, dass die beiden später 160 verwenden. Zu Beginn wird es nur ein winziger Bruchteil mehr als 80MB sein, bis jeder Prozess beginnt, mehr Seiten zu beschmutzen.

+0

Ich denke, es sollte * sein, bis mindestens ein Prozess beginnt, schmutzig zu werden ... * oder etw logisch äquivalent zu diesem. Nun, das ist mein @big = (1 .. 1000000) 'ist nur ein einfacher generischer Beispielcode, es könnte etwas komplexer und verbreiteter sein Der gesamte Codebereich. Und danke für deine Intervention. – ArtM