我正在开发一个 Go 程序,该程序在并发循环中使用
syscall.RawSyscall(syscall.SYS_FORK)
创建子进程。每个子进程都需要执行一条应用了特定 seccomp 和 rlimit 限制的命令 (/bin/ls
)。父进程应等待所有子进程完成使用 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 限制正确应用于每个子进程。
我使用 ChatGPT 来帮助我生成具有相同功能的 C++ 版本,并且它按预期工作。这是我用来测试的 C++ 代码。
#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 限制正确应用于子进程的任何指导,我们将不胜感激。
操作系统:Debian 12
内核版本:6.1.0-18-amd64
尝试过
runtime.Gosched()
。没有运气。我还尝试限制它可以产生的并发进程,并使数量低于我拥有的 CPU 核心。它确实有一点帮助,但有时我仍然会再次遇到它。
fork(2)
来 fork 一个 Go 程序:
子进程是使用单个线程创建的,即调用
的线程。fork()
Go 的调度程序在操作系统线程上调度 goroutine,整个机制确实在子进程中停止工作。 C++ 运行时不存在这些问题,因为它默认情况下不会生成多个线程。
syscall.ForkExec
来执行设置 rlimit
并链式调用子进程的包装器。
另请注意,您应该检查函数返回的错误值。