Правильный способ использования fdopen

Я имею в виду связать дескриптор файла с указателем файла и использовать его для записи. Я собрал программу io.cc ниже:

int main() {
    ssize_t nbytes;
    const int fd = 3;
    char c[100] = "Testing\n";
    nbytes = write(fd, (void *) c, strlen(c));     // Line #1
    FILE * fp = fdopen(fd, "a");
    fprintf(fp, "Writing to file descriptor %d\n", fd);
    cout << "Testing alternate writing to stdout and to another fd" << endl;
    fprintf(fp, "Writing again to file descriptor %d\n", fd);
    close(fd);     // Line #2
    return 0;
}

Я могу поочередно комментировать строки 1 и/или 2, компилировать/запускать

./io 3> io_redirect.txt

и проверьте содержимое io_redirect.txt. Всякий раз, когда строка 1 не закомментирована, она создает в io_redirect.txt ожидаемую строку Testing\n. Если строка 2 прокомментирована, я получаю ожидаемые строки

Writing to file descriptor 3
Writing again to file descriptor 3

в io_redirect.txt. Но если он не прокомментирован, эти строки не отображаются в io_redirect.txt.

  • Почему это?
  • Как правильно использовать fdopen?

ПРИМЕЧАНИЕ. Кажется, это правильный подход для (частичного) ответа на Smart- запись в произвольный файловый дескриптор из C/C++ Я говорю "частичный", так как я смогу использовать C-стиль fprintf. Я все еще хотел бы использовать stream<< в стиле С++.

EDIT: я забыл о fclose(fp). Это «закрывает» часть вопроса.


person sancho.s ReinstateMonicaCellio    schedule 17.05.2020    source источник
comment
Если вы разобрались с одним из своих вопросов, не могли бы вы вместо того, чтобы добавить исправление, удалить его?   -  person John Kugelman    schedule 17.05.2020
comment
@tadman - я проверил, и fclose(fp):, и fflush(fp): close(fd): работают нормально. Будут ли они эквивалентны?   -  person sancho.s ReinstateMonicaCellio    schedule 17.05.2020
comment
Вы смешиваете буферизованную и небуферизованную записи, что всегда будет проблемой. Какой вариант использования здесь? Вы должны выбрать один и только один метод для взаимодействия с дескриптором файла в реальном коде. Любой метод хорош, если вы последовательны. Если вам нужно перейти от одного метода к другому, убедитесь в сбросе и избегайте записи после передачи.   -  person tadman    schedule 17.05.2020
comment
@tadman - Отлично. Связано: stackoverflow.com/a/61708362/2707864   -  person sancho.s ReinstateMonicaCellio    schedule 17.05.2020
comment
@JohnKugelman - Потому что я думаю понял ответ на вопрос. Но я чувствую, что у меня нет авторитетного ответа, и опубликованные комментарии проливают на это свет. Я чувствую, что в данном случае это более полезно, как есть. В остальных случаях я конечно редактирую, чтобы убрать вопрос.   -  person sancho.s ReinstateMonicaCellio    schedule 17.05.2020


Ответы (1)


Почему это?

Открытый поток (поток - это открытый FILE*) буферизуется блоками, поэтому ничего не записывается в место назначения до того, как файл будет сброшен. Выход из приложения закрывает все открытые потоки, что сбрасывает поток.

Поскольку вы закрываете базовый файловый дескриптор перед сбросом потока, поведение вашей программы не определено. Я настоятельно рекомендую вам прочитать posix 2.5.1 Взаимодействие файловых дескрипторов и Стандартные потоки ввода-вывода (которые, тем не менее, написаны ужасным языком), из которых:

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

...

Для первой ручки применяется первое применимое условие ниже. ...

  • ...

  • Если это поток, который открыт для записи или добавления (но не открыт для чтения), приложение должно либо выполнить fflush(), либо поток должен быть закрыт.

Дескриптор — это файловый дескриптор или поток. Активный дескриптор — это последний дескриптор, с которым вы что-то делали.

Поток fp — это активный дескриптор, открытый для добавления к файловому дескриптору 3. Поскольку fp является активным дескриптором и не сбрасывается, а вы переключаете активный дескриптор на fd с помощью close(fd), поведение вашей программы не определено.

Я предполагаю, что, скорее всего, ваша реализация стандартной библиотеки C вызывает fflush(fp) после возврата main, потому что fd закрывается, какой-то внутренний вызов write(3, ...) возвращает ошибку, и в вывод ничего не записывается.

Как правильно использовать fdopen?

Представленное вами использование является правильным способом использования fdopen.

person KamilCuk    schedule 18.05.2020
comment
Итак, есть какая-то ошибка, о которой я мог бы узнать/проверить? Каким будет правильный способ его ловли? - person sancho.s ReinstateMonicaCellio; 18.05.2020
comment
Неопределенное поведение означает, что поведение вашей программы не определено, может случиться что угодно. Невозможно проверить на неопределенное поведение, потому что, как только оно произойдет, может случиться все, что угодно, поэтому единственный способ — написать программы так, чтобы поведение было определено. С практической точки зрения, я бы посоветовал подтвердить с помощью исходного кода glibc, что fclose (или fflush) в таких случаях возвращает ненулевое значение. - person KamilCuk; 18.05.2020
comment
Но вы сказали, что какой-то внутренний вызов write(3,...) возвращает ошибку... Думаю, это не неопределенное поведение. - person sancho.s ReinstateMonicaCellio; 18.05.2020
comment
That is not undefined behavior Стандарт буквально гласит: the result is undefined. Насколько я понимаю, что делает реализация, не имеет значения, ваша программа открывает врата в ад и порождает носовых демонов . - person KamilCuk; 18.05.2020