Два процесса в C читают из одной и той же проблемы с файлом - PullRequest
0 голосов
/ 27 декабря 2018

Я пытаюсь прочитать числа из файла, используя процесс отца и дочерний процесс.

У меня есть файл, содержащий простые числа от 1 до 100. Я пытаюсь прочитать 10 первых чисел, используя процесс отца(это работает хорошо).После этого я создаю дочерний процесс с использованием fork и читаю следующие 10 чисел (это также хорошо работает).Я использую wait (), чтобы отцовский процесс ждал, когда ребенок завершит работу.Затем я убиваю дочерний процесс с помощью kill ().Затем я пытаюсь продолжить чтение оставшихся чисел из файла с отцом, но результаты не совпадают с ожидаемыми:

Вот мой код:

pid_t create_process()
{
    pid_t pid;

do {

    pid = fork();       

} while (pid == -1 && errno == EAGAIN); 

return pid;
}

int main()
{
FILE *fichier = fopen("entiers.txt","r");
int i=0;
int n=0;

if (fichier != 0)
{

    printf("I am the father process of the pid %d\n",getpid());
    for(i=0; i<10; i++)
    {
        fscanf(fichier, "%d\n", &n);
        printf("%d\n",n);
    }

    pid_t pid = create_process();

    if(pid)
    {
        wait(NULL);

        printf("I am the father process of the pid %d\n",getpid());
        do
        {   
            n = fgetc(fichier);
            printf("%d\n",n);               
        } while (n != EOF);

        fclose(fichier);

        kill(pid,SIGKILL);
    }

    if (!pid)
    {
        printf("I am the child process of the pid %d\n",getpid());

        for(i=0; i<10; i++)
        {
            fscanf(fichier, "%d\n", &n);
            printf("%d\n",n);
        }


    }

}

return EXIT_SUCCESS;
}

Это мойfile:

1
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97

А это мой вывод:

I am the father process of the pid: 8213

1
2
3
5
7
11
13
17
19
23

I am the child process of the pid: 8214

29
31
37
41
43
47
53
59
61
67

I am the father process of the pid: 8213

50
57
10
51
49
10
51
55
10
52
49
10
52
51
10
52
55
10
53
51
10
53
57
10
54
49
10
54
55
10
55
49
10
55
51
10
55
57
10
56
51
10
56
57
10
57
55
10
55
49
10
55
51
10
55
57
10
56
51
10
56
57
10
57
55
10
-1

Любая помощь?

Ответы [ 3 ]

0 голосов
/ 27 декабря 2018

Базовый ответ

Учитывая, что файл данных имеет длину 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.

0 голосов
/ 29 декабря 2018

Извините, но вы говорите:

У меня есть файл, содержащий простые числа от 1 до 100. Я пытаюсь прочитать 10 первых чисел, используя процесс отца (он работает хорошо).После этого я создаю дочерний процесс с использованием fork и читаю следующие 10 чисел (это также хорошо работает). Я использую wait (), чтобы отцовский процесс ждал, пока ребенок завершит работу.Затем я убиваю дочерний процесс, используя kill (). Затем я пытаюсь продолжить чтение оставшихся чисел из файла с использованием родительского процесса, но результаты не совпадают с ожидаемыми:

В чем причина ожидания смерти ребенка, если однажды он умирает, вы снова его убиваете.Был ли у вас случай, когда ребенок жил после смерти?

В любом случае, когда вы используете любую из подпрограмм пакета <stdio.h>, ввод буферизуется, поэтому, когда вы делаете один fgetc(), fread(), fscanf(), и т. Д. Вызов, вход собирается в блоках, делая оставшиеся байты готовыми для последовательных чтений, но уже взятыми из системы.

Что это означает, из стандартного ввода, когда вы читаететерминал, только одна строка считывается и ввод идет построчно (да, даже если вы запрашивали только символ), и когда вы читаете текстовый файл, ваш ввод блок за блоком (см. значение #define BLOCSZ в<stdio.h>) Это означает, что родитель получает за него не текст до десятого простого числа, а текст до конца следующего блока данных.Это приведет к искажению следующего читателя, как если бы вы поделились дескриптором файла (вы открыли его до fork() ing), указатель на файл распределяется между обоими процессами ... и, вероятно, то, что получает потомок, не имеет правильного смещенияфайл.

Вторым моментом является то, что вы не можете управлять порядком планирования процессов, и если дочерний процесс выполняет чтение файла до или после того, как отец выполняет его.Я видел, как вы читали первые 10 чисел, прежде чем разветвляться.Это гарантирует, что родительский процесс получает первые 10 чисел в файле ... но в буфере может храниться больше памяти для чтения (что еще хуже, буфер может заканчивать половину числа, поэтому, когда вы читаете в дочернем элементе, он может попасть в серединучисла, читая только вторую половину) Но вы можете закончить чтение в том порядке, в котором вы хотите, если вы правильно синхронизируете процессы и правильно учитываете, что происходит с буферизацией.

0 голосов
/ 27 декабря 2018

В родительском, вы делаете fgetc во втором цикле, но я думаю, что вам нужно сделать fscanf, как вы делаете в ребенке.

Кроме того, kill не является необходимымиз-за предыдущего wait (т. е. дочерний процесс уже завершен [чисто]).

Обратите внимание, что родительский код вернет некоторые числа, которые обработал дочерний элемент.Это [вероятно], потому что родительский поток предварительно буферизовал такой маленький файл.

Чтобы исправить это, добавьте setbuf(fichier,NULL); сразу после fopen.

Также удалите все \nот fscanf.Я делал это в более ранних версиях, но пропустил одну [как отметил Дэвис ниже].Из некоторых моих предыдущих правок вы можете видеть, что он добавил посторонний 271 вместо [правильного] 71 в конечном родительском выводе.


Вот исправление вашего кода [прошу прощениябесполезная очистка стиля и некоторый дополнительный код отладки]:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>

char buf[30];

char *
tellme(FILE *fi)
{

    sprintf(buf,"pos=%llu",ftell(fi));

    return buf;
}

pid_t
create_process(void)
{
    pid_t pid;

    do {
        pid = fork();
    } while (pid == -1 && errno == EAGAIN);

    return pid;
}

int
main(void)
{
    FILE *fichier = fopen("entiers.txt", "r");
    int i = 0;
    int n = 0;

    if (fichier == NULL)
        return EXIT_FAILURE;

#if 1
    setbuf(fichier,NULL);
#endif

    printf("I am the father process of the pid %d\n", getpid());
    for (i = 0; i < 10; i++) {
        fscanf(fichier, "%d", &n);
        printf(" %d", n);
    }
    printf("\n");

    printf("I am the father process of the pid %d -- %s\n",
        getpid(),tellme(fichier));

    fflush(stdout);

    pid_t pid = create_process();

    if (pid) {
        wait(NULL);

#if 0
        fflush(fichier);
#endif

        printf("I am the father process of the pid %d -- %s\n",
            getpid(),tellme(fichier));
#if 0
        do {
            n = fgetc(fichier);
            printf("%d\n", n);
        } while (n != EOF);
#else
        while (1) {
            if (fscanf(fichier, "%d", &n) != 1)
                break;
            printf(" %d", n);
        }
        printf("\n");
#endif

        fclose(fichier);

// NOTE/BUG: process has already terminated
#if 0
        kill(pid, SIGKILL);
#endif
    }

    if (!pid) {
        printf("I am the child process of the pid %d -- %s\n",
            getpid(),tellme(fichier));

        for (i = 0; i < 10; i++) {
            fscanf(fichier, "%d", &n);
            printf(" %d", n);
        }
        printf("\n");

        printf("I am the child process of the pid %d -- %s\n",
            getpid(),tellme(fichier));

        fflush(stdout);
    }

    return EXIT_SUCCESS;
}

Вот вывод ( без the setbuf):

I am the father process of the pid 395735
 1 2 3 5 7 11 13 17 19 23
I am the child process of the pid 395736
 29 31 37 41 43 47 53 59 61 67
I am the father process of the pid 395735
 1 2 3 5 7 11 13 17 19 23
I am the father process of the pid 395735
 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 71 73 79 83 89 97

Вот вывод ( с setbuf и с новой строкой, удаленной из [различных] fscanf):

I am the father process of the pid 399457
 1 2 3 5 7 11 13 17 19 23
I am the father process of the pid 399457 -- pos=25
I am the child process of the pid 399458 -- pos=25
 29 31 37 41 43 47 53 59 61 67
I am the child process of the pid 399458 -- pos=54
I am the father process of the pid 399457 -- pos=54
 71 73 79 83 89 97
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...