Вопрос или проблема
Документация о функции open(), ссылка здесь, упоминает три вещи, которые будут связаны между собой: файл, файловый дескриптор и описание файла. Из трех я предположил, что файловый дескриптор — это целое число, которое мы получаем в ответ на вызов функции open(). Если я ошибаюсь, пожалуйста, дайте мне знать. Я не помню, чтобы слышал о том, что такое описание файла или как оно используется.
Мы можем получить файловый дескриптор (int), используя open().
Мы можем получить указатель на файл (FILE *), используя fopen(). Я считаю, что структура FILE содержит файловый дескриптор.
Чтобы абстрагировать, как данные передаются в файлы/из файлов и другие устройства, мы говорим, что создается поток. Я слышал о потоках только в обсуждениях, связанных с fopen(). Так считаются ли функции open() и fopen() создателями потоков?
Также мы можем получить файловый дескриптор структуры FILE, используя функцию fileno(). Можем ли мы использовать функции read()/write(), которые предназначены для работы с файловыми дескрипторами, на потоке, созданном с помощью fopen(), после получения файлового дескриптора из структуры FILE?
И наконец, если у нас есть файловый дескриптор, возможно, переданный нам другой функцией или процессом (Примечание: я не знаю, происходит ли такое на самом деле), есть ли способ определить, был ли файл, на который он ссылается, открыт с помощью open() или fopen()? Или это имеет значение?
Ответ или решение
Ответ на вопрос о дескрипторах файлов и потоках в C
Вопрос касается связи между дескрипторами файлов, файловыми указателями и потоками, создаваемыми с помощью функций open()
и fopen()
. Давайте разберемся с каждым из этих понятий более подробно.
1. Дескриптор файла и файловая дескрипция
Дескриптор файла (file descriptor, fd) — это целочисленное значение, которое используется для идентификации открытого файла в операционной системе. Вызов функции open()
возвращает целое число, которое представляет дескриптор файла. Например:
int fd = open("example.txt", O_RDONLY);
Файловая дескрипция (file description) — это более сложный объект, который включает в себя, помимо самого дескриптора, информацию о состоянии файла (например, текущую позицию чтения или записи, режим открытия и другие параметры). Этот объект управляется ядром операционной системы.
2. Файловый указатель и поток
В отличие от open()
, функция fopen()
открывает файл и возвращает указатель на структуру FILE
, представляющую поток. Эта структура FILE
содержит, среди прочего, дескриптор файла, который можно получить с помощью функции fileno()
:
FILE *fp = fopen("example.txt", "r");
int fd = fileno(fp);
Потоки предоставляют абстракцию, которая упрощает взаимодействие с файлами, позволяя использовать буферизацию и различные режимы ввода-вывода.
3. Создание потоков
Функция open()
не создает поток в смысле, в котором это делает fopen()
. Поток — это уровня абстракции, используемый для работы с буферизованным вводом и выводом. Таким образом, fopen()
создает поток, а open()
просто открывает файл и возвращает дескриптор.
4. Использование read()
и write()
с файловыми указателями
Вы можете использовать функции read()
и write()
с дескриптором, полученным из FILE
структуры через fileno()
. Например:
char buffer[100];
int bytesRead = read(fd, buffer, sizeof(buffer));
Однако для работы с потоками, созданными с помощью fopen()
, предпочтительнее использовать функции стандартной библиотеки, такие как fread()
и fwrite()
, так как они учитывают все особенности потоков, такие как буферизация и управление ошибками.
5. Определение способа открытия файла
Если вам дан дескриптор файла и вы хотите узнать, был ли он открыт с помощью open()
или fopen()
, однозначно определить это невозможно. Оба метода могут предоставлять доступ к одному и тому же файлу, но информация о методе открытия не сохраняется самими дескрипторами. Однако, если вам это необходимо в контексте управления ресурсами или производительности, вам, возможно, стоит проектировать свое приложение так, чтобы отслеживать источник создания дескрипторов.
Заключение
Таким образом, основное различие между open()
и fopen()
заключается в том, что первая работает с низкоуровневыми дескрипторами, а вторая — с более высокоуровневыми потоками. Эти два метода могут использоваться в одном приложении, однако правильность их применения зависит от конкретных потребностей вашего кода.