简体   繁体   English

C ++外部程序IO

[英]C++ external program IO

I need to run an external program from within a c++ application. 我需要从c ++应用程序中运行外部程序。 I need the output from that program (i want to see it while the program is still running) and it also needs to get input. 我需要该程序的输出(我希望在程序仍在运行时看到它),并且还需要获取输入。

What is the best and most elegant way to redirect the IO? 重定向IO的最佳方法是什么? Should it be running in it's own thread? 它应该在自己的线程中运行吗? Any examples? 有什么例子吗?

It's running on OSX. 它在OSX上运行。

I implemented it like this: 我是这样实现的:

ProgramHandler::ProgramHandler(std::string prog): program(prog){
// Create two pipes
std::cout << "Created Class\n";
pipe(pipe1);
pipe(pipe2);

int id = fork();

std::cout << "id: " << id << std::endl;

if (id == 0)
{
    // In child
    // Close current `stdin` and `stdout` file handles
    close(fileno(stdin));
    close(fileno(stdout));

    // Duplicate pipes as new `stdin` and `stdout`
    dup2(pipe1[0], fileno(stdin));
    dup2(pipe2[1], fileno(stdout));

    // We don't need the other ends of the pipes, so close them
    close(pipe1[1]);
    close(pipe2[0]);

    // Run the external program
    execl("/bin/ls", "bin/ls");
    char buffer[30];
    while (read(pipe1[0], buffer, 30)) {
        std::cout << "Buf: " << buffer << std::endl;
    }
}
else
{
    // We don't need the read-end of the first pipe (the childs `stdin`)
    // or the write-end of the second pipe (the childs `stdout`)
    close(pipe1[0]);
    close(pipe2[1]);


    // Now you can write to `pipe1[1]` and it will end up as `stdin` in the child
    // Read from `pipe2[0]` to read from the childs `stdout`
}

} }

but as an output i get this: 但作为输出,我得到这个:

Created Class 创建的类

id: 84369 id:84369

id: 0 id:0

I don't understand why it s called twice and why it wont fork the first time. 我不明白为什么它叫两次,为什么它不会第一次派生。 What am I doing/understanding wrong. 我在做什么/理解错了。

If using a POSIX system (like OSX or Linux) then you have to learn the system calls pipe , fork , close , dup2 and exec . 如果使用POSIX系统(如OSX或Linux),则必须学习系统调用pipeforkclosedup2exec

What you do is create two pipes, one for reading from the external application and one for writing. 您要做的是创建两个管道,一个用于从外部应用程序读取,另一个用于写入。 Then you fork to create a new process, and in the child process you set up the pipes as stdin and stdout and then call exec which replaces the child process with an external program using your new stdin and stdout file handles. 然后,您fork以创建一个新进程,并在子进程中将管道设置为stdinstdout ,然后调用exec ,该程序使用新的stdinstdout文件句柄用外部程序替换子进程。 In the parent process you can not read the output from the child process, and write to its input. 在父进程中,您无法读取子进程的输出,也无法写入其输入。

In pseudo-code: 用伪代码:

// Create two pipes
pipe(pipe1);
pipe(pipe2);

if (fork() == 0)
{
    // In child
    // Close current `stdin` and `stdout` file handles
    close(FILENO_STDIN);
    close(FILENO_STDOUT);

    // Duplicate pipes as new `stdin` and `stdout`
    dup2(pipe1[0], FILENO_STDIN);
    dup2(pipe2[1], FILENO_STDOUT);

    // We don't need the other ends of the pipes, so close them
    close(pipe1[1]);
    close(pipe2[0]);

    // Run the external program
    exec("/some/program", ...);
}
else
{
    // We don't need the read-end of the first pipe (the childs `stdin`)
    // or the write-end of the second pipe (the childs `stdout`)
    close(pipe1[0]);
    close(pipe2[1]);

    // Now you can write to `pipe1[1]` and it will end up as `stdin` in the child
    // Read from `pipe2[0]` to read from the childs `stdout`
}

Read the manual pages of the system calls for more information about them. 阅读系统调用的手册页,以获取有关它们的更多信息。 You also need to add error checking as all of these system calls may fail. 您还需要添加错误检查,因为所有这些系统调用都可能失败。

Well there is a pretty standard way to do this. 嗯,有一种非常标准的方法可以做到这一点。 In general you would like to fork the process and to close the standard I/O (fd 0,1) of the child. 通常,您希望派生该过程并关闭子级的标准I / O(fd 0,1)。 Before forking have create two pipes, after forking close the standard input and output in the child and connect them to the pipe, using dup. 在分支之前,创建两个管道,在分支之后,关闭子对象中的标准输入和输出,并使用dup将它们连接到管道。

Pseudo code, shows only one side of the connection, I'm sure you can figure out the other side. 伪代码仅显示连接的一侧,我确定您可以弄清楚另一侧。

int main(){
     int fd[2]; // file descriptors
     pipe(fd);

      // Fork child process
      if (fork() == 0){
            char buffer [80];
            close(1);
            dup(fd[1]); // this will take the first free discriptor, the one you just closed. 
            close(fd[1]); // clean up
        }else{
           close(0);
           dup(fd[0]);
           close(fd[0]);
        }    
        return 0;
    }

After you have the pipe set up and one of the parent threads waiting on a select or something, you can call exec for your external tool and have all the data flowing. 设置好管道并让一个父线程等待select或其他操作后,您可以为外部工具调用exec并让所有数据流动。

The basic approach to communicate with a different program on POSIX systems is to setup a pipe() , then fork() your program, close() and dup() file descriptors into the correct location, and finally to exec??() the desired executable. 与POSIX系统上的其他程序进行通信的基本方法是设置pipe() ,然后将程序fork() ,将close()dup()文件描述符放入正确的位置,最后exec??()所需的可执行文件。

Once this is done, you have your two programs connected with suitable streams. 完成此操作后,您就可以将两个程序与适当的流连接起来。 Unfortunately, this doesn't deal with any form of asynchronous processing of the two programs. 不幸的是,这不能处理两个程序的任何形式的异步处理。 That is, it is likely that you either want to access the created file descriptor with suitable asynchronous and non-blocking operations (ie, setup the various file descriptors to be non-blocking and/or access them only when poll() yields results indicating that you can access them). 也就是说,您可能想通过适当的异步和非阻塞操作来访问创建的文件描述符(即,将各种文件描述符设置为非阻塞和/或仅当poll()产生指示以下内容的结果时才访问它们poll()您可以访问它们)。 If there is just that one executable it may be easier to control it from a separate thread, though. 但是,如果只有一个可执行文件,则可能更容易从单独的线程进行控制。

A different approach (and if you are also writing the external program) is to use shared memory. 另一种方法(如果您也正在编写​​外部程序)是使用共享内存。 Something along the lines of (pseudo code) (伪代码)的东西

// create shared memory
int l_shmid = shmget(key, size ,0600 | IPC_CREAT);

if(l_shmid < 0) 
  ERROR

// attach to shared memory
dataptr* ptr = (dataptr*)shmat(l_shmid, NULL, 0600);

// run external program
pid_t l_pid = fork();

if(l_pid == (pid_t)-1)
{
  ERROR

  // detach & delete shared mem
  shmdt(ptr);
  shmctl(l_shmid, 
         IPC_RMID,
         (shmid_ds *)NULL);
  return;
}
else if(l_pid == 0)
{
    // child: 
    execl(path,
          args,
          NULL); 
    return;
}

// wait for the external program to finish
int l_stat(0);
waitpid(l_pid, &l_stat, 0);

// read from shmem
memset(mydata, ..,..);
memcpy(mydata, ptr, ...);

// detach & close shared mem
shmdt(ptr);
shmctl(l_shmid, 
       IPC_RMID,
       (shmid_ds *)NULL);

Your external program can write to shared memory in a similar way. 您的外部程序可以用类似的方式写入共享内存。 No need for pipes & reading/writing/selecting etc. 无需管道和读取/写入/选择等。

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

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