使用管道从文件发送数据时出现同步错误
Error of syncronism in sending data from a file using a pipe
我已经完成了这段代码:
#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;
}
这段代码必须在父进程和子进程之间创建一个管道。
父进程必须读取文件并将每一行发送给子进程。
子进程打印接收到的行。
但是这段代码有一些错误。
如果我 运行 这样,我只会得到文件中的几行文本。
如果我 运行 使用注释的 sleep(1) 我会得到文本的所有行。
任何人都可以看到错误吗?
你有两个问题。
这里:
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)
不会以 null 终止 read_msg
。解决方案是检查实际读取了多少字节 read()
,并在该字节后手动添加终止空值。请注意,您最多需要读取比缓冲区大小少一个字节的内容,以防填满它,因为您需要为终止空值留出空间。
其他评论:
您不检查 fopen()
是否成功,您应该检查。 write()
和 close()
也一样。在其他情况下,您很好地检查了 return 值。
在 fgets(buffer, BUFFER_SIZE, (FILE*)fp)
中转换为 FILE *
无效,因为 fp
已经是 FILE *
类型。它应该被省略。
请参阅 this post 已接受的答案,以了解为什么在此处使用 fgets 会导致问题。
您可以在parent中采取以下做法:
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);
}
并且在 child:
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);
为了保持一致性,因为我们正在使用 lower-level 系统调用,当您 mix-up 缓冲 I/O 系统调用时,事情会变得棘手。
还有一些其他更小的问题(例如,您没有关闭 parent 文件),当 reading/writing 确认没有时,您应该 运行 更多检查这是一个错误,但这正是您问题的核心。
我已经完成了这段代码:
#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;
}
这段代码必须在父进程和子进程之间创建一个管道。 父进程必须读取文件并将每一行发送给子进程。 子进程打印接收到的行。
但是这段代码有一些错误。 如果我 运行 这样,我只会得到文件中的几行文本。 如果我 运行 使用注释的 sleep(1) 我会得到文本的所有行。
任何人都可以看到错误吗?
你有两个问题。
这里:
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)
不会以 null 终止 read_msg
。解决方案是检查实际读取了多少字节 read()
,并在该字节后手动添加终止空值。请注意,您最多需要读取比缓冲区大小少一个字节的内容,以防填满它,因为您需要为终止空值留出空间。
其他评论:
您不检查
fopen()
是否成功,您应该检查。write()
和close()
也一样。在其他情况下,您很好地检查了 return 值。在
fgets(buffer, BUFFER_SIZE, (FILE*)fp)
中转换为FILE *
无效,因为fp
已经是FILE *
类型。它应该被省略。
请参阅 this post 已接受的答案,以了解为什么在此处使用 fgets 会导致问题。
您可以在parent中采取以下做法:
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);
}
并且在 child:
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);
为了保持一致性,因为我们正在使用 lower-level 系统调用,当您 mix-up 缓冲 I/O 系统调用时,事情会变得棘手。
还有一些其他更小的问题(例如,您没有关闭 parent 文件),当 reading/writing 确认没有时,您应该 运行 更多检查这是一个错误,但这正是您问题的核心。