2015-02-06 15 views

risposta

19

È presumibilmente vogliono syscall.ForkExec() dal pacchetto syscall.

Si noti che fork() è stato inventato nel momento in cui non sono stati utilizzati affatto thread e un processo aveva sempre avuto solo un singolo thread di esecuzione in esso, e quindi la creazione di un fork era sicuro. Con Go, la situazione è radicalmente diversa in quanto utilizza intensamente thread a livello di sistema operativo per alimentare la sua pianificazione di goroutine.

Ora, disadorno fork(2) su Linux farà il processo figlio ha solo il singolo thread — quella che chiama fork(2) nel processo padre — tra tutte quelle che erano attivi, tra cui alcune discussioni cruciali utilizzati dal runtime Go. Fondamentalmente questo significa che semplicemente non può aspettarsi che il figlio procss sia in grado di continuare a eseguire il codice Go, e l'unica cosa che si può sensibilmente fare è in qualche modo eseguire immediatamente exec(2). Si noti che questo è ciò per cui è previsto l'uso di syscall.ForkExec().

E ora pensa al problema ulteriormente. In questi giorni direi che l'unica cosa che una chiamata diretta a fork(2) è utile per "lo snapshot dello stato del processo asincrono best-effort" — per il tipo, ad esempio, Redis. Questa tecnica si basa sul fatto che il processo figlio eredita tutte le pagine di dati di memoria dal suo genitore, ma il sistema operativo usa la tecnica copy-on-write per non copiare veramente tutti quei dati, così il bambino può semplicemente sedersi e salvare tutte le strutture dati su disco mentre il suo genitore sta scompigliando modificandoli nel proprio spazio indirizzo. Ogni altro uso concepibile per fork() implica l'immediato exec(), ed è per questo che è exec.Command() et al, quindi perché non usarlo?

+4

"Ogni altro uso concepibile per fork() implica exec immediato(), ed è per questo che exec.Command() et al è, quindi perché non usarlo?" C'è setrlimit(), impostazione di SELinux, chroot() ing, chiusura o impostazione di FDs prima di passare il controllo - e questi devono avvenire tra fork() ed exec(), no? –

+0

E se "lo snapshot dello stato del processo asincrono best-effort" è * precisamente * cosa sto cercando di fare? – cpcallen

+1

@cpcallen, temo, sarebbe "not in go" * in questo modo. * L'essenza del problema è che il runtime di Go fa due cose ai thread a livello di sistema operativo: a) li usa per alimentare le tue goroutine ; b) li usa per i propri bisogni.Poiché solo un singolo thread sopravvive a un fork() ', non appena viene ripreso in un nuovo processo, sarà in uno stato semi-assed, impossibile da funzionare correttamente. – kostix

0

Una soluzione è utilizzare un exec.Command eseguito nella sua goroutine.

Questo è quello che il piccolo progetto akshaydeo/go_process fa:

// Method to fork a process for given command 
// and return ProcessMonitor 
func Fork(processStateListener ProcessStateListener, cmdName string, cmdArgs ...string) { 
    go func() { 
     processMonitor := &ProcessMonitor{} 
     args := strings.Join(cmdArgs, ",") 
     command := exec.Command(cmdName, args) 
     output, err := command.Output() 
     if err != nil { 
      processMonitor.Err = err 
      processStateListener.OnError(processMonitor, err) 
     }  
     processMonitor.Output = &output 
     processStateListener.OnComplete(processMonitor) 
    }() 
} 

The test process_test.go mostra alcuni esempi:

// Test case for fork 
func TestFork(t *testing.T) { 
    processStateListenerImpl := &ProcessStateListenerImpl{make(chan bool)} 
    Fork(processStateListenerImpl,"ls", "-a") //("ping","192.168.3.141","-c","3") 
    // waiting onto monitor 
    <-processStateListenerImpl.monitor 
} 
+0

Si sta creando un processo 'ls'. Voglio dare lo stesso processo che sto eseguendo. Come posso farlo - 'fork' di' C++ '? – Shashwat

+0

@Shashwat non sono sicuro: ho visto solo il contrario (http://stackoverflow.com/q/22805059/6309) – VonC

+0

Sto indovinando qui: c'è un 'return' mancante nel blocco' if err! = Nil' di 'Fork'. –

Problemi correlati