我需要编写一个程序,从命令行创建管道发送文件名到子进程。在孩子读取该文件并使用管道发回。父进程应该打印文件。如果在子进程中发生错误,则必须将错误发送到父进程。
这是我的代码,它在文件文件中打印一些垃圾(并且当我运行它时也禁用在终端模拟器中滚动)。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void main(int argc, char *argv[]) {
int pipefd[2];
char buff[100];
int childpid;
int size;
FILE *file;
if (argc != 2) {
printf("usage:\n%s <filename>\n", argv[0]);
exit(1);
}
if (pipe(pipefd) < 0) {
perror("can't open pipe\n");
}
if ((childpid = fork()) == 0) {
sleep(1);
size = read(pipefd[0], buff, sizeof(buff));
file = fopen(buff, "r");
if (file == NULL) {
write(pipefd[1], "Can't open file", 15);
exit(1);
}
while (!feof(file)) {
if (fgets(buff, sizeof(buff), file) == NULL) {
write(pipefd[1], "Error reading file", 18);
} else {
write(pipefd[1], buff, sizeof(buff));
}
}
} else if (childpid > 0) {
size = strlen(argv[1]);
if (write(pipefd[1], argv[1], size) != size) {
perror("Error writing to pipe\n");
}
wait(NULL);
while ((size = read(pipefd[0], buff, sizeof(buff))) > 0) {
write(1, buff, size);
}
}
exit(0);
}
答案 0 :(得分:2)
如果sizeof(buf)
返回的内容少于此值,则无法写入fgets
有意义的字节。其余的将充满垃圾。
此外,将面向字符串的fgets
与二进制read/write
混合是一种糟糕的风格。使用read
或fread
来读取文件。它们返回读取的字节数,将此数字用作write
的参数。
答案 1 :(得分:2)
经过多次更改,您的程序按预期工作。让我们列出所需的所有更改以及原因 -
I)在孩子和父母身上,一旦完成,就关闭相应的管道。来自read(3)
的{{3}},
如果某个进程打开了管道并且O_NONBLOCK已清除, read()将阻塞调用线程,直到写入一些数据或 管道由管道打开的所有进程关闭 写入。
所以在你的代码中,在作业管道结束的地方做这样的事情,
size = read(pipefd[0], buff, sizeof(buff));
close(pipefd[0]);
write(pipefd[1], buff, strlen(buff));
close(pipefd[1]);
if (write(pipefd[1], argv[1], size) != size) {
perror("Error writing to pipe\n");
}
close(pipefd[1]);
while ((size = read(pipefd[0], buff, sizeof(buff))) > 0)
{
write(1, buff, size);
}
close(pipefd[0]);
您尚未关闭子级中管道的写入结束,而您的父级已在read
II)您在循环中使用类似while(fgets(...))
的内容来从文件中读取数据。 当文件中有换行符并且fgets
多次返回时,这会爆炸,在此过程中每次都覆盖buffer
我总是使用简单的fgetc
和feof
组合来读取文件。因此,将文件读取机制更改为
unsigned count=0;
while (!feof(file) && count < sizeof(buff))
buff[count++]=fgetc(file);
if (feof(file))
buff[--count]=0;
else
buff[sizeof(buff)-1]=0;
III)在编写来自孩子的文件数据时,你应该使用strlen
(因为我们已经确保缓冲区为空终止,见上文)而不是sizeof
,因为缓冲区可能根本不是满的,你最终会写垃圾。所以,改变
write(pipefd[1], buff, sizeof(buff));
到
write(pipefd[1], buff, strlen(buff));
IV)在完成工作后,请关注孩子和家长的安全exit
。像
close(pipefd[1]);
_exit(EXIT_SUCCESS); // in child
和
close(pipefd[0]);
exit(EXIT_SUCCESS); // in parent
PS :我已经更改了文件读取逻辑,因此您的编译器错误现在已经消失,并且遵循 n.m 给出的建议。
答案 2 :(得分:0)
此代码无法编译:
while (fgets(buff, sizeof(buff), file) != NULL) {
write(pipefd[1], "Error reading file", 18);
} else {
write(pipefd[1], buff, sizeof(buff));
}
你不能在那里有else
条款。