Как shell/init создает потоки stdio?

Как shell/init создает потоки stdio?

Я читаю исходный текст Массачусетского технологического институтаОС xv6. Этот фрагмент находится в начале sh.c:

// Ensure that three file descriptors are open.
while((fd = open("console", O_RDWR)) >= 0){
    if(fd >= 3){
      close(fd);
      break;
    }
}

Я понимаю, что это проверка, чтобы увидеть,по меньшей мереОткрываются 3 файловых дескриптора (предположительно для stdin, stdout и stderr) путем проверки того, находится ли вновь выделенный файловый дескриптор выше (или совпадает с) 3.

1) Как можно обращаться к openодному и тому же устройству несколько раз из одного и того же процесса и ожидать разных файловых дескрипторов?

2) Чтобы понять это, я запустил похожий фрагмент на своей хост-машине (x86_64 Linux 4.6.0.1). Тестовая программа многократно opened текстовый файл в цикле, чтобы посмотреть, можем ли мы ожидать другой fd, но она всегда выдавала один и тот же дескриптор файла. Из этого я сделал вывод, что open-ing реального файла и устройства (например /dev/console) как-то отличается, потому что фрагмент из xv6, очевидно, работает (проверено в Qemu). В чем именно разница?

#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>

int main(void)
{
    int fd;
    int cnt = 0;

    while ((fd = open("sample.txt", O_RDWR) > 0)) {
        if (cnt != 10) {
            cnt++;
            printf("File descriptor opened: %d\n", fd);
        } else {
            break;
        }
    }

    return 0;
}

Вот результат его запуска:

$ ./a.out
File descriptor opened: 1
File descriptor opened: 1
[snip]
File descriptor opened: 1
File descriptor opened: 1

РЕДАКТИРОВАТЬОсновываясь на одном из ответов, я запустил straceисполняемый файл и обнаружил, чтоopen действительновозвращает несколько файловых дескрипторов, но все они по какой-то причине не печатаются. Почему бы это?

3) Несколько не по теме, но разве соглашение об использовании потоков stdio в fds 0-2 не является именно этим - соглашением? Например, если инициализирующая последовательность выделила дескрипторы файлов ввода/вывода чему-то другому - повлияет ли это как-то на то, как ее дочерние элементы выполняют свой ввод/вывод?

решение1

На самом деле это 3 вопроса. Немедленно отбросьте вопрос №2, так как программа неверна:

    while ((fd = open("sample.txt", O_RDWR) > 0)) {

Вы, вероятно, имели в виду

    while ((fd = open("sample.txt", O_RDWR)) > 0) {

из-за неправильно расставленных скобок вы проверяете только то, fdбольше ли нуля (что, вероятно, является правильным предположением, поскольку файловые дескрипторы 0, 1 и 2 открыты).

Для #1:openвызов (при успешном выполнении) определен для возврата отдельных файловых дескрипторов. Если устройство не может быть открыто повторно, openвернет -1.

Для #3: конечно, этосоглашение, но и вPOSIXстандартный. В других системах использовались другие соглашения, включая наличие четвертого открытого потока для каждой программы.

Дальнейшее чтение:Использование среды Aegis (июль 1988 г.)
См. страницу 6-9, где говорится, что в Apollo Domain/OS произошла ошибка.входи **выход*

решение2

Нет, код непроверятьдескрипторы, он фактически открывает их. Пока не давая дескрипторов. Каждое открытие даст новый дескриптор файла, т.е. 0,1,2,3. Код прерывается при достижении fd 3, оставляя 0 до 2 открытыми.

Каждый файловый дескриптор — это просто указатель на некоторое место в некотором файле. Поэтому не проблема иметь более одного дескриптора для одного и того же файла.

Если ваша тестовая программа выдает один и тот же fd для разных открытых вызовов, в ней есть ошибка. Пожалуйста, покажите код.

Да, существует строгое соглашение о fd 0-2. Если какой-то код хочет печатать на stdout, он на самом деле печатает на fd 1. Не существует способа «отобразить» stdout на что-то еще.

решение3

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

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


2) Зависит от вашего кода. Умный язык (т.е. не C) может пересчитать переменную, содержащую открытый файл, и закрыть ее непосредственно перед повторным открытием файла. Трудно сказать, не видя кода.

Но небольшой тест: открытие одного и того же файла дважды для одной и той же переменной в Perl приводит к одному и тому же номеру FD:

perl -e 'open F, "test.txt"; printf "%d ", fileno(F); open F, "test.txt"; printf "%d\n", fileno(F)'
3 3

Запуск показывает strace, что файл закрывается непосредственно перед повторным открытием.

При наличии двух различных переменных мы получаем два числа ФД:

perl -e 'open F, "test.txt"; printf "%d ", fileno(F); open G, "test.txt"; printf "%d\n", fileno(G)'
3 4

3) Технически, можно сказать, что стандартные номера файлов — это соглашение. Соглашение, кодифицированное вPOSIX и стандарт ISO C:

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

Но соглашение в любом случае в том, что может быть возможно запустить программу без них, без того, чтобы ядро ​​беспокоилось. Или нет: читая спецификацию вызовов exec, кажется, чторазрешено для реализации открыть что-то для вас:

Если файловый дескриптор 0, 1 или 2 в противном случае был бы закрыт после успешного вызова одной из функций семейства exec, реализации могут открыть неопределенный файл для файлового дескриптора в новом образе процесса.

(Конечно, вы можете закрыть их в самой программе.)

Если вы решите не запускать их при запуске, совместимость будет потеряна:

Если стандартная утилита или соответствующее приложение выполняются с файловым дескриптором 0, не открытым для чтения, или с файловым дескриптором 1 или 2, не открытым для записи, среда, в которой выполняется утилита или приложение, будет считаться несоответствующей, и, следовательно, утилита или приложение могут вести себя не так, как описано в настоящем стандарте.

TheСтраницы руководства Linux излагают это несколько практично:

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

Связанный контент