В системах Unix, почему мы должны явно `open ()` и `close ()` файлы, чтобы иметь возможность `read ()` или `write ()` их?

50

Почему open()и close()существуют в дизайне файловой системы Unix?

Может ли ОС просто обнаружить первый раз read()или write()была вызвана и делать то, open()что обычно делает?

user5977637
источник
22
Стоит отметить, что эта модель не является частью файловой системы, а скорее Unix API . Файловая система просто заботится о том, куда на диске идут байты и куда помещать имя файла и т. Д. Было бы совершенно возможно иметь альтернативную модель, которую вы описываете, поверх файловой системы Unix, такой как UFS или ext4, это было бы до ядро, чтобы перевести эти вызовы в надлежащие обновления для файловой системы (так же, как сейчас).
Марсель
18
Как сформулировано, я думаю, что это больше о том, почему open()существует. «Не могла ли ОС просто обнаружить первый раз read () или write () и сделать то, что обычно делает open ()?» Есть ли соответствующее предложение для закрытия ?
Джошуа Тейлор
7
Как бы вы сказали read()или write()какой файл для доступа? Предположительно, пройдя путь. Что если путь к файлу изменится, когда вы к нему обращаетесь (между двумя read()или write()вызовами)?
user253751 25.02.16
2
Также вы обычно не делаете контроль доступа включенным read()и write()просто включенным open().
Павел Шимерда
6
@Johnny: Возможно, вы забываете, насколько ограниченным было оборудование в те дни. PDP-7, на котором Unix был впервые реализован, имел (по данным Google) максимум 64 КБ ОЗУ и тактовую частоту 0,333 МГц - гораздо меньше, чем простой микроконтроллер в наши дни. Выполнение такой сборки мусора или использование системного кода для контроля доступа к файлам поставили бы систему на колени.
jamesqf

Ответы:

60

Деннис Ритчи упоминает в «Эволюции Unix время обмена системы» , что openи closeвместе с read, writeи creatприсутствовали в правой системе с самого начала.

Я думаю, что система без openи closeне будет немыслимой, однако я считаю, что это усложнит дизайн. Как правило, вы хотите сделать несколько вызовов чтения и записи, а не только один, и это, вероятно, было особенно верно на тех старых компьютерах с очень ограниченным объемом оперативной памяти, на которых возникла UNIX. Наличие дескриптора, который поддерживает текущее положение файла, упрощает это. Если readилиwriteдолжны были вернуть дескриптор, они должны были бы вернуть пару - дескриптор и их собственный статус возврата. Часть ручки пары была бы бесполезна для всех других вызовов, что сделало бы это расположение неловким. Выход из состояния курсора на ядро ​​позволяет повысить эффективность не только за счет буферизации. Существует также некоторая стоимость, связанная с поиском пути - наличие дескриптора позволяет заплатить его только один раз. Кроме того, некоторые файлы в мировоззрении UNIX даже не имеют пути к файловой системе (или не имеют - теперь они делают с такими вещами, как /proc/self/fd).

PSkocik
источник
7
Стоимость поиска пути и проверки разрешения и т. Д. И т. Д. Очень значительна. Если вы хотите создать систему без open/ close, вы наверняка захотите реализовать такие вещи, как /dev/stdoutразрешить трубопровод.
Питер Кордес
5
Я думаю, что другой аспект этого заключается в том, что вы можете сохранить этот дескриптор в одном и том же файле при использовании нескольких операций чтения, когда вы оставляете файл открытым. В противном случае возможны случаи, когда другой процесс отменяет связь и воссоздает файл с тем же именем, а чтение файла по частям может оказаться совершенно непоследовательным. (Отчасти это может зависеть и от файловой системы.)
Bruno
2
Я разработал один без close (); Вы передаете номер индекса и смещение в read () и write (). Я не могу обойтись без open () очень легко, потому что именно там живет разрешение имен.
Джошуа
3
@Joshua: такая система имеет принципиально другую семантику, потому что дескрипторы файлов Unix ссылаются не на файлы (inode), а на описания файлов , которых может быть много для данного файла (inode).
R ..
@Joshua, вы просто переименовали open()в get_inode()и сделал всю систему более жесткой (невозможно читать / писать один и тот же файл в нескольких позициях одновременно).
vonbrand
53

Тогда все из readи writeвызовы должны передавать эту информацию по каждой операции:

  • название файла
  • права доступа к файлу
  • добавляется ли вызывающий или создает
  • ли абонент сделать работу с файлом (отбрасывать неиспользованные для чтения буферов и обеспечение записи-буферов действительно закончили писать)

Считаете ли вы независимые вызовы open , read, writeи closeбыть проще , чем одноцелевой I / O сообщение основано на вашей философии дизайна. Разработчики Unix решили использовать простые операции и программы, которые можно комбинировать разными способами, а не одну операцию (или программу), которая делает все.

Томас Дики
источник
Вызывающим также в большинстве случаев необходимо указать желаемое смещение в файле. В некоторых ситуациях (например, протокол UDP, который обеспечивает доступ к данным), когда каждый запрос независимо идентифицирует файл и смещение, может быть полезным, поскольку он устраняет необходимость для сервера поддерживать состояние, но в целом более удобно иметь сервер следить за положением файла. Кроме того, как отмечено в другом месте, код, который собирается записывать файлы, часто должен заблокировать их заранее и заблокировать их позже; расчесывать эти операции с открытием / закрытием очень удобно.
суперкат
5
«Файл» может не иметь имени или прав доступа в первую очередь; readи writeне ограничиваются файлами, которые живут в файловой системе, и это фундаментальное решение для разработки в Unix, как объясняет pjc50.
reinierpost
1
Кроме того, где в файле для чтения / записи его - начало, конец или произвольная позиция (как правило, сразу после окончания последней операции чтения / записи) - ядро ​​отслеживает это для вас (с режимом направить все записи в конец файла, иначе файлы открываются с позиции в начале и продвигаются при каждом чтении / записи и могут быть перемещены с помощью lseek)
Random832
51

Концепция дескриптора файла важна из-за выбора дизайна UNIX, что «все является файлом», включая вещи, которые не являются частью файловой системы. Такие как ленточные накопители, клавиатура и экран (или телетайп!), Перфорированные устройства чтения карт / лент, последовательные соединения, сетевые соединения и (ключевое изобретение UNIX) прямые соединения с другими программами, называемыми «каналами».

Если вы посмотрите на многие из простого стандартного UNIX утилита типа grep, особенно в их оригинальной версии, вы заметите , что они не включают в себя вызовы к open()и , close()но только readи write. Дескрипторы файла устанавливаются оболочкой вне программы и передаются при запуске. Поэтому программе не нужно заботиться о том, записывает ли она файл или другую программу.

А также open, другие способы получения файлов дескрипторы socket, listen, pipe, dup, и очень Heath Robinson механизм для передачи дескрипторов файлов через трубу: https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux -разъем

Изменить: некоторые лекционные заметки, описывающие слои косвенности и как это позволяет O_APPEND работать разумно. Обратите внимание, что хранение данных inode в памяти гарантирует, что системе не придется идти и извлекать их снова для следующей операции записи.

pjc50
источник
1
Кроме того creat, и listenне создает fd, но когда (и если) поступает запрос во время прослушивания, acceptсоздается и возвращается fd для нового (подключенного) сокета.
dave_thompson_085
18
Это правильный ответ. Известный (небольшой) набор операций над файловыми дескрипторами является объединяющим API для всех видов ресурсов, которые производят или потребляют данные. Эта концепция очень успешна. Строка могла бы иметь синтаксис, определяющий тип ресурса вместе с фактическим местоположением (URL-адрес кого-нибудь?), Но копирование строк, вокруг которых занимают несколько процентов доступной оперативной памяти (что было на PDP 7–16 кБ?), Представляется чрезмерным ,
Питер - Восстановить Монику
Возможно, было бы, если бы низкоуровневые вызовы и оболочка были разработаны одновременно. Но pipeбыл представлен через несколько лет после начала разработки под Unix.
Томас Дики
1
@Thomas Dickey: Это просто показывает, насколько хорош был оригинальный дизайн, поскольку он допускал простое расширение для pipe & c :-)
jamesqf
Но, следуя этой аргументации, этот ответ не дает ничего нового.
Томас Дики
10

Ответ - нет, потому что open () и close () создают и уничтожают дескриптор соответственно. Есть моменты (ну, действительно, все время, действительно), когда вы можете захотеть гарантировать, что вы являетесь единственным вызывающим абонентом с определенным уровнем доступа, так как другой вызывающий (например) записывающий файл, который вы анализируете, неожиданно может оставить приложение в неизвестном состоянии или приводящее к живому или тупиковому состоянию, например, лемма «Обедающие философы».

Даже без этого соображения есть последствия для производительности, которые следует учитывать; close () позволяет файловой системе (если это уместно или если вы вызвали ее) очистить буфер, который вы занимали, дорогая операция. Несколько последовательных изменений в потоке в памяти гораздо эффективнее, чем несколько по существу не связанных циклов чтения-записи-изменения в файловой системе, которая, как вы знаете, существует на расстоянии полмира, разбросанная по центру обработки данных с большой задержкой. Даже при локальном хранении память обычно на много порядков быстрее, чем объемное хранилище.

msaunier
источник
7

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

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

あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ
источник
5

Поскольку путь к файлу может двигаться, если вы предполагаете, что он останется прежним.

Mehrdad
источник
4

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

Если в ОС не было открытия / закрытия, то с помощью readили writeэти действия с файлами все равно должны были бы выполнять инициализацию, очистку буфера / управление и т. Д. Каждый раз. Это много накладных расходов для повторного чтения и записи.

PeterT
источник
Не забывайте, что open () и close () сохраняют также позицию в файле (для следующего чтения или следующей записи). Поэтому в конце или read () и write () потребуется структура для обработки всех параметров или аргументы для каждого параметра. Создание структуры (сайт программиста) эквивалентно открытию, поэтому, если ОС также знает об открытом, у нас есть только больше преимуществ.
Джакомо Катенацци
1

Unix-мантра - это «предлагать один способ ведения дел», что означает «разложение» на (повторно используемые) кусочки, которые можно объединять по желанию. Т.е. в этом случае отделите создание и уничтожение файловых дескрипторов от их использования. Важные преимущества появились позже, с каналами и сетевыми подключениями (ими также манипулируют с помощью файловых дескрипторов, но они создаются другими способами). Возможность доставки файловых дескрипторов (например, передача их дочерним процессам в виде «открытых файлов», которые выживают exec(2)и даже несвязанным процессам через канал) возможна только таким способом. Особенно, если вы хотите предложить контролируемый доступ к защищенному файлу. Таким образом, вы можете, например, открыть/etc/passwd для записи и передачи его дочернему процессу, которому не разрешено открывать этот файл для записи (да, я знаю, что это нелепый пример, не стесняйтесь редактировать что-то более реалистичное).

vonbrand
источник