You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下如何将mmap内存作为子进程标准输入输出并传递?

在Linux中用共享内存作为子进程的stdin/stdout实现

可行性结论

完全可以通过共享内存实现父进程与子进程的无管道/临时文件通信,将共享内存映射为子进程的标准输入和输出。但不能用std::system,因为它依赖shell执行命令,无法直接替换标准IO的文件描述符,必须用fork()+exec()系列函数手动完成重定向。

核心实现步骤

  1. 创建共享内存对象:用shm_open创建两个共享内存对象(分别对应stdin和stdout),通过ftruncate设置大小,再用mmap映射到进程地址空间。
  2. 父进程预写入输入数据:往stdin对应的共享内存区域写入子进程需要的输入内容,注意调整文件指针到起始位置,确保子进程能从头读取。
  3. Fork子进程并重定向IO:
    • 子进程中关闭原有的STDIN_FILENO(0)和STDOUT_FILENO(1)。
    • 用dup2()将共享内存的文件描述符复制到0和1的位置,替换标准输入输出。
    • 关闭不再需要的共享内存文件描述符,解除内存映射(可选,子进程退出后会自动清理)。
    • 调用exec()系列函数启动目标程序。
  4. 父进程读取输出:等待子进程执行完毕后,直接从stdout对应的共享内存区域读取输出结果。
  5. 清理资源:解除内存映射,关闭文件描述符,用shm_unlink删除共享内存对象,避免残留。

完整代码示例

#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cstring>
#include <iostream>

int main() {
    // 创建stdin共享内存
    const char* shm_stdin_name = "/my_stdin";
    int fd_stdin = shm_open(shm_stdin_name, O_CREAT | O_RDWR, 0666);
    if (fd_stdin == -1) {
        perror("shm_open stdin failed");
        return 1;
    }
    size_t stdin_size = 100;
    if (ftruncate(fd_stdin, stdin_size) == -1) {
        perror("ftruncate stdin failed");
        close(fd_stdin);
        shm_unlink(shm_stdin_name);
        return 1;
    }
    char* p_stdin = (char*)mmap(nullptr, stdin_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_stdin, 0);
    if (p_stdin == MAP_FAILED) {
        perror("mmap stdin failed");
        close(fd_stdin);
        shm_unlink(shm_stdin_name);
        return 1;
    }

    // 创建stdout共享内存
    const char* shm_stdout_name = "/my_stdout";
    int fd_stdout = shm_open(shm_stdout_name, O_CREAT | O_RDWR, 0666);
    if (fd_stdout == -1) {
        perror("shm_open stdout failed");
        munmap(p_stdin, stdin_size);
        close(fd_stdin);
        shm_unlink(shm_stdin_name);
        return 1;
    }
    size_t stdout_size = 200;
    if (ftruncate(fd_stdout, stdout_size) == -1) {
        perror("ftruncate stdout failed");
        munmap(p_stdin, stdin_size);
        close(fd_stdin);
        close(fd_stdout);
        shm_unlink(shm_stdin_name);
        shm_unlink(shm_stdout_name);
        return 1;
    }
    char* p_stdout = (char*)mmap(nullptr, stdout_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_stdout, 0);
    if (p_stdout == MAP_FAILED) {
        perror("mmap stdout failed");
        munmap(p_stdin, stdin_size);
        close(fd_stdin);
        close(fd_stdout);
        shm_unlink(shm_stdin_name);
        shm_unlink(shm_stdout_name);
        return 1;
    }

    // 父进程写入输入数据
    const char* input = "Hello from parent!\n";
    strncpy(p_stdin, input, stdin_size - 1);
    p_stdin[stdin_size - 1] = '\0';
    lseek(fd_stdin, 0, SEEK_SET); // 重置文件指针到开头

    // Fork子进程
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        munmap(p_stdin, stdin_size);
        munmap(p_stdout, stdout_size);
        close(fd_stdin);
        close(fd_stdout);
        shm_unlink(shm_stdin_name);
        shm_unlink(shm_stdout_name);
        return 1;
    } else if (pid == 0) {
        // 子进程重定向stdin
        if (close(STDIN_FILENO) == -1) {
            perror("close stdin failed");
            _exit(1);
        }
        if (dup2(fd_stdin, STDIN_FILENO) == -1) {
            perror("dup2 stdin failed");
            _exit(1);
        }

        // 子进程重定向stdout
        if (close(STDOUT_FILENO) == -1) {
            perror("close stdout failed");
            _exit(1);
        }
        if (dup2(fd_stdout, STDOUT_FILENO) == -1) {
            perror("dup2 stdout failed");
            _exit(1);
        }

        // 清理子进程中不需要的资源
        close(fd_stdin);
        close(fd_stdout);
        munmap(p_stdin, stdin_size);
        munmap(p_stdout, stdout_size);

        // 执行目标程序(示例用cat,读取stdin并输出到stdout)
        execlp("cat", "cat", nullptr);
        perror("execlp failed");
        _exit(1);
    } else {
        // 父进程等待子进程完成
        int status;
        waitpid(pid, &status, 0);
        if (WIFEXITED(status)) {
            std::cout << "子进程退出状态:" << WEXITSTATUS(status) << std::endl;
        } else {
            std::cout << "子进程异常终止" << std::endl;
        }

        // 读取子进程输出
        std::cout << "子进程输出内容:\n" << p_stdout << std::endl;

        // 父进程清理资源
        munmap(p_stdin, stdin_size);
        munmap(p_stdout, stdout_size);
        close(fd_stdin);
        close(fd_stdout);
        shm_unlink(shm_stdin_name);
        shm_unlink(shm_stdout_name);
    }

    return 0;
}

关键注意事项

  • 共享内存权限:shm_open的mode参数要设置合理(如0666),确保子进程能访问;不过因为子进程继承父进程的文件描述符,只要父进程有读写权限即可。
  • 内存同步:使用MAP_SHARED标志确保父子进程对共享内存的修改互相可见。
  • 资源清理:必须调用shm_unlink删除共享内存对象,否则该对象会一直存在于系统中,直到重启或手动删除。
  • std::system的局限性:该函数会启动shell,无法直接将文件描述符重定向为标准IO,因此必须用fork()+exec()的组合。

内容的提问来源于stack exchange,提问作者Антон Сазонов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 00:23:09