Невозможно выйти из цикла while после чтения информации, записанной в канал - PullRequest
0 голосов
/ 27 марта 2019

TLDR: Вы должны закрыть конец записи всех каналов во всех дочерних элементах.Чтение обнаружит EOF, только если ни один процесс не имеет конца записи, все еще открытого.Кредиты @Bodo

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

Мне кажется, что я почти на месте, получая правильные результаты, но мой код, кажется, ведет к бесконечному циклу while после чтения всех записанных строк до конца чтения канала.

Вот соответствующий фрагмент кода

int child_work(int pipes[][2], int proc, int procid, FILE * out)
{
    int i;
    pid_t mypid;
    Expression exp;
    float result;
    int procidx = procid;
    char expression[MAIN_BUF_LEN];
    int r_val;
    printf("entered while loop for child process %d\n", mypid);
    while(1)
    {
        if ( (r_val = read(pipes[procid][0], expression, MAIN_BUF_LEN)) > 0)
        {
            printf("return values of read: %d\n", r_val);
            exp_readln(&exp, expression);
            result = exp_cal(&exp);
            printf("[#%d]: %d %0.3f\n", procidx, mypid, result);
            fprintf(out, "#%d: %d %0.3f\n", procidx, mypid, result);
            fflush(out);
            procidx += proc;
        }
        else
        {
            break;
        }
    }
    printf("exited while loop and reached end of child process %d\n", mypid);
    return 0;

int main(int argc, char **argv)
{
    if (argc != 4)
    {
        printf("not enough arguments");
        return 0;
    }

    const char *infile;  // Name of infile
    const char *outfile; // Name of outfile
    int proc;            // Number of child process to fork

    // Save arguments to variables
    infile = argv[1];
    outfile = argv[2];
    sscanf(argv[3], "%u", &proc);

    int pipes[proc][2]; // Pipes to be created
    pid_t child_pids[proc]; // store all the pids of children created

    int i; // Loop counter

    char buf[MAIN_BUF_LEN];
    Expression exp;

    FILE * in_ptr, *out_ptr;
    // Open infile with read-only, outfile with write and append.
    if ((in_ptr = fopen(infile, "r")) == NULL)
    {
        printf("Error in opening file. Ending program. \n");
        return 1;
    }
    out_ptr = fopen(outfile, "a+");

    // Get parent pid and print to outfile
    int ppid = getpid();
    fprintf(out_ptr, "%d\n", ppid);
    fflush(out_ptr);

    // $proc pipes should be created and saved to pipes[proc][2]
    for (i = 0; i < proc; ++i)
    {
        // TODO
        if (pipe(pipes[i]) == -1 )
        {
            printf("Pipe failed for pipe %d\n", i);
            return 1;
        }
    }

    // $proc child processes should be created.
    // Call child_work() immediately for each child.
    for (i = 0; i < proc; ++i)
    {
        int pid;
        // create child only if in parent process
        if (getpid() == ppid)
        {
            pid = fork();
            if (pid != 0)
                printf("created child with child pid %d\n", pid);
                child_pids[i] = pid;
        }

        if (pid == 0) // in child process
        {
            child_work(pipes, proc, i, out_ptr);
            break;
        }
        else if (pid < 0) // error in forking
        {
            printf("Fork failed.\n");
        }
    }

    // Close reading end of pipes for parent
    for (i = 0; i < proc; ++i)
    {
        // TODO
        if (getpid() == ppid)
            close(pipes[i][0]);
    }

    // Read lines and distribute the calculations to children in round-robin
    // style.
    // Stop when a empty line is read.

    char* line = NULL;
    size_t len = 0;
    ssize_t read = 0;
    int j = 0;
    while ((read = getline(&line, &len, in_ptr)) != -1) {
        //printf("Retrieved line of length %zu:\n", read);
        //printf("%s", line);
        j = j % proc;
        write(pipes[j++][1], line, strlen(line)+1);
    }

    // Close all the pipes when the task ends
    for (i = 0; i < proc; ++i)
    {
    //   close(pipes[i][READ]);
       close(pipes[i][WRITE]);
    }
    printf("Task 6 complete!");

    for (i = 0; i < proc; ++i)
    {
        waitpid(child_pids[i], NULL, 0);
    }

    fprintf(out_ptr, "\n");
    fflush(out_ptr);

    return 0;
}

Это вывод, который я получаю, который, по-видимому, застревает в бесконечном цикле while, поскольку процесс не завершится.Кроме того, значение return values of read: должно быть 22 или 23 в зависимости от конкретного входного файла, который я использую, но я не знаю, почему он увеличивается для определенных последующих дочерних процессов.Кажется, что ни один из дочерних процессов не может выйти из цикла while, так как этот printf("exited while loop and reached end of child process %d\n", mypid); не выполняется.Насколько я понимаю, что если канал был прочитан, возвращаемое значение будет размером в байтах прочитанной строки, а если оно достигнет EOF или ошибки, возвращаемое значение будет 0 или -1, соответственно.

entered while loop for child process 16016
entered while loop for child process 16017
entered while loop for child process 16018
entered while loop for child process 16020
return values of read: 22
entered while loop for child process 16019
[#0]: 16016 1.783
return values of read: 22
return values of read: 22
[#2]: 16018 0.061
[#1]: 16017 0.195
return values of read: 22
return values of read: 22
[#5]: 16016 0.269
return values of read: 46
[#10]: 16016 1.231
return values of read: 22
return values of read: 22
[#6]: 16017 0.333
return values of read: 22
return values of read: 46
[#11]: 16017 1.684
[#7]: 16018 -0.734
return values of read: 46
[#12]: 16018 0.134
[#3]: 16019 0.778
return values of read: 68
[#4]: 16020 -0.362
return values of read: 68
[#9]: 16020 0.506
[#8]: 16019 -0.450

Буду признателен за любую глупую ошибку, которую я могу сделать.Спасибо!

1 Ответ

0 голосов
/ 27 марта 2019

В коде есть несколько проблем.

К сожалению, я не могу скомпилировать его и исправить ошибки, потому что он неполон.

  1. Невозможно определить массивы с размеромэто не является постоянной величиной, как это.

    int pipes[proc][2]; // Pipes to be created
    

    Я ожидаю, что компилятор покажет предупреждение в этой строке.
    Вы должны либо использовать динамическое выделение (malloc), либо статически распределить массивы с помощьюМаксимальный размер и убедитесь, что proc не больше максимального.

  2. Вы должны закрыть конец записи всех каналов во всех дочерних элементах.read будет обнаруживать EOF только в том случае, если ни один процесс не имеет конца записи еще открытым.

  3. Вместо

    while(1)
    {
        if ( (r_val = read(pipes[procid][0], expression, MAIN_BUF_LEN)) > 0)
        {
            /*...*/
        }
        else
        {
            break;
        }
    }
    

    Я предлагаю

    while((r_val = read(pipes[procid][0], expression, MAIN_BUF_LEN)) > 0)
    {
            /*...*/
    }
    
  4. Вместо

        pid = fork();
        if (pid != 0)
            printf("created child with child pid %d\n", pid);
    

    должно быть

        pid = fork();
        if (pid > 0)
            printf("created child with child pid %d\n", pid);
    

    , поскольку pid < 0 является ошибкой.

  5. Вместо

    if (pid == 0) // in child process
    {
        child_work(pipes, proc, i, out_ptr);
        break;
    }
    

    используйте

    if (pid == 0) // in child process
    {
        child_work(pipes, proc, i, out_ptr);
        return 0;
    }
    

    При break; дочерний процесс продолжит выполнение кода после цикла for, который будет читать файл и записывать в каналы, когдаchild_work возвращает.

  6. Не гарантируется, что каждый дочерний элемент получит свою очередь на read из канала до того, как родительский write s получит следующие данные, поэтому он может получитьдва или более сообщений в одном read.В реальных приложениях вы также должны быть готовы обрабатывать неполные вызовы read или write и продолжать запись / чтение оставшихся данных с помощью дополнительных вызовов read или write.

    Я думаю, что самый простой способдля обработки частичных read или write будет использоваться буферизованный ввод-вывод.Вы можете использовать fdopen с дескриптором файла wrtite или дескриптором файла чтения канала и записывать / читать данные как строку текста, оканчивающуюся новой строкой, например, с помощью fprintf или fgets.

...