c - 在 fork 卡在未知位置的读取循环中后从子管道读取

标签 c pipe fork

我正在编写一个程序,其中我 fork 了两次以创建 2 个子进程,这些子进程通过它们的标准输出向父进程发送信息。 children 启动程序使其递归。发生的情况是父进程在读取子进程时卡在读取循环中,但这不是一个永远的循环(有有限的预期输出);它只是不会越过它。

        char response1[256];
        double complex r1[sizeof(workable)/sizeof(float)];
        char* thing = NULL;
        int countC1 = 1;
        int bytesread;
        char* response1Whole = NULL;

        while((bytesread = read(pipefdc1b[0],response1, 256)) > 0){
          fprintf(stderr,"PID:%d -> Reading Bytes from Child 1 Bytes read: %d\n\n", getpid(), bytesread);
          if(bytesread == 256){
            response1Whole = (char*) realloc(response1Whole, countC1*256 );
          }else{
            response1Whole = (char*) realloc(response1Whole, (countC1-1)*256+bytesread);
          }
          strcat(response1Whole, response1);

          countC1++;
          fprintf(stderr,"PID:%d -> Current Builts Input %s\n\n", getpid(), response1Whole);
        }

        fprintf(stderr,"PID:%d -> Child 1 Read\n\n", getpid());

我认为这是卡住的代码片段。它打印出所有“当前内置输入”和“从子 1 读取字节”部分,但从未到达“子 1 读取”部分。值得注意的是,有一个有限且确定的输出,它读取的内容是预期的,程序就停止了。

if(count == 1){
    fprintf(stdout,"%s", st);
    fflush(stdout);
    exit(EXIT_SUCCESS);
}

这是决定输出的代码块。我觉得这里可能存在一些问题,它没有正确终止它的输出流,但它又退出了。

这里是创建管道到“问题”区域的代码:

int pipefdc1a[2];  //p->c1
int pipefdc1b[2];  //c1->p||

int pipefdc2a[2];  //p->c1
int pipefdc2b[2];  //c2->p

if(pipe(pipefdc1a) == -1){
  fprintf(stderr, "Pipe Creation Failed\n");
  exit(EXIT_FAILURE);
}
if(pipe(pipefdc1b) == -1){
  fprintf(stderr, "Pipe Creation Failed\n");
  exit(EXIT_FAILURE);
}
if(pipe(pipefdc2a) == -1){
  fprintf(stderr, "Pipe Creation Failed\n");
  exit(EXIT_FAILURE);
}
if(pipe(pipefdc2b) == -1){
  fprintf(stderr, "Pipe Creation Failed\n");
  exit(EXIT_FAILURE);
}
//fprintf(stderr,"PID:%d -> Pipes Created\n\n", getpid());
fflush(stdout);
pid_t pid = fork();
pid_t pid2;

switch (pid) {
  case -1:

    fprintf(stderr, "Cannot fork!\n");
    exit(EXIT_FAILURE);
  case 0:
    //child 1
    close(pipefdc1a[1]);
    close(pipefdc1b[0]);
    close(pipefdc2a[0]);
    close(pipefdc2a[1]);
    close(pipefdc2b[0]);
    close(pipefdc2b[1]);

    dup2(pipefdc1a[0],STDIN_FILENO);
    close(pipefdc1a[0]);

    dup2(pipefdc1b[1], STDOUT_FILENO);
    close(pipefdc1b[1]);

    execlp("./forkFFT","forkFFT", NULL);

    exit(EXIT_FAILURE);

  default:
    //parent
    fflush(stdout);
    pid2 = fork();
    switch(pid2){
      case -1:
        fprintf(stderr, "Cannot fork!\n");
        exit(EXIT_FAILURE);
      case 0:
        //child 2
        //fprintf(stderr,"PID:%d -> New Child 2 Created\n\n", getpid());
        close(pipefdc1a[1]);
        close(pipefdc1a[0]);
        close(pipefdc1b[0]);
        close(pipefdc1b[1]);
        close(pipefdc2a[1]);
        close(pipefdc2b[0]);

        dup2(pipefdc2a[0],STDIN_FILENO);
        close(pipefdc2a[0]);

        dup2(pipefdc2b[1], STDOUT_FILENO);
        close(pipefdc2b[1]);

        execlp("./forkFFT","forkFFT", NULL);

        exit(EXIT_FAILURE);
      default:
        //parent


        fprintf(stderr, "entered parent switch. PID1: %d, PID2: %d\n\n", pid, pid2);

        write(pipefdc1a[1], stp1, evensize);
        write(pipefdc2a[1], stp2, oddsize);
        close(pipefdc1a[1]);
        close(pipefdc2a[1]);

        char response1[256];
        char response2[256];
        double complex r1[sizeof(workable)/sizeof(float)];
        double complex r2[sizeof(workable)/sizeof(float)];
        char* thing = NULL;
        int countC1 = 1;
        int bytesread;
        char* response1Whole = NULL;



        while((bytesread = read(pipefdc1b[0],response1, 256)) > 0){
          fprintf(stderr,"PID:%d -> Reading Bytes from Child 1 Bytes read: %d\n\n", getpid(), bytesread);
          if(bytesread == 256){
            response1Whole = (char*) realloc(response1Whole, countC1*256 );
          }else{
            response1Whole = (char*) realloc(response1Whole, (countC1-1)*256+bytesread);
          }
          strcat(response1Whole, response1);

          countC1++;
          fprintf(stderr,"PID:%d -> Current Builts Input %s\n\n", getpid(), response1Whole);
        }

        fprintf(stderr,"PID:%d -> Child 1 Read\n\n", getpid());
        char* token = strtok(response1Whole, "\n");
        while(token != NULL){
          double real = (double)strtof(token, &thing);
          if(token == thing){
            fprintf(stderr, "Real Part is NAN\n");
            exit(EXIT_FAILURE);
          }
          fprintf(stderr, "Real Number Read from child 1: %lf. PID1: %d, PID2: %d\n\n", real, pid, pid2);
          double imaginary = 0.0;
          if(thing != NULL){
            fprintf(stderr,"Thing String: %s\n", thing);
            char* check = NULL;
            imaginary = (double)strtof(thing, &check);
            if(check == thing){
              fprintf(stderr, "Imaginary Part is NAN\n");
              exit(EXIT_FAILURE);
            }
          }
          fprintf(stderr, "Imaginary part of number 1: %lf. PID1: %d, PID2: %d\n\n",imaginary, pid, pid2);
          r1[countC1] = real + imaginary*I;
          token = strtok(NULL,"\n");
        }

        fprintf(stderr, "made it 1!\n\n");

        char* thing2 = NULL;
        int countC2 = 0;
        FILE* pipe2File = fdopen(pipefdc2b[0], "r");
        while(fgets(response2, 256, pipe2File) != NULL){
          fprintf(stderr, "Reading from child 2. PID1: %d, PID2: %d\n\n", pid, pid2);

          double real = (double)strtof(response2, &thing2);
          if(response1 == thing2){
            fprintf(stderr, "Real Part is NAN\n");
            exit(EXIT_FAILURE);
          }
          fprintf(stderr, "Real Number Read from child 2: %lf. PID1: %d, PID2: %d\n\n", real, pid, pid2);
          double imaginary = 0.0;
          if(thing2 != NULL && thing2[0] != '\n'){
            fprintf(stderr,"Thing2 String: %s\n", thing2);
            char* check2 = NULL;
            imaginary = (double)strtof(thing2, &check2);
            if(check2 == thing2){
              fprintf(stderr, "Imaginary Part is NAN\n");
              exit(EXIT_FAILURE);
            }
          }
          fprintf(stderr, "Imaginary part of number 2: %lf. PID1: %d, PID2: %d\n\n",imaginary, pid, pid2);
          r2[countC2] = real + imaginary*I;
          countC2++;
        }

        fclose(pipe2File);
        fprintf(stderr, "made it 2!\n\n");

        waitpid(pid, &status, 0);
        if(status == 1){
          fprintf(stderr, "Child did not terminate normally!\n");
          exit(EXIT_FAILURE);
        }
        waitpid(pid2, &status, 0);
        if(status == 1){
          fprintf(stderr, "Child did not terminate normally!\n");
          exit(EXIT_FAILURE);
        }

我为 child 1 和 2 尝试了 2 个不同的循环,因为我在某个时候认为这是问题所在,但事实并非如此。

我对 C 的理解有些局限;如果你能回答并解释那就太好了。

我正在努力遵守“最少要求的代码”规则,但如果您觉得还需要其他东西,请询问,我会添加它们。

最佳答案

正如我在 comment 中猜测的那样,您没有关闭足够多的文件描述符 — 这一次,问题出在父进程中,尽管通常是子进程没有进行足够的关闭。


经验法则:如果您 dup2() 管道的一端连接到标准输入或标准输出,同时关闭 返回的原始文件描述符 pipe() 尽快地。 特别是,您应该在使用任何 exec*() 函数族。

如果您使用以下任一方式复制描述符,则该规则也适用 dup() 或者 fcntl() 使用 F_DUPFD


您通过 4 个 pipe() 调用创建了 8 个文件描述符。在 child 中,您根据经验法则关闭它们。

但是,在尝试从子文件读取到 EOF 之前,您的父文件只关闭了 2 个文件描述符。而且,由于它仍然有管道的写端,它正在从打开的地方读取,所以它永远不会得到 EOF 指示,因为理论上它可以写入管道。

因此,您的父代码如下所示:

    write(pipefdc1a[1], stp1, evensize);
    write(pipefdc2a[1], stp2, oddsize);
    close(pipefdc1a[1]);
    close(pipefdc2a[1]);

应该看起来更像:

    write(pipefdc1a[1], stp1, evensize);
    write(pipefdc2a[1], stp2, oddsize);
    close(pipefdc1a[1]);
    close(pipefdc2a[1]);
    close(pipefdc1a[0]);  // Extra
    close(pipefdc2a[0]);  // Extra
    close(pipefdc1b[1]);  // Extra
    close(pipefdc2b[1]);  // Read all about it!

我认为您需要考虑编写函数来完成工作,而不是将所有代码都放在 main() 程序中,但您仍然需要处理调用 close () 足够频繁了。

关于c - 在 fork 卡在未知位置的读取循环中后从子管道读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53764592/

相关文章:

c - pipe 和 fork 的麻烦

c - 该代码 C 的可能 3 个输出是什么?叉()

c++ - Linux 的 fork 函数与 Windows 的 CreateProcess 相比——复制了什么?

c - 数据包捕获 C 代码不会终止显示捕获的数据包数量

c - 尝试将数组转换为具有空字符的字符串时程序崩溃

将 C 变量转换为 JSON 格式

Python 多处理管道 "Deadlock"

c - Gentoo GCC 出现失败

bash - 最小化 bash 中延迟循环处理的缓冲管道

mysql - 为什么 Python 子进程不能像预期的那样使用 named_pipe?