Reputation: 19
I'm reading lines of text from file, and for each line I'm processing it using several { fork() --> child process invokes execvp(), and parent invokes wait() } . at the end of process I'm writing the results to a file.
Problem is: the while loop seems to iterate too much and also the writing to the file.
The results.csv file contains 6 lines instead of just 2 (the while iteration iterates a text file with 2 lines, but also when I use printf it seems like the last line is read twice).
What am I missing here?
The code example is:
FILE* results = fopen("results.csv", "w");
if (results == NULL){
fclose(fp);
perror("Failed opening results file");
exit(-1);
}
fdIn = open(inputPath, O_RDONLY);
if (fdIn < 0){
perror("Failed opening input file");
exit(-1);
}
while (fgets(student, sizeof(student), fp) != NULL) {
// override end line char of unix ('\n') with '\0'
student[strlen(student)-1] ='\0';
pid = fork();
if (pid < 0){
close(fdIn);
perror("Failed creating process for executing student's program");
exit(-1);
}
if (pid == 0) {// son process code
fdOut = open("tempOutput.txt", (O_WRONLY | O_CREAT | O_TRUNC), 0666);
if (fdOut < 0){
perror("Failed opening temporary output file");
exit(-1);
}
close(1);
dup(fdOut);
close(fdOut);
close(0);
dup(fdIn);
close(fdIn);
char studProgPath[bufSize];
strcpy(studProgPath,studentsFolderPath);
strcat(studProgPath,"/");
strcat(studProgPath,student);
strcat(studProgPath,"/");
strcat(studProgPath,"a.out");
char * args[] = {"a.out", NULL};
ret_code = execvp(studProgPath,args);
if (ret_code == -1){
perror("Failed executing student program");
exit(-1);
}
}
waited = wait(&stat);
if (stat == -1){ // need to grade 0
printf("%s,0\n",student);
}else{ // open process to compare the output with the expected
pid = fork();
if (pid < 0){
perror("Failed opening process for comparing outputs");
exit(-1);
}
if(pid == 0) { // son process
char * args[] = {"comp.exe",outputPath,"tempOutput.txt",NULL};
ret_code = execvp("comp.exe",args);
exit(ret_code);
}
waited = wait(&stat);
if (stat == -1) {
perror("Failed executing comparing program");
exit(-1);
} else if (stat == 0 || stat == 1) { // if outputs are not the same
fprintf(results,"%s,0\n",student);
} else { // matching outputs grade 100
fprintf(results,"%s,100, pid: %d\n",student,getpid());
}
}
}
Upvotes: 1
Views: 94
Reputation: 8475
The file which gets triple entries gets opened here:
FILE* results = fopen("results.csv", "w");
The following lines write to this results
file, slightly before the function calls fork()
:
} else if (stat == 0 || stat == 1) { // if outputs are not the same fprintf(results,"%s,0\n",student); } else { // matching outputs grade 100 fprintf(results,"%s,100, pid: %d\n",student,getpid()); }
This file should be flushed with fflush(results)
before the fork, otherwise the buffer of results
might be flushed three times: in the parent, and in the two copies in the children.
Also, results
and student
should be closed with fclose(results)
and student
, before calling execvp. If the files are not closed, then the a.out
might manipulate the results
file. I assume that a.out
is an external code which you don't control.
while (fgets(student, sizeof(student), fp) != NULL) {
// override end line char of unix ('\n') with '\0'
student[strlen(student)-1] ='\0';
fflush(results); // otherwise each child may flush the same chars
pid = fork();
if (pid < 0){
fclose(results); // otherwise ./a.out might write to this file
fclose(fp); // better also close it.
close(fdIn);
Upvotes: 2