getline () многократно читает файл, когда используется fork () - PullRequest
0 голосов
/ 27 февраля 2019

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

int main(int argc,char *argv[]){
    int const IN_SIZE = 255;
    char *input = NULL;
    size_t len = IN_SIZE;
    // get file address
    fileAdr = argv[2];

    // open file
    srcFile = fopen(fileAdr, "r");

    if (srcFile == NULL) {
        printf("No such file!\n");
        exit(-1);
    }

    while (getline( &input, &len, srcFile) != -1) {
        strtok(input, "\n");
        printf("%s\n", input);
        // some code that parses input, firstArgs == input
        execSimpleCmd(firstArgs);            
    }
    fclose(srcFile);
}

Я использую fork () в моей программе и, скорее всего, это вызывает эту проблему.

void execSimpleCmd(char **cmdAndArgs) {

    pid_t pid = fork();
    if (pid < 0) {
        // error
        fprintf(stderr, "Fork Failed");
        exit(-1);
    } else if (pid == 0) {
        // child process
        if (execvp(cmdAndArgs[0], cmdAndArgs) < 0) {
            printf("There is no such command!\n");
        }
        exit(0);
    } else {
        // parent process
        wait(NULL);
        return;
    }
}

Кроме того, иногда программа читает и печатает комбинации из нескольких строк.Например, если входной файл, как показано ниже:

ping
ww    
ls
ls -l
pwd

, он печатает что-то вроде pwdg, pwdww и т. Д. Как это исправить?

1 Ответ

0 голосов
/ 28 февраля 2019

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

POSIX-описание fclose() имеет следующую фразу:

[CX] [Option Start] Если файл еще не находится в EOF, и файл способен искать, смещение файлалежащее в основе описание открытого файла должно быть установлено в позицию файла потока , если поток является активным дескриптором описания базового файла.

(где CX означает расширение длястандарт ISO C и exit(), конечно, работает fclose() на всех потоках.)

Я могу воспроизвести странное поведение этой программы (в Debian 9.8):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
    FILE *f;
    if ((f = fopen("testfile", "r")) == NULL) {
        perror("fopen");
        exit(1);
    }

    int right = 0;
    if (argc > 1)
        right = 1;

    char *line = NULL;
    size_t len = 0;
    // first line 
    getline(&line, &len, f);
    printf("%s", line);

    pid_t p = fork();
    if (p == -1) {
        perror("fork");
    } else if (p == 0) {
        if (right)
            _exit(0);  // exit the child 
        else
            exit(0);   // wrong way to exit
    } else {
        wait(NULL);  // parent
    }

    // rest of the lines
    while (getline(&line, &len, f) > 0) {
        printf("%s", line);
    }

    fclose(f);
}

Затем:

$ printf 'a\nb\nc\n' > testfile
$ gcc -Wall -o getline getline.c
$ ./get
getline   getline2  
$ ./getline
a
b
c
b
c

Запуск его с strace -f ./getline ясно показывает, что ребенок ищет файловый дескриптор назад:

clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f63794e0710) = 25117
strace: Process 25117 attached
[pid 25116] wait4(-1,  <unfinished ...>
[pid 25117] lseek(3, -4, SEEK_CUR)      = 2
[pid 25117] exit_group(1)               = ?

(Я не видел поиск скод, который не предполагал разветвления, но я не знаю почему.)

* 1034Итак, что происходит, так это то, что библиотека C в основной программе считывает блок данных из файла, и приложение выводит первую строку.После разветвления дочерний процесс выходит и ищет fd обратно туда, где находится указатель файла уровня приложения.Затем родитель продолжает работу, обрабатывает оставшуюся часть буфера чтения и, когда он завершает, продолжает чтение из файла.Поскольку дескриптор файла был найден обратно, строки, начинающиеся со второй, снова доступны.

В вашем случае повторное fork() на каждой итерации, похоже, приводит к бесконечному циклу.

Использование _exit() вместо exit() в дочернем узле решает проблему в этом случае , так как _exit() выходит только из процесса, он не выполняет никаких операций с буферами stdio.

При _exit() все выходные буферы также не сбрасываются, поэтому вам нужно будет вручную fflush() вызвать stdout и любые другие файлы, в которые вы пишете.

Однако, если вы сделали это с другой стороны, когда ребенок читает и буферизует больше, чем обрабатывает, тогда ребенку было бы полезно найти обратно fd, чтобы родитель мог продолжить с того места, где ребенок фактически ушел.

Другим решением было бы не смешивать stdio с fork().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...