使用execvp cat的C程序输入不超过150行

时间:2013-10-14 13:22:09

标签: c pipe

我正在编写一个程序,将行号放在给定文本文档的每一行的前面。我通过使用管道写这个,因为这是学习管道如何工作。

问题:当输入文件中有少量数据(大约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);
    }
}

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

感谢所有的帮助!

新代码:

#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
    }
 }

制作了一张图纸,让自己清楚(认为这是正确的) enter image description here

1 个答案:

答案 0 :(得分: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上是64 KiB(SuSE 10) 。因此,有一点,世界上最好的意愿,你不能再向cat发送任何数据,因为你没有读回任何数据,而且两个管道都满了

如何解决?

您可以考虑采用三流程解决方案:

  1. 进程P1读取文件并写入P2。
  2. P2为cat -n,其输出为P3。
  3. P3读取其标准输入并将其写入标准输出。
  4. 所有这一切都记住了练习的重点是使用管道 - 如果你只是想完成工作,你只需执行cat -n,其输入来自文件1并输出即可提交2。

    或者,您可以使用非阻塞读取。将O_NONBLOCK设置添加到cat -n的管道输入文件描述符中(使用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容量的类似程序表明,在Mac OS X上FIFO的容量为8 KiB,在Linux上再次为64 KiB。

    还有一些配置参数供您查看 - 请参阅pathconf() - 但其中的乐趣在哪里。