我正在编写一个简单的C程序,它递归地创建子文件,并根据用户的输入,用它们对文件中的所有数字求和。用户可以选择三种预定的文件大小,以及三种可以生成的子文件数量。理论上,可以有任意数量的子文件或任意大小的文件,但为了简单起见,这里只有3个。
我遇到的问题是,无论我使用哪个文件,只有当程序只使用1个子项时,总和才是正确的。如果使用其他数量的子项,例如4个,这个数字很接近,但不完全正确。有人能告诉我是什么导致了这个问题吗?
下面是我认为有问题的代码段:
// C program to demonstrate use of fork() with pipe()
// By: Maxwell Wendlandt
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
FILE *file;
int numForks;
// initialize pipes for up to 4 children
int fd[4][2];
// initialize up to 4 processes
pid_t pid[4];
int total = 0;
int finalResult = 0;
char fileName[10] = "";
int fileNum;
int numLines;
// ask which file to scan
printf("Enter file number 1 (1000 nums), 2 (10000 nums) or 3 (100000 nums):\n");
scanf("%i", &fileNum);
// chose the file
switch(fileNum)
{
case 1 :
printf("File 1 selected.\n");
strcpy(fileName, "file1.dat");
numLines = 1000;
break;
case 2 :
printf("File 2 selected.\n");
strcpy(fileName, "file2.dat");
numLines = 10000;
break;
case 3 :
printf("File 3 selected.\n");
strcpy(fileName, "file3.dat");
numLines = 100000;
break;
default :
printf("Enter a valid file number next time.\n");
return 0;
}
// ask how many children (forks)
printf("Do you want 1, 2 or 4 child processes?\n");
scanf("%i", &numForks);
for (int i = 0; i < numForks + 1; i++)
{
if (pipe(fd[i]) == -1)
{
printf("Error with creating pipe.\n");
return 1;
}
}
for(int i = 0; i < numForks; i++)
{
pid[i] = fork();
if(pid[i] == -1)
{
printf("Error creating child.\n");
return 1;
}
if(pid[i] == 0)
{
// children
int sum = 0, num = 0;
int start, end;
file = fopen(fileName, "r");
start = i * (numLines / numForks);
printf("start: %i\n", start);
end = ((i + 1) * (numLines / numForks));
printf("end: %i\n", end);
fseek(file, (start * 4), SEEK_SET);
for(int i = start; i < end; i++)
{
fscanf(file, "%d", &num);
printf("num on line %d is: %d\n", i + 1, num);
sum += num;
}
printf("sum in child: %d\n", sum);
write(fd[i][1], &sum, sizeof(sum));
close(fd[i][1]);
return 0;
}
}
// parent
for(int i = 0; i < numForks; i++)
{
read(fd[i][0], &total, sizeof(total));
close(fd[i][0]);
finalResult += total;
}
printf("The grand total: %i\n", finalResult);
for(int i = 0; i < numForks; i++)
{
wait(NULL);
}
return 0;
}
提前感谢!
1条答案
按热度按时间3ks5zfa01#
文件的每一行都有一个3位数字。2所以一个1000位数字的文件有1000行。
这意味着每行包含四个五字节-三位数字、回车符和换行符。例如,
123\r\n
。此处的off-by-two错误将导致每次寻道偏移,并且每个子级将从比它们应该读取的位置更早的位置读取。如果每行是五个字节,则应为
start * 5
。旁白:我猜文件中的数字是用零填充的(见下面的生成示例)。
如果是这样的话,
fscanf
说明符%i
可能不合适,因为它充当strtol
,基数为0
,这意味着数字基数由其第一个字符确定。当以零填充的数字被解析为八进制时,这可能会导致混乱的结果。例如:
004
-八进制,值为4
。040
-八进制,值为32
。400
-十进制,值为400
。009
-八进制,无效值(0
)。011
-八进制,值为9
。%d
会将输入解析为以10为基数的数字。这有几个问题。
i < numForks + 1
是一个差一的错误。用户也可以输入任意数字。如果fd
是通过越界索引访问的,则这将调用Undefined Behaviour。通常,您应该检查更多函数的返回值,如
scanf
、fscanf
、fseek
、write
和read
,以确保使用的是有效数据。首选
perror
和fprintf(stderr, ...)
将有用的错误消息打印到正确的流。非常粗略的重构:
用于生成要使用(
./gen 1000 > file1.dat
)进行测试的文件的代码:以及一个健全性检查器(
./sanity-check < file1.dat
):