Я пытаюсь написать программу, которая получает на вход до 10 .txt файлов, содержащих строки с именами учеников и их оценками. Предполагается, что программа создает новый процесс для каждого входного файла и записывает имя каждого учащегося и среднюю оценку во временный файл, названный в честь pid процесса.
В конце концов, родительский процесс должен дождаться завершения всех процессов и создать новый процесс для объединения всех временных файлов в комбинированный выходной файл. Я изо всех сил пытался правильно передать pids вплоть до создания выходного файла, поэтому я всегда успешно записываю только один файл, а другой нет (при работе с двумя входными файлами).
По сути, каждый раз отсутствует один из pid, поэтому моя программа пытается прочитать несуществующий файл. Я изо всех сил пытаюсь понять, почему это происходит, и возможное исправление. Любая помощь будет оценена.
Программа:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define GRADES_FILE "all_std.log"
typedef struct {
char name[11];
int grades[256];
int num_grades;
} student_t;
void report_data_summary(int num_stud) {
fprintf(stderr, "grade calculation for %d students is done\n", num_stud);
}
void calculate_students_grade_average_from_input(int file_number, char* filenames[])
{
student_t students[256];
int num_students = 0;
char filename[50];
sprintf(filename, "%d.temp", getpid());
FILE* fp_in = fopen(filenames[file_number], "r");
if (fp_in == NULL) {
printf("Could not open file %s\n", filenames[file_number]);
exit(EXIT_FAILURE);
}
while (!feof(fp_in)) {
fscanf(fp_in, "%s", students[num_students].name);
int grade;
students[num_students].num_grades = 0;
while (fscanf(fp_in, "%d", &grade) == 1) {
students[num_students].grades[students[num_students].num_grades] = grade;
students[num_students].num_grades++;
}
num_students++;
}
fclose(fp_in);
FILE* fp_out = fopen(filename, "w");
if (fp_out == NULL) {
printf("Could not open output file %s\n", filename);
return;
}
for (int j = 0; j < num_students; ++j) {
int sum = 0;
for (int k = 0; k < students[j].num_grades; ++k) {
sum += students[j].grades[k];
}
float avg = (float)sum / students[j].num_grades;
fprintf(fp_out, "%s %.1f\n", students[j].name, avg);
}
fclose(fp_out);
fprintf(stderr, "process: %d file: %s number of students: %d\n", getpid(), filenames[file_number], num_students);
}
void create_output(int file_count, pid_t* temp_processes[])
{
int total_students = 0;
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
exit(EXIT_FAILURE);
}
else if (pid == 0) {
FILE* fp_final = fopen(GRADES_FILE, "w");
if (fp_final == NULL) {
perror("Could not open final output file");
exit(EXIT_FAILURE);
}
for (int i = 0; i < file_count; ++i) {
char temp_filename[16];
sprintf(temp_filename, "%d.temp", temp_processes[i]);
FILE* fp_temp = fopen(temp_filename, "r");
if (fp_temp == NULL) {
printf("Could not open temporary file %s\n",temp_filename);
return;
}
char line[101];
while (fgets(line, sizeof(line), fp_temp)) {
fputs(line, fp_final);
total_students++;
}
report_data_summary(total_students);
fclose(fp_temp);
}
fclose(fp_final);
return;
}
else {
wait(NULL);
}
}
void ex01(int file_count, char* filenames[])
{
pid_t temp_processes[10];
pid_t pid;
for (int i = 1; i < file_count; ++i) {
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(EXIT_FAILURE);
}
else if (pid > 0) {
temp_processes[i-1] = pid;
printf("%d = %d\n",0,temp_processes[0]);
printf("%d = %d\nend\n",1,temp_processes[1]);
}
else {
calculate_students_grade_average_from_input(i, filenames);
return;
}
}
for (int i = 1; i < file_count; ++i) {
wait(NULL);
}
if (pid > 0)
{
create_output(file_count - 1, temp_processes);
}
}
int main(int argc, char* argv[]) {
ex01(argc, argv);
return 0;
}
Вход:
file1.txt:
Abraham 80 90 75
Benny 90
Garland 70 9 90 100
file2.txt:
Dana 90 95
Ron 100 80 90
Выходной файл:
Abraham 81.7
Benny 90.0
Garland 67.2
В выводе отсутствуют студенты второго файла.
Вывод консоли:
0 = 35222
1 = 0
end
0 = 35222
1 = 35223
end
process: 35223 file: gr_2.txt number of students: 2
process: 35222 file: gr_1.txt number of students: 3
grade calculation for 3 students is done
Could not open temporary file 0.temp
Я знаю, я пытаюсь преодолеть этот пробел, используя временные файлы и сохраняя их имена, чтобы в конечном итоге использовать их в основном процессе. Но эта доставка имени где-то терпит неудачу, и я не могу ее найти.
Строка while (!feof(fp_in)) неверна. Я предлагаю вам прочитать это: Почему «пока( !feof(файл) )» всегда неправильно?
Я предлагаю вам заняться отладкой резиновой уточки вашего кода. Все процессы используют для вывода один единственный файл, который не используется совместно процессами. Когда один процесс открывает его для записи, текущее содержимое файла будет потеряно.
@Someprogrammerdude Я понимаю это, но думаю, что проблема возникает заранее. Запись в файл происходит только в create_output, но сначала он пытается прочитать из несуществующего файла (неправильное имя файла), и здесь происходит сбой. Механизм передачи pids неверен.
В строке sprintf(filename, "%d.temp", getpid()); вы, кажется, предполагаете, что возвращаемый тип getpid() (то есть pid_t) эквивалентен int. Это предположение кажется мне опасным. Я считаю, что sprintf(filename, "%jd.temp", (intmax_t)getpid()); будет безопаснее. Обратите внимание, что intmax_t требует #include <stdint.h>.
Вывод показывает, что имя файла, который не удалось открыть, было 0.temp, но ваши временные файлы названы с помощью идентификаторов, а не индексов. 0 не является допустимым pid.
Я предполагаю, что вы просто экспериментируете с fork() — это интересная тема, — но ваш вариант использования здесь патологически не подходит для fork(). Вы создаете подпроцесс для чтения каждого отдельного файла... а затем последовательно читаете каждый из выходных файлов подпроцессов основной программой? Это просто не очень хороший вариант использования многопроцессорной обработки.
Примечание: я предлагаю вам изменить ex01(argc, argv); на ex01(argc-1,argv+1);. Таким образом, file_count и filenames будут иметь одинаковое значение во всех функциях. Например, вы можете использовать один и тот же цикл for (int i = 0; i < file_count; ++i) { в функциях create_output и ex01 вместо использования for (int i = 1; i < file_count; ++i) { в одной функции и for (int i = 0; i < file_count; ++i) { в другой функции.
Это не многопоточная программа. Пожалуйста, исправьте заголовок, теги и ожидания.





В линии
void create_output(int file_count, pid_t* temp_processes[])
вы определили второй аргумент функции как тип pid_t **. Однако в функции ex01 вы вызываете функцию create_output следующим образом:
create_output(file_count - 1, temp_processes);
Выражение temp_processes будет распадаться на &temp_processes[0], которое имеет тип pid_t*. Следовательно, ваша программа вызывает неопределенное поведение, поскольку типы второго параметра не совпадают.
Ваш компилятор должен предупредить вас об этом. Это предупреждение, которое я получаю от gcc:
<source>: In function 'ex01':
<source>:142:39: warning: passing argument 2 of 'create_output' from incompatible pointer type [-Wincompatible-pointer-types]
142 | create_output(file_count - 1, temp_processes);
| ^~~~~~~~~~~~~~
| |
| pid_t * {aka int *}
<source>:65:43: note: expected 'pid_t **' {aka 'int **'} but argument is of type 'pid_t *' {aka 'int *'}
65 | void create_output(int file_count, pid_t* temp_processes[])
| ~~~~~~~^~~~~~~~~~~~~~~~
Я предлагаю вам изменить строку
void create_output(int file_count, pid_t* temp_processes[])
к:
void create_output(int file_count, pid_t temp_processes[])
Я только что попробовал это, прежде чем пришел сюда, чтобы увидеть, что вы предлагаете то же самое, и, конечно же, теперь программа работает так, как ожидалось. Большое спасибо!
Функция
forkсоздает совершенно новый и независимый процесс, а не поток. Также помните, что процессы действительно независимы друг от друга, со своей собственной картой виртуальной памяти. Вы ожидаете, что между процессами нет общей памяти.