Проблемы с логикой трассировки и выводом кода включают dup () dup2 () и fork () - PullRequest
0 голосов
/ 22 июня 2019

В вопросе от экзамена по операционной системе я пытался отследить следующий код безуспешно.

В вопросе говорится, что предположения:

  • открыт хотя бы STDOUT.

  • foo.txt содержит строку «abcdef» 6 байтов

  • bar.txt содержит строку"567"

  • вывод в ответ a567b.

Может ли кто-нибудь проследить этот код и нарисовать для меня массив дескрипторов файлов?,Заранее спасибо ..

main() {
    char buf[1024];
    int fd_foo = open("foo.txt", O_RDONLY);
    if (fd_foo != 4) {
        dup2(fd_foo, 4);

        close(fd_foo);
    }
    int fd_bar = open("bar.txt", O_RDONLY);
    if (fd_bar != 0) {
        close(0);
        dup(fd_bar);
        close(fd_bar);
    }
    switch (fork()) {
    case -1: exit(1);
    case 0:
        dup2(4, 5);
        close(4);
        execl("child", "child", (char *)NULL);
        break;
    default:
        wait(NULL);
        read(4, buf, 1);
        write(1, buf, 1);
    }
} // main

содержание дочернего исходного файла.

int main() {
    char buf[3];
    read(5, buf, 1);
    write(1, buf, 1);
    read(0, buf, 3);

    write(1, buf, 3);
}

1 Ответ

1 голос
/ 22 июня 2019

Давайте сначала посмотрим на main() в основном файле, а затем на поток main() в дочернем файле.

Прежде чем мы начнем, давайте рассмотрим стандартные присвоения дескриптора файла для Cприложение при запуске из Linux, из stdout (3) - справочная страница Linux .

При запуске программы целочисленные файловые дескрипторы, связанные с потоками stdin, stdout иstderr - 0, 1 и 2 соответственно.Символы препроцессора STDIN_FILENO, STDOUT_FILENO и STDERR_FILENO определяются с этими значениями в.(Применение freopen (3) к одному из этих потоков может изменить номер дескриптора файла, связанного с потоком.)

Далее давайте рассмотрим, что делает системный вызов dup(), с DUP (2) Руководство программиста Linux .

Системный вызов dup () создает копию дескриптора файла oldfd, используя дескриптор неиспользуемого файла с наименьшим номером для нового дескриптора.

После успешного возврата старые и новые файловые дескрипторы могут использоваться взаимозаменяемо.Они ссылаются на одно и то же описание открытого файла (см. Open (2)) и, таким образом, совместно используют смещение файла и флаги состояния файла;например, если смещение файла изменяется с помощью lseek (2) для одного из файловых дескрипторов, смещение также изменяется для другого.

main() в основном файле выглядит какследует с комментариями в качестве комментариев:

main() {
    char buf[1024];

    // open the file foo.txt and then dup() the file handle received from the open()
    // to be file handle number 4. Close the original file handle received.
    int fd_foo = open("foo.txt", O_RDONLY);
    if (fd_foo != 4) {
        dup2(fd_foo, 4);

        close(fd_foo);
    }
    // at this point the file handle 4 refers to the file foo.txt

    // open the file bar.txt and make sure that the file handle received is file handle
    // handle 0. if not then we close file handle 0 and dup the file handle to bar.txt
    // File handle 0 is Standard Input or STDIN.
    int fd_bar = open("bar.txt", O_RDONLY);
    if (fd_bar != 0) {
        close(0);
        dup(fd_bar);
        close(fd_bar);
        // Since dup() looks for the lowest numbered file descriptor and we have
        // just closed file descriptor 0, the result of dup() is to now have the
        // file bar.txt to also be accessed through file handle 0.
    }
    // at this point we have the following file assignments:
    //  - file handle 0 which was to Standard In is now file bar.txt
    //  - file handle 1 is to Standard Out
    //  - file handle 2 is to Standard Error
    //  - file handle 4 is to file foo.txt

    // now do a fork and the forked process will then execute the program whose
    // source code is in the child source file. the child process will
    // inherit our open file handles since we did not specify otherwise.
    switch (fork()) {
    case -1: exit(1);       // if error just exit.
    case 0:
        // we be the forked process so we now
        //  - dup file handle 4 to file handle 5 and close 4
        //  - load in the child process on top of ourselves
        //  - loaded child process will inherit our open file handles
        dup2(4, 5);
        close(4);
        execl("child", "child", (char *)NULL);
        // at this point we now jump to the source code of the child source file
        break;
    default:
        // we are the parent process so now lets just wait for the child to
        // finish. Once it has finished we will then do some final file I/O
        // then exit.
        // Note: while the forked process closed file handle 4, the parent process
        //       has not so file handle 4 is still valid for the parent.
        wait(NULL);
        read(4, buf, 1);     // read 1 character from file foo.txt
        write(1, buf, 1);    // write it to Standard Output
    }
} // main

дочерний процесс, который запускается.

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

  • файловый дескриптор 5 прикреплен к открытому файлу foo.txt
  • файловый дескриптор 0 прикреплен к открытому файлу bar.txt
  • дескриптор файла 1 присоединен к стандартному выводу

Исходный код дочернего файла

int main() {
    char buf[3];
    read(5, buf, 1);    // read one character from foo.txt, the letter "a" from the string "abcdef"
    write(1, buf, 1);   // write it to Standard Out
    read(0, buf, 3);    // read three characters from bar.txt, the string "567"

    write(1, buf, 3);   // write them to Standard out
}

Результатом всего этого является следующий ввод / вывод.

  • запускается основной процесс, настраивает дескрипторы файлов, разветвляется и загружается дочерний

  • основной процесс ожидает завершения дочернего процесса

  • дочерний процесс читает «a» из файла foo.txt, оставляя «bcdef» непрочитанным.

  • дочерний процесс записывает «a» в стандартный вывод

  • дочерний процесс читает «567» из файла bar.txt, ничего не читая

  • дочерний процесс записывает «567» в стандартный вывод

  • дочерний процесс завершается

  • основной процесс продолжает работать

  • основной процесс читает «b» из файла foo.txt, оставляя «cdef» непрочитанным

  • основной процесс записывает «b» в стандартный вывод

  • выход из основного процесса

Результатом этого является то, что «a567b» записывается в Standard Out этими двумя взаимодействующими процессами.Они совместно используют одни и те же файлы, хотя доступ к файлу foo.txt осуществляется двумя разными файловыми дескрипторами, и они используют один и тот же стандартный вывод.

...