Базовый ответ
Учитывая, что файл данных имеет длину 73 байта (дать или взять - у вас может быть дополнительный пробел, о котором я не догадывался), первый вызов fscanf()
будет читатьвесь файл в память.Затем родительский процесс читает из памяти 10 строк, перемещая указатель чтения в стандартный буфер ввода-вывода.Конечные переводы строк формата fscanf()
на самом деле не нужны;%d
пропускает пробел, который включает в себя новые строки, и если входные данные не были получены из файла, конечная пустая строка была бы очень плохим UX - пользователь должен был бы ввести (начало) следующего числа, чтобы завершитьтекущий ввод.(См. scanf()
оставляет новую строку в буфере и Каков эффект от пробела в строке формата scanf()
? .)
Тогдапроцесс вилки.Дочерний объект является точной копией родителя, поэтому он продолжает чтение с того места, где остановился родитель, и печатает 10 цифр, как вы ожидали, а затем завершает работу.
Затем родительский процесс возобновляется.Он не сделал ничего, чтобы изменить положение указателя в памяти, поэтому он продолжает там, где остановился.Однако код чтения теперь читает отдельные символы и печатает их десятичные значения, поэтому он получает 50, 57, 10 - коды символов для '2'
, '9'
и '\n'
.И поэтому вывод продолжается для всех остальных простых чисел на входе.
Вам действительно нужно исправить ввод, чтобы возобновить использование fscanf()
вместо fgetc()
.
НетДля родителей разумный способ узнать, что сделал ребенок, кроме перехода от буферизованного ввода-вывода к небуферизованному вводу-выводу.Если вы переключились на небуферизованный ввод-вывод, вызвав setbuf(fichier, NULL);
или setvbuf(fichier, NULL, _IONBF, 0);
после открытия файла, но перед выполнением любой другой операции с потоком файлов, вы увидите, что родительский процесс продолжается с того места, где он остановился.
Примечание: я не убежден в цикле в create_process()
- если ресурсов недостаточно, по крайней мере, немного подождите, чтобы дать системе время найти некоторые, но это более распространенное лечение«Недостаточно ресурсов» как фатальная ошибка.
Еще одно замечание: отправка сигнала процессу, который уже умер (потому что вы ожидали его смерти), не будет работать.
Вот некоторый пересмотренный код:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
static pid_t create_process(void)
{
pid_t pid = fork();
if (pid < 0)
{
fprintf(stderr, "Failed to fork\n");
exit(1);
}
return pid;
}
int main(void)
{
const char filename[] = "entiers.txt";
FILE *fichier = fopen(filename, "r");
int i = 0;
int n = 0;
// setbuf(fichier, NULL);
// setvbuf(fichier, NULL, _IONBF, 0);
if (fichier == 0)
{
fprintf(stderr, "Failed to open file '%s' for reading\n", filename);
exit(1);
}
printf("I am the parent process with the pid %d\n", getpid());
for (i = 0; i < 10; i++)
{
if (fscanf(fichier, "%d", &n) != 1)
break;
printf("%d\n", n);
}
pid_t pid = create_process();
if (pid == 0)
{
printf("I am the child process with the pid %d\n", getpid());
for (i = 0; i < 10; i++)
{
if (fscanf(fichier, "%d", &n) != 1)
break;
printf("%d\n", n);
}
}
else
{
wait(NULL);
printf("I am the parent process with the pid %d\n", getpid());
while (fscanf(fichier, "%d", &n) == 1)
printf("%d\n", n);
}
fclose(fichier);
return EXIT_SUCCESS;
}
Пример вывода:
I am the parent process with the pid 15704
2
3
5
7
11
13
17
19
23
29
I am the child process with the pid 15705
31
37
41
43
47
53
59
61
67
71
I am the parent process with the pid 15704
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97
Очень часто такие вопросы связаны с вводом-выводом файлового дескриптора, и обсуждение должно охватывать различие междуоткрыть дескриптор файла и описание открытого файла и объяснить, что распределяется между процессами, а что нет.Поскольку входной файл настолько мал, это не проблема с этим кодом.Если бы таблица простых чисел поднялась, скажем, до 999983 (самое большое простое число меньше миллиона), и дочерний процесс прочитал намного больше данных, то вы бы увидели совершенно разные эффекты.
Небуферизованный ввод и трейлингновые строки в scanf()
строки формата
Эмпирическое наблюдение показывает, что когда в исходной версии кода, показанной выше, было scanf("%d\n", &n)
как в первом цикле чтения родительского элемента, так и в цикле чтения дочернего элемента, и программа была настроена на использованиенебуферизованный ввод вывод будет выглядеть следующим образом:
…
67
71
I am the parent process with the pid 27071
33
79
…
, где 33
не ожидается на первый взгляд.Тем не менее, есть объяснение того, что идет не так.
В потоке есть хотя бы один байт pushback (даже без буферизации), поэтому в точке, где родительские вилки, и родительский, и дочерний, имеют3
из 31
в позиции pushback (поскольку символ новой строки читался как пробел, а первый непустой символ, то есть 3
строки, содержащей 31
, читался и возвращался во входной буфер).
чеДочерний объект является почти точной копией родителя, он читает символ возврата и продолжает с 1
, получает символ новой строки, а затем 3
из 37
и печатает 31
, как и следовало ожидать.Это продолжается до тех пор, пока он не прочитает 7
в начале 73
и не вытолкнет его обратно в свой собственный входной буфер, но это, конечно, не повлияет на входной буфер родителя (это отдельные процессы).Ребенок выходит.
Родитель возобновляет.Он имеет 3
в своей позиции возврата, а затем получает 3
из 73
(поскольку родительский и дочерний элементы имеют одно и то же описание открытого файла, а позиция чтения связана с описанием, а не с дескриптором, поэтомудочерний элемент переместил позицию чтения), затем он получает новую строку и завершает сканирование (в последнем цикле пропущен завершающий пробел в строке формата scanf()
) и правильно печатает 33
.Затем он приступает к чистому прочтению оставшейся части ввода, пропуская пробел (символ новой строки) перед прочтением каждого числа.
Изменение кода на использование fscanf(fichier, "%d", &n)
означает, что дочерний процесс останавливается с символом новой строки перед 73
в его буфере возврата и позиция чтения, указывающая на 7
из 73
, что именно там, где это нужно родителю.
Если первый родительский цикл пропустил символ новой строки в fscanf()
формат, тогда ребенок все равно работал бы, но родитель сообщал бы 3
как первое число, когда оно возобновилось, вместо 33
.