我正在开发一个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)来Forking Go程序:

子进程是用一个线程创建的,即调用fork()的线程.

Go的调度程序在OS线程上调度goroutine,整个机制在子线程中停止工作.C++运行时不存在这些问题,因为它默认不会产生多个线程.

你可以调用syscall.ForkExec来执行一个包装器,该包装器设置rlimit并链式调用子进程.

还请注意,您应该判断从函数返回的错误值.

Go相关问答推荐

消费者在NAT中是如何实现的

../golang/pkg/mod/github.com/wmentor/lemmas@v0.0.6/processor.go:72:9:未定义:令牌.进程

在Mac中使用uname获取处理器体系 struct 时,在为AMD64构建Go二进制时出现错误结果

如何在S汇编器中更高效地将全局数据加载到霓虹灯寄存器?

如何在Golang中使用ECHO服务器实现Socket.IO服务器

Redis:尽管数据存在,但 rdb.Pipelined 中出现redis:nil错误

使用Go和Operator SDK通过API调用设置Kubernetes Pods的安装步骤

如何将字节文件高效地读入int64切片?

为什么 net/http 不遵守超过 30 秒的超时持续时间?

同一文件上的多个 Arrow CSV 阅读器返回 null

更改多对多连接表的名称

在 .go 文件中运行一个函数和在 Go 模板中调用它有什么区别?

如何使用 Go 代理状态为 OK 的预检请求?

如何将元素从一个切片移动到另一个切片

如何使用 Docker 引擎 SDK 和 Golang 运行 docker 挂载卷

具有两个或多个模型的 GORM 查询

如何在Go中替换符号并使下一个字母大写

GRPC 反向代理混淆 GRPC 和 GRPC-Web

出于某种原因,Golang (Go) AES CBC 密文被填充了 16 个 0x00 字节

Go lang - 惯用的默认后备