Подтвердить что ты не робот

Может ли кто-нибудь объяснить простое описание "файлового дескриптора" после fork()?

В "Расширенное программирование в среде Unix", второе издание, W. Richard Stevens.

Раздел 8.3 функция fork.

Здесь описание:

Важно, чтобы родительский элемент и дочерний элемент имели одинаковое смещение файла.

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

[1. Что это значит? если родительский std-вывод перенаправляется на файл 'file1', например, что следует обновлять после записи потомка? родительский исходный сдвиг вывода std или перенаправленный вывод (i.e file1) offset? Не может быть позже, правильно?]

[2. Как производится обновление? через ядро ​​явно, OS неявно, самим дескриптором файлов? После fork я подумал, что родитель и ребенок пошли своими путями и имеют собственную COPY файлового дескриптора. Итак, как ребенок обновляет смещение с родительской стороны?]

В этом случае ребенок может писать стандартный вывод, пока родитель его ждет; по завершении ребенка родитель может продолжить запись на стандартный вывод, зная, что его вывод будет добавлен к тому, что написал ребенок. Если родительский и дочерний объекты не разделяли одно и то же смещение файла, такого типа взаимодействия было бы сложнее выполнить и потребовало бы явных действий родителя.

Если оба родителя и ребенка записываются в один и тот же дескриптор, без какой-либо формы синхронизации, например, если родитель ожидает ожидание дочернего элемента, их вывод будет смешанным (предполагая, что это дескриптор, который был открыт перед fork). Хотя это возможно, это не обычный режим работы.

Существует два нормальных случая для обработки дескрипторов после fork.

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

  • И родитель, и ребенок идут своими путями. Здесь, после fork, родитель закрывает дескрипторы, которые ему не нужны, и ребенок делает то же самое. Таким образом, они не мешают другим открытым дескрипторам. Этот сценарий часто применяется к сетевым серверам.

[3. Когда вызывается fork(), все, что я понимаю, это то, что ребенок получает КОПИЮ того, что имеет родитель, дескриптор файла в этом случае и делает свою вещь. Если какое-либо смещение изменяет файловый дескриптор этого родительского и дочернего долей, это может быть только потому, что дескриптор помнит само смещение. Я прав?]

Извините, что я новичок в концепциях.

Любая помощь? Спасибо.

4b9b3361

Ответ 1

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

В качестве примера можно использовать эту программу:

#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(void)
{
    int fd;

    fd = open("output", O_CREAT|O_TRUNC|O_WRONLY, 0666);

    if(!fork()) {
        /* child */
        write(fd, "hello ", 6);
        _exit(0);
    } else {
        /* parent */
        int status;

        wait(&status);
        write(fd, "world\n", 6);
    }
}

(Вся ошибка проверена)

Если мы скомпилируем эту программу, назовите ее hello и запустите ее следующим образом:

./hello

вот что происходит:

Программа открывает файл output, создавая его, если он уже не существует или обрезает его до нулевого размера, если он существует. Ядро создает описание файла (в ядре Linux это struct file) и связывает его с файловым дескриптором для вызывающего процесса (самым низким неотрицательным целым, еще не используемым в этой таблице дескриптора файла процесса). Дескриптор файла возвращается и назначается fd в программе. Для аргумента предположим, что fd равно 3.

В программе есть fork(). Новый дочерний процесс получает копию таблицы дескриптора родительского файла, но описание файла не копируется. Номер записи 3 в таблицах файлов обоих процессов указывает на тот же struct file.

Родительский процесс ждет, пока дочерний процесс пишет. Запись ребенка заставляет первую половину "hello world\n" быть сохраненной в файле и продвигает смещение файла на 6. Смещение файла находится в struct file!

Ребенок завершает работу, родительский wait() заканчивается, а родитель записывает, используя fd 3, который все еще связан с тем же описанием файла, у которого его смещение файла обновлено дочерним элементом write(). Таким образом, вторая половина сообщения сохраняется после первой части, а не перезаписывает ее, как это было бы сделано, если бы родитель имел смещение файла с нулем, что было бы, если описание файла не было общим.

Наконец родитель выходит, и ядро ​​видит, что struct file больше не используется и освобождает его.

Ответ 2

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

Таблица filedescript пользователя (часть записи таблицы процессов), таблица таблицы и таблицы inode (таблица v- node). Теперь указатель filedescriptor (который является индексом к таблице дескриптора файла) указывает на запись таблицы файлов, которая указывает на запись таблицы inode.
Теперь смещение файла (позиция, из которой происходит следующее чтение/запись), находится в таблице Файл.

Скажем, у вас есть файл, открытый в родительском, это означает, что у него есть дескриптор, файл запись в таблице и ссылка на inode.
Теперь, когда вы создаете ребенка, таблица описателей файлов копируется для дочернего элемента. Таким образом, счетчик ссылок в записи таблицы файлов (для этого открытого дескриптора) увеличивается, что означает, что теперь есть две ссылки для одной записи в таблице файлов.

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

  • Что это значит? если родительский std-вывод перенаправляется на файл 'file1', например, что следует обновлять после записи потомка? родительский исходный выходной сигнал std или перенаправлено выключение (i.e file1) смещения? Не может быть позже, правильно?]

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

Теперь скажем, что ребенок выходит. Таким образом, родитель выходит из ожиданий и пишет что-то по стандарту (который перенаправлен). Теперь, когда родительский вывод вызова записи будет помещен → после данных, которые были написаны дочерним. Почему → , поскольку значение тока смещения теперь изменяется после того, как ребенок записал.

 Parent ( )
  {
    open a file for writing, that is get the 
    descriptor( say fd);
    close(1);//Closing stdout
    dup(fd); //Now writing to stdout  means writing to the file
    close(fd)
        //Create a child that is do a  fork call.
    ret = fork();
    if ( 0 == ret )
    {
        write(1, "Child", strlen("Child");
        exit ..
    }
        wait(); //Parent waits till child exit.

         write(1, "Parent", strlen("Parent");
    exit ..
}

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

2. Как обновляется обновление? через ядро ​​явно, OS неявно, самим дескриптором файлов? После развилки я думал, что родитель и ребенок пошли своими путями и имеет собственную COPY файлового дескриптора. Итак, как ребенок обновляет смещение с родительской стороны?]

Now I think the answer is clear-> by the system call that is by the OS.

[3. Когда вызывается fork(), все, что я понимаю, это то, что ребенок получает КОПИЮ того, что имеет родитель, файловый дескриптор в этом случае, и делает свою вещь. Если какое-либо смещение изменяет файловый дескриптор этого родительского и дочернего долей, это может быть только потому, что дескриптор помнит само смещение. Я прав?]

Это также должно быть ясно. Запись таблицы файлов пользователей указывает на таблицу файлов запись таблицы (которая содержит смещение).

Другими словами, системные вызовы могут извлекать смещение из дескриптора.