Неожиданный EOF при чтении файла в многопроцессорной среде

У меня есть файл instructions.txt (обозначается указателем файла fp) который состоит из 12 строк (каждая строка содержит байты ls\n а также ps\n в качестве альтернативы).

Первоначально основной процесс открывает файл в режиме чтения, создает и инициализирует общую область памяти. mem и создает еще 11 процессов, используя fork(),

Предполагается, что каждый из 12 процессов читает только одну строку из файла и выполняет эту инструкцию. Прежде чем войти в крайнее while блокировать, вызывая ftell(fp) во всех процессах возвращает 0.

Проблема в том, что после первого процесса, который входит в самый внешний if блок читает одну строку, используя fgetsзвонит ftell в других процессах возвращает 36 (размер файла 12x3 = 36 байт). ftell в процессе, который выполнил fgets first по-прежнему возвращает 3 (конец первой строки).

Так что в следующий раз любой процесс вызывает fgets, он возвращает EOF

Область общей памяти mem используется в качестве "массива", где элементы с 1-го по 12-й содержат PID 12 процессов, а 0-й элемент используется в качестве индекса, чтобы определить, какой процесс войдет во внешний if блок.

Вот фрагмент кода, который вызывает проблему -

while(mem[0] > 0)
{
    printf("(%u) pos = %ld\n", curr_pid, ftell(fp));

    // only the process whose PID matches the value in 
    // mem[mem[0]] can enter
    if(curr_pid == mem[mem[0]])
    {
        printf("\n\nprocess %u enters CS\n", curr_pid);
        char instr[100];
        printf("pos before read = %ld\n", ftell(fp));
        if(fgets(instr, 100, fp) == NULL)
        {
            perror("fgets error or EOF");
            //return 1;
        }
        printf("pos after read = %ld\n", ftell(fp));
        instr[strlen(instr)-1] = 0;
        printf("process %u executing command: %s, size = %lu\n", curr_pid, instr, strlen(instr));

        /* execute instruction */
        char *args[] = {instr, NULL};
        pid_t exec_pid = fork();
        if(exec_pid == -1)
        {
            perror("fork error");
        }
        else if(exec_pid == 0) // child execs
        {
            execvp(instr, args);
            perror("execvp error");
            return 1;
        }
        printf("process %u leaving CS\n", curr_pid);
        sleep(5);
        mem[0]--; // alow next process to enter and read
    }
    sleep(1);
}

Вот

  • (FILE *) fp указатель файла на instructions.txt файл

  • (int *) mem является общей памятью, прикрепленной ко всем 12 процессам

  • mem[0] это индекс (значения от 1 до 12 включительно), по которому PID для процесса, который будет выбран для ввода if блок найден в mem (То есть, mem[mem[0]] содержит PID mem[0]й процесс

  • (pid_t) curr_pid хранит собственный PID для каждого процесса

По сути, только один процесс входит в самый внешний if блокировать, пока другие "ждут", зацикливаясь, пока не наступит их очередь

1 ответ

Решение

Все ваши процессы имеют потоки, связанные с одним и тем же описанием открытого файла, поддерживаемым ядром. Буферы этих потоков принадлежат процессам, но смещение файла принадлежит описанию открытого открытого файла.

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

Если вы хотите, чтобы несколько процессов совместно читали из одного и того же файла, вам нужно будет это учитывать. Я вижу как минимум два механизма:

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

  2. Вы используете низкоуровневый ввод-вывод с файловыми дескрипторами (open(), read()) вместо потокового ввода / вывода. Если вы выполняете какую-либо буферизацию, вы сохраняете буфер в разделяемой памяти.

Также обратите внимание, что вам нужна некоторая форма синхронизации, чтобы гарантировать, что каждый процесс видит записи в общую память, выполняемые другими процессами. Вы можете предусмотреть это и избавиться от расточительных спин-блокировок, создав и должным образом используя разделяемый процессом мьютекс и переменную условия.

Другие вопросы по тегам