Chcę rozwidlić proces go i przywrócić identyfikator nowego procesu (procesów), ale wszystko, co widzę w bibliotekach exec
lub os
, to rozpoczęcie nowego procesu.Jak rozwidlić proces go?
Odpowiedz
Ty podobno chcą syscall.ForkExec()
z pakietu syscall
.
Zauważ, że fork()
został wynaleziony w momencie, gdy w ogóle nie używano nici, a proces zawsze zawierał tylko jeden wątek wykonania, a zatem rozwidlenie było bezpieczne. W przypadku Go sytuacja jest diametralnie różna, ponieważ w znacznym stopniu wykorzystuje wątki na poziomie systemu operacyjnego, aby zasilać harmonogram tworzenia goroutine.
Teraz ozdób fork(2)
Linux uczyni proces dziecko ma tylko jeden wątek — ten, który nazywa fork(2)
w procesie macierzystym — wśród wszystkich tych, które były aktywne, w tym kilka istotnych wątków używanych przez program Go. Zasadniczo oznacza to, że po prostu nie można oczekiwać, że proces potomny będzie mógł kontynuować wykonywanie kodu Go,, a jedyne, co można rozsądnie zrobić, to jakoś natychmiast wykonać exec(2)
. Zauważ, że to jest to, do czego powinno się używać syscall.ForkExec()
.
A teraz pomyśl jeszcze o problemie. Powiedziałbym, że w dzisiejszych czasach jedyną rzeczą, do której można uzyskać bezpośrednie połączenie z fork(2)
, jest "asynchroniczny asynchroniczny stan migawek stanu procesu" "— rodzaju, powiedzmy, Redis zastosowań. Technika ta polega na tym, że proces potomny dziedziczy wszystkie strony danych pamięci od rodzica, ale system operacyjny używa techniki kopiowania przy zapisie, aby naprawdę nie kopiować wszystkich danych, więc dziecko może po prostu usiąść i zapisać wszystkie struktury danych na dysk, podczas gdy jego rodzic odsuwa się, modyfikując je w swojej własnej przestrzeni adresowej. Każde inne wyobrażalne użycie dla fork()
implikuje natychmiastową exec()
, i do tego właśnie jest exec.Command()
i inne, więc dlaczego po prostu go nie używać?
Jednym z rozwiązań jest użycie exec.Command
wykonanego w jego gorutine.
To, co robi mały projekt 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
pokazuje kilka przykładów:
// 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
}
Jest to rozwidlenie procesu 'ls'. Chcę rozwidlić ten sam proces, którego używam. Jak mogę to zrobić - 'fork' of' C++ '? – Shashwat
@Shashwat nie jestem pewien: widziałem tylko odwrotnie (http://stackoverflow.com/q/22805059/6309) – VonC
Zgaduję, tutaj: brakuje 'return' w bloku' if err! = Zero' z 'Widelca'. –
"Każde inne możliwe użycie dla fork() implikuje natychmiastowe exec(), i do tego służy exec.Command() i wsp., Więc dlaczego po prostu go nie używać?" Jest setrlimit(), konfigurowanie SELinux, chroot(), zamykanie lub konfigurowanie FD przed przekazaniem kontroli - a te muszą się zdarzyć między fork() i exec(), nie? –
Co jeśli "asynchroniczne miganie stanu asynchronicznego procesu" jest * dokładnie * co próbuję zrobić? – cpcallen
@cpcallen, obawiam się, że byłoby to "nie w Go" * w ten sposób. * Esencją problemu jest to, że środowisko wykonawcze Go robi dwie rzeczy z wątkami na poziomie systemu operacyjnego: a) używa ich do zasilania twoich goroutines ; b) wykorzystuje je na własne potrzeby.Ponieważ tylko pojedyncze wątki przeżywają 'fork()', gdy tylko zostanie wznowiony w nowym procesie, będzie on w stanie na wpół do siebie, niemożliwym do prawidłowej pracy. – kostix