2015-02-06 2 views

ответ

19

Вы хотите, мол, syscall.ForkExec() из syscall пакета.

Отметьте, что fork() был изобретен в то время, когда нити не использовались вообще, и процесс всегда имел только один поток выполнения в нем, и, следовательно, разветвление было безопасным. С Go ситуация кардинально отличается, так как она сильно использует потоки уровня ОС для управления графиком goroutine.

Теперь прикрасы fork(2) на Linux сделает процесс ребенка есть только один поток — тот, который называется fork(2) в родительском процессе — среди всех тех, которые были активны, в том числе некоторых важных потоков, используемых во время выполнения Go. В основном это означает, что вы просто не можете ожидать, что дочерний procss сможет продолжить выполнение кода Go,, и единственное, что вы можете разумно сделать, это как-то сразу выполнить exec(2). Обратите внимание, что для этого используется syscall.ForkExec().

А теперь подумайте о проблеме дальше. Я бы сказал, что в эти дни единственное, что прямой вызов fork(2) полезен для «моментального снимка состояния асинхронного процесса» с максимальным усилием, — вид, скажем, Redis. Этот метод основан на том, что дочерний процесс наследует все страницы данных памяти от его родителя, но ОС использует метод копирования на запись, чтобы не копировать все эти данные, поэтому ребенок может просто сидеть там и сохранять все структуры данных на диск, в то время как его родительский язык отключается, изменяя их в своем адресном пространстве. Каждое другое возможное использование для fork() подразумевает немедленный exec(), и вот что такое exec.Command() и др., Так почему бы просто не использовать его?

+4

«Любое другое возможное использование fork() подразумевает немедленный exec(), и это то, что exec.Command() и др., Так почему бы просто не использовать его?» Есть setrlimit(), настройка SELinux, chroot() ing, закрытие или настройка FDs перед передачей управления - и это должно произойти между fork() и exec(), no? –

+0

Что делать, если «мгновенный снимок состояния асинхронного процесса с наилучшим усилием» - это именно то, что я пытаюсь сделать? – cpcallen

+1

@cpcallen, боюсь, это будет «не в Go» * таким образом. Суть проблемы в том, что среда выполнения Go делает две вещи для потоков уровня ОС: a) использует их для питания ваших goroutines ; б) использует их для своих нужд.Так как только один поток выживает 'fork()', как только он будет возобновлен в новом процессе, он будет находиться в полуобеспеченном состоянии, что невозможно работать должным образом. – kostix

0

Одним из решений является использование exec.Command, выполненного в его goroutine.

Вот что маленький проект akshaydeo/go_process делает:

// 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 показывает некоторые примеры:

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

Это forking 'ls' процесс. Я хочу разблокировать тот же процесс, который я запускаю. Как я могу это сделать - 'fork'' C++ '? – Shashwat

+0

@Shashwat не уверен: я видел только обратное (http://stackoverflow.com/q/22805059/6309) – VonC

+0

Я угадываю здесь: в блоке 'if err! = Nil' отсутствует' return' «Форк». –

Смежные вопросы