Я работаю над программой Go, которая создает дочерние процессы, используя syscall.RawSyscall(syscall.SYS_FORK)
в параллельном цикле. Каждый дочерний процесс должен выполнить команду (/bin/ls
) с применением определенных ограничений seccomp и rlimit. Родительский процесс должен дождаться завершения использования syscall.Wait4
всеми дочерними процессами. Однако я столкнулся с проблемой, из-за которой программа зависает на syscall.Wait4(int(r1), nil, 0, nil)
. Вот соответствующая часть кода:
package main
import (
"fmt"
"os"
"os/exec"
"runtime"
"sync"
"syscall"
)
const n = 100
func main() {
var wg sync.WaitGroup
wg.Add(n)
for _ = range n {
go func() {
r1, _, err := syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
if err != 0 {
println("Error: ", err)
panic(err)
}
if r1 == 0 {
// Apply seccomp and rlimit restrictions here
cmd := exec.Command("/bin/ls", "ls")
cmd.Run()
os.Exit(0)
} else {
fmt.Println(int(r1))
syscall.Wait4(int(r1), nil, 0, nil)
wg.Done()
}
}()
}
wg.Wait()
fmt.Println("Done")
}
Если я удалю строку syscall.Wait4
, программа больше не зависнет, но она также не будет ждать завершения всех своих дочерних элементов, что не является желаемым поведением. Мне нужно убедиться, что родительский процесс ожидает завершения всех дочерних процессов и что ограничения seccomp и rlimit правильно применяются к каждому дочернему процессу.
Я использовал
#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include <thread>
const int n = 100;
void forkAndExec() {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child process
execl("/bin/ls", "ls", nullptr);
// If execl is successful, this line won't be executed
perror("execl");
exit(EXIT_FAILURE);
} else {
// Parent process
std::cout << "Child PID: " << pid << std::endl;
int status;
waitpid(pid, &status, 0);
}
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < n; ++i) {
threads.emplace_back(forkAndExec);
}
for (auto &t : threads) {
t.join();
}
std::cout << "Done" << std::endl;
return 0;
}
Может ли кто-нибудь помочь мне понять, почему программа зависает на syscall.Wait4
и как это исправить, чтобы родительский процесс ждал завершения всех дочерних процессов перед выходом? Кроме того, мы будем очень признательны за любые рекомендации по правильному применению ограничений seccomp и rlimit к дочерним процессам.
ОС: Дебиан 12.
Версия ядра: 6.1.0-18-amd64.
Пробовал с runtime.Gosched()
. Не повезло. Также я попытался ограничить количество параллельных процессов, которые он может порождать, и сделать число ниже имеющегося у меня ядра ЦП. Это немного помогает, но иногда я все еще сталкиваюсь с этим снова.
Глядя на C++, кажется, что вы просто хотите разветвить + exec, что уже делает exec.Command
. Вам вообще не нужны системные вызовы.
Вы не можете просто создать форк программы Go с помощью fork(2):
Дочерний процесс создается с помощью одного потока — того, который вызвал
fork()
.
Планировщик Go планирует горутины в потоках ОС, и весь механизм перестает работать в дочернем элементе. Среда выполнения C++ не имеет подобных проблем, поскольку по умолчанию она не создает несколько потоков.
Существует syscall.ForkExec, который вы можете вызвать для выполнения оболочки, которая устанавливает rlimit
и вызывает цепочку дочерних процессов.
Также обратите внимание, что вам следует проверить значения ошибок, возвращаемые функциями.
Вы не можете создать форк программы Go, потому что среда выполнения Go всегда многопоточная.