Как разветвить процесс пошел?


Я хочу развилить процесс go и получить обратно идентификатор нового процесса (ов), но все, что я могу увидеть в библиотеках exec или os, - это запустить новый процесс.

2 7

2 ответа:

Вы якобы хотите syscall.ForkExec() от syscall пакет.

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

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

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

Одним из решений является использование 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)
    }()
}

Испытание 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
}