使用管道从文件发送数据时的同步错误

时间:2016-03-30 21:42:31

标签: c pipe readfile

我已经完成了这段代码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFFER_SIZE 255
#define PATH "./test.txt"

int main(int argc, char **argv)
{
    char read_msg[BUFFER_SIZE];
    FILE *fp;
    char buffer[BUFFER_SIZE];
    fp = fopen(PATH, "r");
    int fd[2];
    pid_t pid;
    if(pipe(fd) == -1){
        perror("Pipe failed");
        return 1;
    }

    pid=fork();
    if (pid==-1)
    {
        perror("Fork failed");
        return 1;
    }

    if (pid>0)
    {
        close(fd[0]);
        fgets(buffer, BUFFER_SIZE, (FILE*)fp);
        do{
            write(fd[1],buffer,strlen(buffer)+1); 
            //sleep(1);
        }while(fgets(buffer, BUFFER_SIZE, (FILE*)fp) != NULL);
    close(fd[1]);
    wait(NULL);
}else{
    close(fd[1]);
    while(read(fd[0], read_msg, BUFFER_SIZE) != 0){
    printf("Child had read: %s", read_msg);
    } 
    close(fd[0]);
    }
    return 0;
}

此代码必须在父进程和子进程之间创建管道。 父进程必须读取文件并将每一行发送到子进程。 子进程打印接收到的行。

但是这段代码有一些错误。 如果我像这样运行,我只得到文件中的一些文本行。 如果我使用注释睡眠(1)运行,我会得到文本的所有行。

有人能看到错误吗?

2 个答案:

答案 0 :(得分:2)

你有两个问题。

下面:

write(fd[1],buffer,strlen(buffer)+1);

您正在将终止空值写入文件。当你调用read()时,如果有多行可用,它会将它们全部读入你的缓冲区,但当你printf()它们时,它只会打印到第一个终止空值,所以你在第一个之后永远不会看到任何一条线。添加sleep()调用会阻止在其他进程有read()之前将多行写入文件,因此在这种情况下不会出现问题。

解决方案:不要将终止空值写入文本文件。同时,您可以通过更改以下内容使代码更紧凑:

fgets(buffer, BUFFER_SIZE, (FILE*)fp);
do{
    write(fd[1],buffer,strlen(buffer)+1); 
}while(fgets(buffer, BUFFER_SIZE, (FILE*)fp) != NULL);

为:

while ( fgets(buffer, BUFFER_SIZE, fp) ) {
     write(fd[1], buffer, strlen(buffer));
}

其次,read()不会为您终止字符串,所以当您实现此解决方案时,此调用:

read(fd[0], read_msg, BUFFER_SIZE)

不会终止read_msg。解决方案是检查实际读取的字节数read(),并在那么多字节之后手动添加终止空值。请注意,如果填写它,您将需要读取最多一个小于缓冲区大小的字节,因为您需要空间来终止空值。

其他评论:

  1. 您不会检查fopen()是否成功,您应该这样做。 write()close()也是如此。在其他情况下,您可以很好地检查返回值。

  2. 由于FILE *已经是fgets(buffer, BUFFER_SIZE, (FILE*)fp)类型,fpFILE *的投射无效。它应该被省略。

答案 1 :(得分:2)

请参阅this post接受的答案,了解为何在此处使用fgets会导致问题。

您可以在父级中采用以下方法:

   if (pid>0)
{
    int size, file_fd;
    file_fd = fileno(fp);

    close(fd[0]);
    while((size = read(file_fd, buffer, BUFFER_SIZE)) != 0) {
        write(fd[1],buffer,size); 
        //sleep(1);
    }
close(fd[1]);
wait(NULL);
}

在孩子身上:

else{
    close(fd[1]);
    int size;
    while((size = read(fd[0], read_msg, BUFFER_SIZE)) != 0){
        write(1,read_msg, size);
    } 
    close(fd[0]);
    }
    fflush(stdout);
    fclose(fp);
    return 0;
}

以大块读取。请注意,我更换了:

printf("Child had read: %s", read_msg);

为了保持一致性,因为我们使用较低级别的系统调用,当您将printf等缓冲I / O与系统调用混淆时,事情会变得棘手。

还有一些其他更小的问题(例如,你不要关闭父文件),你应该在阅读/写作时运行更多的检查,以确认没有错误,但这触及问题的核心。