3

我正在编写一个程序,将行号放在给定文本文档的每一行前面。我是通过使用管道来编写的,因为这样做的目的是了解管道的工作原理。

问题:当输入文件中有少量数据(大约150行)时,程序运行良好。当我输入更多行(200+)时,输出文件一直很好,直到第 153 行,然后它重复最后 40 行左右。

当我输入更多行(6000)时,程序永远不会结束,输出文件保持为空。

我不知道哪里出了问题,所以如果能得到一些帮助会很棒。

代码 :

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <wait.h>
#include <limits.h>

void Parent(int *ipPX, int *ipPY);
void Child(int *IpPX, int *IpPY);

FILE *ifp, *ofp; //input and output file
void fileHandling (char *argv[2]);

int main(int argc, char *argv[]) {
    pid_t pid;
    int pdX[2], pdY[2];

    fileHandling(argv);
    pipe(pdX);
    pipe(pdY); //make new file descriptors in table
    switch (pid = fork()) {
        case 0: Child(pdX, pdY);
            break;
        case -1: perror("Error creating child");
            exit(1);
        default: Parent(pdX, pdY);
    }
    exit(EXIT_SUCCESS);
}

void Parent(int *ipPX, int *ipPY) {
    char *line = NULL;
    size_t len = 0;
    ssize_t readedLines;
    char buf[LINE_MAX];
    int n;

    close(ipPX[0]);close(ipPY[1]); //close not used fd's

    while ((readedLines = getline(&line, &len, ifp)) > 0) {
        write(ipPX[1], line, readedLines); //read from input file and write to cat process
    }
    close(ipPX[1]); //done with this fd

    while ((n = read(ipPY[0], buf, sizeof (buf))) > 0) {
        fprintf(ofp, "%s", buf); //read from cat process and print to file
    }
    close(ipPY[0]); //done with this fd

    wait(0); //wait for child to finish
}

void Child(int *ipPX, int *ipPY) {
    close(ipPX[1]);
    close(ipPY[0]);
    close(0);
    dup(ipPX[0]);
    close(ipPX[0]);
    close(1);
    dup(ipPY[1]);
    close(ipPY[1]);
    execlp("cat", "cat", "-n", NULL);
    perror("Execlp error"); //not reached unless execlp fails
    exit(1);
}

void fileHandling (char *argv[2]){
    char *outputFile; //name of output file 

     if (!(ifp = fopen(argv[1], "r"))) {
        fprintf(stderr, "Error opening input file");
        exit(1);
    }
     if (argv[2] == NULL) {
         outputFile = "LineNumbersOutput";
     }
     else {
         outputFile = argv[2];
     }
    if (!(ofp = fopen(argv[2], "w+"))) {
        fprintf(stderr, "Error opening output file");
        exit(1);
    }
}

更新:现在我添加了第三个进程,但是在之前的代码中添加的行号已经消失了。现在输出完成了(不管文件有多大)

感谢所有的大力帮助!

新代码:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <wait.h>
#include <limits.h>

void Parent(int *ipPX, int *ipPY);
void CatProcess(int *IpPX, int *IpPY);
void WriteToFile(int *ipPX, int *ipPY);

FILE *ifp, *ofp; //input and output file
void FileHandling (char *argv[2]);

int main(int argc, char *argv[]) {
    pid_t catpid;
    pid_t writeToFile;

    int pdX[2], pdY[2];

    FileHandling(argv);
    pipe(pdX);
    pipe(pdY); //make new file descriptors in table

    //first child 
    switch (catpid = fork()) {
        case 0: CatProcess(pdX, pdY);
            break;
        case -1: perror("Error creating Cat child");
            exit(1);
        default: 
        writeToFile = fork();
    }

    //second child
    switch (writeToFile){
        case 0: WriteToFile(pdX, pdY);
            break;
        case -1: perror("Error creating writeToFile child");
            exit(1);
        default: 
            Parent(pdX, pdY);
    }

    exit(EXIT_SUCCESS);
}

void Parent(int *ipPX, int *ipPY) {
    char *line = NULL;
    size_t len = 0;
    ssize_t readedLines;

    close(ipPX[0]);close(ipPY[1]); close(ipPY[0]);//close not used fd's

    while ((readedLines = getline(&line, &len, ifp)) > 0) {
        write(ipPX[1], line, readedLines); //read from input file and write to cat process
    }
    close(ipPX[1]); //done with this fd

    wait(0); //wait for child to finish
}

void CatProcess(int *ipPX, int *ipPY) {
    close(ipPX[1]);
    close(ipPY[0]);
    close(0);
    dup(ipPX[0]);
    close(ipPX[0]);
    close(1);
    dup(ipPY[1]);
    close(ipPY[1]);
    execlp("cat", "cat", "-n", NULL);
    perror("Execlp error"); //not reached unless execlp fails
    exit(1);
}

void FileHandling (char *argv[2]){
    char *outputFile; //name of output file 

     if (!(ifp = fopen(argv[1], "r"))) {
        fprintf(stderr, "Error opening input file");
        exit(1);
    }
     if (argv[2] == NULL) {
         outputFile = "LineNumbersOutput";
     }
     else {
         outputFile = argv[2];
     }
    if (!(ofp = fopen(argv[2], "w+"))) {
        fprintf(stderr, "Error opening output file");
        exit(1);
    }
}

void WriteToFile(int *ipPY,int *ipPX){
   char buf[LINE_MAX];
   int n;
   close(ipPY[1]); close(ipPX[0]); close(ipPX[1]); //close unused fd's
   close(0); dup(ipPY[0]); close(ipPY[0]); //redirect stdin
   while ((n = read(0, buf, sizeof (buf))) > 0) {
        fprintf(ofp, "%s", buf); //read from cat process and print to file
    }
 }

画了一张图让自己看清楚(认为是正确的) 在此处输入图像描述

4

1 回答 1

3

更新的答案

新代码几乎可以工作了——但有一个微小(但很关键)的不一致:

void Parent(int *ipPX, int *ipPY) { ... }
void CatProcess(int *ipPX, int *ipPY) { ... }
void WriteToFile(int *ipPY,int *ipPX){ ... }

中的代码WriteToFile()是在假设参数按顺序编写的情况下编写的ipPX, ipPY。当您更改定义时,代码运行良好。我最终通过打印出三个函数中的文件描述符分配并发现(这并不难!)描述符在WriteToFile().

您还应该修复fprintf().

请注意,如果没有要检查的代码,调试它的可能性为零。这是我(轻微)修改的工作代码。

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

void Parent(int *ipPX, int *ipPY);
void CatProcess(int *IpPX, int *IpPY);
void WriteToFile(int *ipPX, int *ipPY);

FILE *ifp, *ofp; //input and output file
void FileHandling(int argc, char *argv[2]);

int main(int argc, char *argv[]) {
    pid_t catpid;
    pid_t writeToFile;
    int pdX[2], pdY[2];

    FileHandling(argc, argv);
    pipe(pdX);
    pipe(pdY); //make new file descriptors in table

    //first child 
    switch (catpid = fork()) {
        case 0:
            CatProcess(pdX, pdY);
            break;
        case -1:
            perror("Error creating Cat child");
            exit(1);
        default: 
            writeToFile = fork();
            break;
    }

    //second child
    switch (writeToFile){
        case 0:
            WriteToFile(pdX, pdY);
            break;
        case -1:
            perror("Error creating writeToFile child");
            exit(1);
        default: 
            Parent(pdX, pdY);
            break;
    }

    exit(EXIT_SUCCESS);
}

void Parent(int *ipPX, int *ipPY)
{
    char *line = NULL;
    size_t len = 0;
    ssize_t readedLines;
    fprintf(stderr, "\n%d: mom: %d -> %d, %d -> %d\n",
            (int)getpid(), ipPX[1], ipPX[0], ipPY[1], ipPY[0]);

    close(ipPX[0]);
    close(ipPY[1]);
    close(ipPY[0]);

    while ((readedLines = getline(&line, &len, ifp)) > 0)
        write(ipPX[1], line, readedLines); //read from input file and write to cat process
    close(ipPX[1]); //done with this fd
    fclose(ifp);
    ifp = 0;
    free(line);

    wait(0); //wait for child to finish
}

void CatProcess(int *ipPX, int *ipPY)
{
    fprintf(stderr, "\n%d: cat: %d -> %d, %d -> %d\n",
            (int)getpid(), ipPX[1], ipPX[0], ipPY[1], ipPY[0]);
    close(ipPX[1]);
    close(ipPY[0]);
    close(0);
    dup(ipPX[0]);
    close(ipPX[0]);
    close(1);
    dup(ipPY[1]);
    close(ipPY[1]);
    execlp("cat", "cat", "-n", NULL);
    perror("execlp error"); //not reached unless execlp fails
    exit(1);
}

void FileHandling(int argc, char *argv[2])
{
    char *outputFile; //name of output file 

    if (argc < 2 || argc > 3)
    {
        fprintf(stderr, "Usage: %s input [output]\n", argv[0]);
        exit(1);
    }

    if (!(ifp = fopen(argv[1], "r"))) {
        fprintf(stderr, "Error opening input file");
        exit(1);
    }
    if (argv[2] == NULL)
        outputFile = "LineNumbersOutput";
    else
        outputFile = argv[2];
    if (!(ofp = fopen(outputFile, "w+"))) {
        fprintf(stderr, "Error opening output file");
        exit(1);
    }
}

void WriteToFile(int *ipPX,int *ipPY)
{
    char buf[LINE_MAX];
    int n;
    fprintf(stderr, "\n%d: dog: %d -> %d, %d -> %d\n",
            (int)getpid(), ipPX[1], ipPX[0], ipPY[1], ipPY[0]);
    close(ipPY[1]);
    close(ipPX[0]);
    close(ipPX[1]);

    close(0);
    dup(ipPY[0]);
    close(ipPY[0]); //redirect stdin
    while ((n = read(0, buf, sizeof(buf))) > 0)
        fprintf(ofp, "%.*s", n, buf);
}

<wait.h>头非标准;标准标题是<sys/wait.h>.

原始答案

您的问题是管道的容量有限,根据 POSIX,它可以小至 4 KiB,传统上为 5 KiB,在 Mac OS X 上为 64 KiB,在古代 Linux(SuSE 10)上为 64 KiB。因此,在世界上最好的情况下,您无法向其发送更多数据,cat因为您没有从中读取任何内容并且两个管道都已满。

如何解决?

您可以考虑一个三流程解决方案:

  1. 进程 P1 读取文件并写入 P2。
  2. P2 是cat -n,它的输出到 P3。
  3. P3 读取其标准输入并将其写入标准输出。

所有这一切都记住了练习的重点是使用管道——如果你只是想完成工作,你只需执行cat -n它的输入来自文件 1,输出到文件 2。

或者,您可以使用非阻塞读取。cat -n将 O_NONBLOCK 设置添加到从(使用fcntl()-F_GETFL F_SETFL)返回的管道的输入文件描述符中。在你的主循环中,每次你写一行时,你都会尝试从孩子那里读回一行。如果你得到了什么,就把它写出来;如果没有,没关系。当您完成对孩子的写入后,关闭输出管道(让cat知道不会有更多数据)并将读取管道设置回阻塞并完成从孩子的读取;当这次读取返回 0 字节时,你就完成了。

pipesize.c

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

int main(void)
{
    int fd[2];  /* Output pipe - for parent */
    char buffer[16] = "ABCDEFGHIJKLMNOP";

    if (pipe(fd) < 0)
    {
        fprintf(stderr, "Failed to create pipe\n");
        exit(1);
    }

    int p_flags = fcntl(fd[1], F_GETFL);
    p_flags |= O_NONBLOCK;
    fcntl(fd[1], F_SETFL, p_flags);
    size_t nbytes = 0;
    while (write(fd[1], buffer, sizeof(buffer)) == (ssize_t)sizeof(buffer))
        nbytes += sizeof(buffer);
    printf("PIPE buffer size: %zu bytes\n", nbytes);

    return 0;
}

该程序确定管道的容量。一个用于确定 FIFO 容量的类似程序显示,FIFO 的容量在 Mac OS X 上为 8 KiB,在 Linux 上为 64 KiB。

还有一些配置参数,您可以查找这些参数——请参阅pathconf()——但其中的乐趣在哪里。

于 2013-10-14T14:22:37.867 回答