在 C++ 中实现子进程执行和管道通信:一个实用指南

简介:

在这篇博客中,我们将深入探索如何在 C++ 程序中实现子进程的创建与执行,以及父子进程间的管道通信。核心代码提供了一个框架,用于接收用户命令、创建子进程并利用 execvp 系统调用执行这些命令。此外,我们通过创建管道(pipe),展示了如何在父子进程间安全地传递数据。

本文重点介绍 fork, pipe, 和 execvp 的使用方法,并解释了如何将标准输出和标准错误从子进程重定向到父进程。这一过程涉及对 Unix 系统调用的深入理解,尤其是进程间通信(IPC)的概念。我们的目标是提供一个清晰的指南,帮助读者理解和实现在现代操作系统中广泛应用的进程创建、执行和通信机制。

该教程适合对操作系统、进程管理以及 Unix/Linux 系统编程感兴趣的读者。无论是系统编程新手还是有经验的开发者,都可以通过这个实例加深对进程间通信和命令行界面(CLI)应用编程的理解。

exec相关

一共有六个分三组记忆

execlp -> execvp
execl  -> execv
execle  -> execve

这几个函数的速记

记忆口诀如下:

0、exec是通用前缀
1、 p 代表第一个参数是file 也就是程序的名字比如 ls, 如果名字没带p, 代表第一个参数是path, 比如 /bin/ls
2、 l 代表参数传递的是list, 参数是一个个传递的,最后一个参数为NULL
3、 v 代表argv, 参数是用char* 数组传递的
4、 e 代表环境变量

1、execlp  l->list,p-> file          参数就是(file, a1, a2, a3,..., NULL);
2、execvp  v->argv,p->file.          参数就是file, aegv)
3、execl   l->list,没p -> path       参数就是(path, a1, a2, a3, ..., NULL);
4、execv   v->argv,没p->path         参数(path, argv);
5、execle  l->list,没p->path, e->env 参数(path, a0, a1, ..., NULL, env);
6、execve  v->aegv,没p->path, e->env 参数(path, argv, env);

实战代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 

bool isCommandSupport(std::string command) {
    if (command == "ls" || command == "pwd" || command == "ps" || command == "cat" || command == "grep" ||
        command == "wc" || command == "exit") {
        return true;
    }
    return false;
}

void childTask() {
    while (true) {
        // 获得一整行输入
        std::string line;
        size_t len = 0;
        std::getline(std::cin, line);
        // 进行分割
        char *p = strtok((char *) line.c_str(), " ");
        char *argv[100];
        int argc = 0;
        while (p != NULL) {
            argv[argc++] = p;
            p = strtok(NULL, " ");
        }
        argv[argc] = NULL;
        if (argc == 0) {
            continue;
        }
        if (isCommandSupport(argv[0])) {
            if (strcmp(argv[0], "exit") == 0) {
                exit(0);
            }
        } else {
            printf("command not support\n");
            continue;
        }
        int pipefd[2];
        pid_t pid;
        char buf;
        if (pipe(pipefd) == -1) {
            perror("pipe");
            exit(EXIT_FAILURE);
        }

        pid = fork();
        if (pid == -1) {
            perror("fork");
            exit(EXIT_FAILURE);
        }
        if (pid == 0) {    /* 子进程 */
            close(pipefd[0]);          // 关闭读端
            dup2(pipefd[1], STDOUT_FILENO); // 将标准输出重定向到管道写端
            dup2(pipefd[1], STDERR_FILENO); // 将标准错误也重定向到管道写端
            close(pipefd[1]);          // 关闭原始写端
            execvp(argv[0], argv);
            _exit(EXIT_FAILURE);
        } else {          /* 父进程 */
            close(pipefd[1]);          // 关闭写端
            while (read(pipefd[0], &buf, 1) > 0) {
                write(STDOUT_FILENO, &buf, 1);
            }
            close(pipefd[0]);          // 关闭读端
            int status;
            waitpid(pid, &status, 0);
            if (WIFEXITED(status)) {
            } else if (WIFSIGNALED(status)) {
                printf("child terminated abnormally, signal %d\n", WTERMSIG(status));
            }
        }
    }
}

int main() {
   childTask();
}

你可能感兴趣的:(c++,windows,linux)