Go 程序卡在并发分叉循环中的 syscall.Wait4

问题描述 投票:0回答:1

我正在开发一个 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 核心。它确实有一点帮助,但有时我仍然会再次遇到它。

go concurrency fork goroutine
1个回答
0
投票

你不能简单地使用

fork(2)
来 fork 一个 Go 程序:

子进程是使用单个线程创建的,即调用

fork()
的线程。

Go 的调度程序在操作系统线程上调度 goroutine,整个机制确实在子进程中停止工作。 C++ 运行时不存在这些问题,因为它默认情况下不会生成多个线程。

您可以调用

syscall.ForkExec
来执行设置
rlimit
并链式调用子进程的包装器。

另请注意,您应该检查函数返回的错误值。

© www.soinside.com 2019 - 2024. All rights reserved.