繁体   English   中英

与子进程stdout / stdin通信

[英]Communicate with child process stdout/stdin

我正在尝试与进程通信(它本身写入stdin和stdout以在终端中与用户进行交互)并读取它的stdin并在C中写入它的stdout。

因此,我尝试以编程方式替换shell用户。 一个美国式的例子:想象一下,我想出于某种原因在C中使用VIM。 然后我还需要编写命令(stdout)并从编辑器(stdin)读取东西。

最初我认为这可能是一项微不足道的任务,但似乎没有标准方法。 int system(const char *command); 只执行一个命令并将命令stdin / stdout设置为一个调用进程。

因为这无处可去,我看了FILE *popen(const char *command, const char *type); 但手册页说明:

由于管道根据定义是单向的,因此类型参数可以仅指定读取或写入,而不是两者; 结果流相应地是只读的或只写的。

及其含义:

popen()的返回值在所有方面都是普通的标准I / O流,除非必须用pclose()而不是fclose(3)来关闭它。 写入这样的流写入命令的标准输入; 命令的标准输出 与调用popen()的进程相同 ,除非命令本身改变了它。 相反, 从“popened”流读取读取命令的标准输出命令的标准输入与调用popen()的进程相同

因此,使用popen()并不是完全不可能,但在我看来它非常不优雅,因为我必须解析调用进程的stdout(调用popen()的代码)以解析从popened命令(当使用popen类型'w'时)。

相反,当使用类型'r'调用popen时,我需要写入调用的进程stdin,以便将数据写入popened命令。 在这种情况下,我甚至不清楚这两个进程是否在stdin中接收相同的数据......

我只需要控制程序的stdin和stdout。 我的意思是不能有这样的功能:

stdin_of_process, stdout_of_process = real_popen("/path/to/bin", "rw")
// write some data to the process stdin
write("hello", stdin_of_process)
// read the response of the process
read(stdout_of_process)

所以我的第一个问题是 :实现上层功能的最佳方法是什么?

目前我正在尝试以下方法与另一个进程通信:

  1. int pipe(int fildes[2]);设置两个管道int pipe(int fildes[2]); 一个管道读取进程的stdout,另一个管道写入进程的stdin。
  2. 叉子。
  3. 使用int execvp(const char *file, char *const argv[]);执行我想在forked子进程中与之通信的进程int execvp(const char *file, char *const argv[]);
  4. 在原始过程中使用两个管道与孩子沟通。

这很容易说机器人不是那么简单(至少对我来说)。 我奇怪地设法在一个案例中这样做,但当我试图通过一个更简单的例子来理解我在做什么时,我失败了。 这是我目前的问题:

我有两个程序。 第一个只是每100毫秒写一个递增的数字到它的标准输出:

#include <unistd.h>
#include <time.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

void sleepMs(uint32_t ms) {
    struct timespec ts;
    ts.tv_sec = 0 + (ms / 1000);
    ts.tv_nsec = 1000 * 1000 * (ms % 1000);
    nanosleep(&ts, NULL);
}

int main(int argc, char *argv[]) {
    long int cnt = 0;
    char buf[0x10] = {0};

    while (1) {
        sleepMs(100);
        sprintf(buf, "%ld\n", ++cnt);
        if (write(STDOUT_FILENO, buf, strlen(buf)) == -1)
            perror("write");
    }
}

现在第二个程序应该读取第一个程序的stdout(请记住我最终想要读取和写入一个进程,因此对于上层用例使用popen()的技术正确解决方案可能是正确的在这个特定的情况下,因为我简化了我的实验,只是捕获了底部程序的标准输出。 我希望从底层程序中读取上层程序写入stdout的任何数据。 但它没有读任何东西。 可能是哪里的原因? (第二个问题)。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <time.h>

void sleepMs(uint32_t ms) {
    struct timespec ts;
    ts.tv_sec = 0 + (ms / 1000);
    ts.tv_nsec = 1000 * 1000 * (ms % 1000);
    nanosleep(&ts, NULL);
}

int main() {
    int pipe_fds[2];
    int n;
    char buf[0x100] = {0};
    pid_t pid;

    pipe(pipe_fds);

    char *cmd[] = {"/path/to/program/above", NULL};

    if ((pid = fork()) == 0) { /* child */
        dup2(pipe_fds[1], 1); // set stdout of the process to the write end of the pipe
        execvp(cmd[0], cmd); // execute the program.
        fflush(stdout);
        perror(cmd[0]); // only reached in case of error
        exit(0);
    } else if (pid == -1) { /* failed */
        perror("fork");
        exit(1);
    } else { /* parent */

        while (1) {
            sleepMs(500); // Wait a bit to let the child program run a little
            printf("Trying to read\n");
            if ((n = read(pipe_fds[0], buf, 0x100)) >= 0) { // Try to read stdout of the child process from the read end of the pipe
                buf[n] = 0; /* terminate the string */
                fprintf(stderr, "Got: %s", buf); // this should print "1 2 3 4 5 6 7 8 9 10 ..."
            } else {
                fprintf(stderr, "read failed\n");
                perror("read");
            }
        }
    }
}

是一个(C ++ 11风格)完整的例子。

但是,出于许多实际目的, Expect库可能是一个不错的选择(查看其源代码分发的example子目录中的代码)。

你有正确的想法,我没有时间分析你的所有代码来指出具体的问题,但我想指出一些你可能忽略的关于程序和终端如何工作的事情。

终端作为“文件”的想法是天真的。 像vi这样的程序使用库(ncurses)来发送特殊控制字符(并更改终端设备驱动程序设置)。 例如,vi将终端设备驱动程序本身置于可以一次读取字符的模式中。

以这种方式“控制”像vi这样的程序是非常重要的。

关于你的简化实验......

你的缓冲区是一个字节太小。 另外,请注意IO有时是行缓冲的。 所以,你可能会尝试确保转换换行符(使用printf而不是sprintf / strlen / write ...你已经将stdout连接到你的管道),否则你可能看不到数据,直到命令换行。 我不记得管道是行缓冲的,但值得一试。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM