C:如何将命名管道重定向到子进程的标准输入/外部

发布于 2024-12-22 23:00:12 字数 2407 浏览 0 评论 0原文

基本上我想在 C 中(并且没有缓冲)做与这个 bash 脚本相同的事情:

#!/bin/sh
cat ./fifo_in | myprogram > ./fifo_out

换句话说,我想执行“myprogram”并将其 stdin 和 stdout 重定向到之前创建的两个管道。

另一个程序将数据输入 fifo_in 并从 fifo_out 读出。

当然,从 ./fifo_in 读取、在父级中缓冲并写入 myprogram 的标准输入(对于 stdout 和 ./fifo_out 则相反)是很容易的,但我认为可能有一种方法可以让“myprogram”直接从 fifo 读取/写入,而无需在父进程中进行缓冲。

编辑:

尤金的答案似乎是正确的,但我无法让它发挥作用。

我在 C 端使用这个函数,这对我来说似乎是正确的:

pid_t execpipes(const char *wd, const char *command, const char *pipename)
{
char pipename_in[FALK_NAMESIZE];
char pipename_out[FALK_NAMESIZE];
strcpy(pipename_in, FALKPATH);
strcat(pipename_in, "/");
strcat(pipename_in, FALK_FIFO_PATH);
strcat(pipename_in, "/");
strncat(pipename_in, pipename, FALK_NAMESIZE-2);
strcpy(pipename_out, pipename_in);
strcat(pipename_out, "R");

pid_t pid;
pid = fork();
if (pid < 0)
{   //Error occured
    perror("fork");
    exit(1);
}
if (pid == 0)
{
    chdir(wd);
    d("execpipes: pipename_in=\"%s\"\n", pipename_in);
    d("          pipename_out=\"%s\"\n", pipename_out);
    freopen(pipename_in,"r",stdin);
    freopen(pipename_out,"w",stdout);

    d("execpipes: command=\"%s\"\n", command);

    execl("/bin/sh", "sh", "-c", command, (char *)NULL); // using execv is probably faster
    // Should never get here
    perror("execl");
    exit(1);
}
return pid;
}

我从 PHP 脚本读取和写入管道(仅发布相关部分):

$pipe_in = fopen($fp.$pipename, "w");
$DEBUG .= "Write to pipe_in\n";
$ret = fwrite($pipe_in, $in);

$pipe_out = fopen($fp.$pipename.'R', "r");
$DEBUG .= "Read from pipe_out\n";
$atext = fread($pipe_out, 200000);  // Program hangs here

程序正确启动,通过 $pipe_in< 接收输入/code> 正确,正确处理数据并且(因为它运行了好几个月)我假设它正确地将数据输出到标准输出,但是当我尝试从 $pipe_out 读取时,它挂起。我知道管道本身设置正确,因为如果我不打开 $pipe_out,程序不会获得任何输入 - 这是有道理的,因为没有 $pipe_out< 的读取器/code> 因此管道并不完整。所以我可以打开 $pipe_out,但我无法从中读取任何内容,这很奇怪。

Edit2:

程序现在可以运行了,谢谢大家 - 由于某种原因,第一个管道必须先关闭,然后才能从第二个管道中读取:

$pipe_in = fopen($fp.$pipename, "w");
$pipe_out = fopen($fp.$pipename.'R', "r");
$DEBUG .= "Write to pipe_in\n";
$ret = fwrite($pipe_in, $in);
fclose($pipe_in);

$DEBUG .= "Read from pipe_out\n";
$atext = fread($pipe_out, 200000);
fclose($pipe_out);

unlink($fp.$pipename);
unlink($fp.$pipename.'R');

Basically I want to do in C (and without buffering) the same as this bash-script:

#!/bin/sh
cat ./fifo_in | myprogram > ./fifo_out

In other words I want to exec "myprogram" and redirect its stdin and stdout to two pipes which have been created previously.

Another program is feeding data into fifo_in and reading out of fifo_out.

Of course it would be easy to just read from ./fifo_in, buffer it in the parent and write to myprogram's stdin (and reverse for stdout and ./fifo_out) but I think there is probably a way to let "myprogram" read/write directly from/to the fifos without buffering in the parent process.

Edit:

Eugen's answer seems to be the correct one, but I cannot get it to work.

I use this function on the C-side, which seems correct to me:

pid_t execpipes(const char *wd, const char *command, const char *pipename)
{
char pipename_in[FALK_NAMESIZE];
char pipename_out[FALK_NAMESIZE];
strcpy(pipename_in, FALKPATH);
strcat(pipename_in, "/");
strcat(pipename_in, FALK_FIFO_PATH);
strcat(pipename_in, "/");
strncat(pipename_in, pipename, FALK_NAMESIZE-2);
strcpy(pipename_out, pipename_in);
strcat(pipename_out, "R");

pid_t pid;
pid = fork();
if (pid < 0)
{   //Error occured
    perror("fork");
    exit(1);
}
if (pid == 0)
{
    chdir(wd);
    d("execpipes: pipename_in=\"%s\"\n", pipename_in);
    d("          pipename_out=\"%s\"\n", pipename_out);
    freopen(pipename_in,"r",stdin);
    freopen(pipename_out,"w",stdout);

    d("execpipes: command=\"%s\"\n", command);

    execl("/bin/sh", "sh", "-c", command, (char *)NULL); // using execv is probably faster
    // Should never get here
    perror("execl");
    exit(1);
}
return pid;
}

I read and write the pipes from a PHP-script (only relevant part posted):

$pipe_in = fopen($fp.$pipename, "w");
$DEBUG .= "Write to pipe_in\n";
$ret = fwrite($pipe_in, $in);

$pipe_out = fopen($fp.$pipename.'R', "r");
$DEBUG .= "Read from pipe_out\n";
$atext = fread($pipe_out, 200000);  // Program hangs here

The program is started correctly, receives the input via $pipe_in correctly, processes the data correctly and (because it ran fine for many months) I assume it puts out the data correctly to stdout, but when I try to read from $pipe_out, it hangs. I know that the pipes themselves are set up correctly because if I don't open $pipe_out, the program does not get any input - which makes sense because there is no reader for $pipe_out and therefore the pipeline is not complete. So I can open $pipe_out, but I cannot read anything from it, which is quite strange.

Edit2:

Program works now, thanks guys - For some reason the first pipe has to be closed before you can read from the second pipe:

$pipe_in = fopen($fp.$pipename, "w");
$pipe_out = fopen($fp.$pipename.'R', "r");
$DEBUG .= "Write to pipe_in\n";
$ret = fwrite($pipe_in, $in);
fclose($pipe_in);

$DEBUG .= "Read from pipe_out\n";
$atext = fread($pipe_out, 200000);
fclose($pipe_out);

unlink($fp.$pipename);
unlink($fp.$pipename.'R');

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绅刃 2024-12-29 23:00:12

我会为 myprogram 编写一个小包装器

freopen("./fifo_in","r",stdin)
freopen("./fifo_out","w",stdout)

(当然不是使用常量路径!),然后 execve myprogram

I'd write a small wrapper for myprogram, that does

freopen("./fifo_in","r",stdin)
freopen("./fifo_out","w",stdout)

(Ofcourse not with constant paths!), then execve myprogram

⊕婉儿 2024-12-29 23:00:12

Korn shell 支持协进程,我认为它可以有效地满足您的要求:从管道读取并写入管道(可以是 C 进程的标准输出和标准输入)

http://www.dartmouth.edu/~rc/classes/ksh/coprocesses.html

Korn shell supports coprocesses, which I think effectively does what you ask: read from a pipe and write to a pipe (which can be stdout and stdin of a C process)

http://www.dartmouth.edu/~rc/classes/ksh/coprocesses.html
绝對不後悔。 2024-12-29 23:00:12

怎么样

myprogram < ./fifo_in > ./fifo_out

至于摆脱缓冲:由于您的程序直接读取/写入管道,因此缓冲不会对您造成伤害。

重要的一点是写入 fifo_in 的进程应该正确刷新,这样您就不必等待。您的输出也是如此:“工作单元”完成后,刷新您的 stdout ,这将使数据可供读取输出管道的任何人使用。

但是您无法在 myprogram 中执行任何操作来使 fifo_in 的编写器刷新其缓冲区。

[编辑] 要从 C 语言(无需 shell 的帮助)执行此操作,请使用如下代码:(

- Put the names of the two pipes into local variables on the stack
- Call `fork()`. If that returns '0', then open the two fifos with `freopen()` [like Eugen suggested][1]
- Call `execve` to launch the real exec.

简而言之)这就是 shell 在运行命令时所做的事情。确保父进程(fork() 返回 PID != 0 的进程)处理信号 SIGCHLD

How about

myprogram < ./fifo_in > ./fifo_out

?

As for getting rid of the buffering: Since your program directly reads/writes the pipes, the buffering shouldn't hurt you.

An important point is that the process which writes fifo_in should flush properly so you don't have to wait. The same goes for your output: As soon as a "work unit" is complete, flush your stdout which will make the data available to whoever reads the output pipe.

But you can't do anything in myprogram to make the writer of fifo_in flush its buffers.

[EDIT] To do this from C (without the help of a shell), use code like this:

- Put the names of the two pipes into local variables on the stack
- Call `fork()`. If that returns '0', then open the two fifos with `freopen()` [like Eugen suggested][1]
- Call `execve` to launch the real exec.

That's (in a nutshell) what the shell is doing when it runs commands. Make sure the parent process (the one where fork() returns a PID != 0) handles the signal SIGCHLD

绝影如岚 2024-12-29 23:00:12

也许您正在寻找命名管道?例如:

   mkfifo fifo_in

作为 my_program.c 的测试存根,通过缓冲的 stdin 读取 fifo_in

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
   char buf[80];
   if (!freopen("./fifo_in", "r", stdin)) {
      perror("freopen");
      exit(EXIT_FAILURE);
   }
   while (!ferror(stdin)) {
      while (fgets(buf, sizeof buf, stdin))
         fputs(buf, stdout);
      sleep(1);
   }
   return 0;
}

然后作为编写器的测试,使用 bash shell:

for x in {1..10}; do 
   echo $x 
   echo $x >> fifo_in 
   sleep 1 
done

注意:

  • 我更喜欢使用无缓冲 I/O。
  • 至少在我的机器上,作者会阻塞,直到有读者为止。
  • 在这个示例中,读者无法判断作者何时完成。

Perhaps you are looking of a named pipe? For example:

   mkfifo fifo_in

As a test stub for my_program.c, to read fifo_in via the buffered stdin:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
   char buf[80];
   if (!freopen("./fifo_in", "r", stdin)) {
      perror("freopen");
      exit(EXIT_FAILURE);
   }
   while (!ferror(stdin)) {
      while (fgets(buf, sizeof buf, stdin))
         fputs(buf, stdout);
      sleep(1);
   }
   return 0;
}

Then as a test for the writer, using the bash shell:

for x in {1..10}; do 
   echo $x 
   echo $x >> fifo_in 
   sleep 1 
done

Notes:

  • I'd prefer to use unbuffered I/O.
  • The writer, at least on my machine, blocks until there is a reader.
  • The reader, in this sample, cannot tell when the writer is finished.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文