C exec/pipe/select 程序 - 缺少子进程的输入

发布于 2024-12-28 20:53:44 字数 2392 浏览 3 评论 0原文

我有一个生成子脚本的程序。子脚本只是将任何输入 1/2 的时间重新发送回 STDOUT 和 STDERR。另一半时间,它会悄悄地消耗掉它。我得到的是对子项的写入结果的计时错误:

Line1: STDOUT Line number 1
Line3: STDERR Line number 1
Line3: STDOUT Line number 3
Getting leftovers
endLine: STDERR Line number 3

第 1 行应该通过相同的 Line1 读取来读取。同样,第 3 行也应该被相同的 Line3 尝试选中。

我试图解决的问题是我希望能够向孩子写入一行数据,检查是否有任何响应并重复。以下是测试程序:

子脚本:

#! /usr/bin/perl 

$| = 1;
select (STDERR);
$|=1;

my $i = 0;
open (F,">> e.out");
select F;
$|=1;
select (STDOUT);

while (<>) {
  chomp;
  print F "($_)\n";
  if ($i++) {
    print "STDOUT $_\n";
    print STDERR "STDERR $_\n";
  }
  $i %= 2;
}
close F;

父 C 程序:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>

main () {
  pid_t pid;
  int p2child[2];
  int c2parent[2];

  pipe (p2child);
  pipe (c2parent);

  if ((pid = fork()) < 0) {
    fprintf (stderr, "Fork error: %s\n", strerror(errno));

/*
  Child Process
*/
  } else if (pid == 0) {
    close (p2child[1]);
    dup2 (p2child[0], STDIN_FILENO);
    close (c2parent[0]);
    dup2 (c2parent[1], STDOUT_FILENO);
    dup2 (c2parent[1], STDERR_FILENO);

    if (execlp ("./e", "./e", 0 )) {
perror("Exec failed");
    }
/*
  Parent Process
*/
  } else {
    FILE* istream;
    FILE* ostream;
    char line[80];
    fd_set set;
    struct timeval timeout;
    int ret;
    int counter;

    close (p2child[0]);
    close (c2parent[1]);

    ostream = fdopen (p2child[1], "w");
    istream = fdopen (c2parent[0], "r");

    for (counter = 0; counter < 5; counter++) {
      fprintf (ostream, "Line number %d\n", counter);
      fflush (ostream);

      do {

        FD_ZERO(&set);
        FD_SET(c2parent[0], &set);
        timeout.tv_sec = 0;
        timeout.tv_usec = 500000;
        ret = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
        if (ret > 0) {
          fgets(line, 80, istream);
          fprintf (stdout, "Line%d: %s", counter, line);
          fflush (stdout);
        }
      } while (ret > 0);
    }

fprintf (stdout, "Getting leftovers\n");
    while (fgets(line, 80, istream)) {
      fprintf (stdout, "endLine: %s", line);
      fflush (stdout);
    }

    close (p2child[1]);
    close (c2parent[0]);

    waitpid (pid, NULL, 0);
  }
  fprintf (stderr, "Exiting\n");
}

I have a program which spawns off a child script. The child script simply respews any input 1/2 the time back to STDOUT and STDERR. The other half the time, it quietly consumes it. What I am getting is a mis-timing of the results from the writes to the child:

Line1: STDOUT Line number 1
Line3: STDERR Line number 1
Line3: STDOUT Line number 3
Getting leftovers
endLine: STDERR Line number 3

Line number 1 should have been read via the same Line1 read. Similarly, Line number 3 should also have been picked up by the same Line3 attempt.

The problem I am trying to solve is that I want to be able to write a line of data to the child, check for any response(s) and repeat. Here are the test programs:

Child script:

#! /usr/bin/perl 

$| = 1;
select (STDERR);
$|=1;

my $i = 0;
open (F,">> e.out");
select F;
$|=1;
select (STDOUT);

while (<>) {
  chomp;
  print F "($_)\n";
  if ($i++) {
    print "STDOUT $_\n";
    print STDERR "STDERR $_\n";
  }
  $i %= 2;
}
close F;

Parent C program:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>

main () {
  pid_t pid;
  int p2child[2];
  int c2parent[2];

  pipe (p2child);
  pipe (c2parent);

  if ((pid = fork()) < 0) {
    fprintf (stderr, "Fork error: %s\n", strerror(errno));

/*
  Child Process
*/
  } else if (pid == 0) {
    close (p2child[1]);
    dup2 (p2child[0], STDIN_FILENO);
    close (c2parent[0]);
    dup2 (c2parent[1], STDOUT_FILENO);
    dup2 (c2parent[1], STDERR_FILENO);

    if (execlp ("./e", "./e", 0 )) {
perror("Exec failed");
    }
/*
  Parent Process
*/
  } else {
    FILE* istream;
    FILE* ostream;
    char line[80];
    fd_set set;
    struct timeval timeout;
    int ret;
    int counter;

    close (p2child[0]);
    close (c2parent[1]);

    ostream = fdopen (p2child[1], "w");
    istream = fdopen (c2parent[0], "r");

    for (counter = 0; counter < 5; counter++) {
      fprintf (ostream, "Line number %d\n", counter);
      fflush (ostream);

      do {

        FD_ZERO(&set);
        FD_SET(c2parent[0], &set);
        timeout.tv_sec = 0;
        timeout.tv_usec = 500000;
        ret = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
        if (ret > 0) {
          fgets(line, 80, istream);
          fprintf (stdout, "Line%d: %s", counter, line);
          fflush (stdout);
        }
      } while (ret > 0);
    }

fprintf (stdout, "Getting leftovers\n");
    while (fgets(line, 80, istream)) {
      fprintf (stdout, "endLine: %s", line);
      fflush (stdout);
    }

    close (p2child[1]);
    close (c2parent[0]);

    waitpid (pid, NULL, 0);
  }
  fprintf (stderr, "Exiting\n");
}

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

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

发布评论

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

评论(1

孤凫 2025-01-04 20:53:44

当您调用 fgets() 时,您从流中读取一行输入,但 stdio 本身可能已读取更多内容并对其进行缓冲;这是你的问题。 select() 返回 0 的时间比您预期的要早,因为先前的 fgets() 调用导致 stdio 吸收所有剩余的输入。作为测试,

                fgets(line, 80, istream);

在选择循环中替换为

                char *p = line;
                do {
                    read(c2parent[0], p, 1);
                } while (*p++ != '\n');

,您应该看到读取和写入处于同步状态,没有剩余输入。

When you call fgets(), you read a line input from the stream but stdio itself may have read more and buffered it; this is your problem. select() is returning 0 earlier than you expect because the prior fgets() call caused stdio to soak up all the remaining input. As a test, replace

                fgets(line, 80, istream);

in the select loop with

                char *p = line;
                do {
                    read(c2parent[0], p, 1);
                } while (*p++ != '\n');

and you should see the reads and writes fall into lockstep with no leftover input.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文