我正在编写一个程序,将行号放在给定文本文档的每一行的前面。我通过使用管道写这个,因为这是学习管道如何工作。
问题:当输入文件中有少量数据(大约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
}
}
制作了一张图纸,让自己清楚(认为这是正确的)
答案 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
发送任何数据,因为你没有读回任何数据,而且两个管道都满了
如何解决?
您可以考虑采用三流程解决方案:
cat -n
,其输出为P3。所有这一切都记住了练习的重点是使用管道 - 如果你只是想完成工作,你只需执行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()
- 但其中的乐趣在哪里。