Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Лабораторная работа № 5. Организация файловой системы в LINUX. Работа с файлами и директориями. Понятие о memory mapped файлахСтр 1 из 7Следующая ⇒
Лабораторная работа № 5. Организация файловой системы в LINUX. Работа с файлами и директориями. Понятие о memory mapped файлах ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ Разделы носителя информации (partitions) в LINUX Физические носители информации – магнитные или оптические диски, ленты и т.д., использующиеся как физическая основа для хранения файлов, в операционных системах принято логически делить на разделы (partitions) или логические диски. Причем слово "делить" не следует понимать буквально, в некоторых системах несколько физических дисков могут быть объединены в один раздел. В операционной системе LINUX физический носитель информации обычно представляет собой один или несколько разделов. В большинстве случаев разбиение на разделы производится линейно, хотя некоторые варианты LINUX могут допускать некое подобие древовидного разбиения. Количество разделов и их размеры определяются при форматировании диска. Поскольку форматирование диска относится к области администрирования операционных систем, оно здесь рассматриваться не будет. Наличие нескольких разделов на диске может определяться требованиями операционной системы или пожеланиями пользователя. Допустим, пользователь хочет разместить на одном жестком диске несколько операционных систем с возможностью попеременной работы в них, тогда он размещает каждую операционную систему в своем разделе. Или другая ситуация: необходимость работы с несколькими видами файловых систем. Под каждый тип файловой системы выделяется отдельный логический диск. Третий вариант – это разбиение диска на разделы для размещения в разных разделах различных категорий файлов. Скажем, в одном разделе помещаются все системные файлы, а в другом разделе – все пользовательские файлы. Примером операционной системы, внутренние требования которой приводят к появлению нескольких разделов на диске, могут служить ранние версии MS-DOS, для которых максимальный размер логического диска не превышал 32 Мбайт. Для простоты далее будем полагать, что у нас имеется только один раздел и, следовательно, одна файловая система. Вопросы взаимного сосуществования нескольких файловых систем в рамках одной операционной системы мы затронем далее перед обсуждением реализации подсистемы ввода-вывода.
Понятие суперблока Мы уже коснулись содержимого заголовка раздела, когда говорили о массиве индексных узлов файловой системы. Оставшуюся часть заголовка в ext2fs принято называть суперблоком. Суперблок хранит информацию, необходимую для правильного функционирования файловой системы в целом. В нем содержатся, в частности, следующие данные.
В некоторых модификациях файловой системы ext2fs последние два списка выносятся за пределы суперблока, но остаются в заголовке раздела. При первом же обращении к файловой системе суперблок обычно целиком считывается в адресное пространство ядра для ускорения последующих обращений. Поскольку количество логических блоков и индексных узлов в файловой системе может быть весьма большим, нецелесообразно хранить списки свободных блоков и узлов в суперблоке полностью. При работе с индексными узлами часть списка свободных узлов, находящаяся в суперблоке, постепенно убывает. Когда список почти исчерпан, операционная система сканирует массив индексных узлов и заново заполняет список. Часть списка свободных логических блоков, лежащая в суперблоке, содержит ссылку на продолжение списка, расположенное где-либо в блоках данных. Когда эта часть оказывается использованной, операционная система загружает на освободившееся место продолжение списка, а блок, применявшийся для его хранения, переводится в разряд свободных. Системный вызов ftruncate() Прототип системного вызова #include <sys/types.h> #include <unistd.h> int ftruncate(int fd, size_t length); Описание системного вызова Системный вызов ftruncate предназначен для изменения длины открытого регулярного файла.
Параметр fd является дескриптором соответствующего файла, т. е. значением, которое вернул системный вызов open(). Параметр length – значение новой длины для этого файла. Если параметр length меньше, чем текущая длина файла, то вся информация в конце файла, не влезающая в новый размер, будет потеряна. Если же он больше, чем текущая длина, то файл будет выглядеть так, как будто мы дополнили его до недостающего размера нулевыми байтами. Возвращаемое значение Системный вызов возвращает значение 0 при нормальном завершении и значение -1 при возникновении ошибки. Операции чтения из файла и записи в файл. Для операций чтения из файла и записи в файл применяются системные вызовы read() и write(), которые мы уже обсуждали ранее. Надо отметить, что их поведение при работе с файлами имеет определенные особенности, связанные с понятием указателя текущей позиции в файле. При работе с файлами информация записывается в файл или читается из него, начиная с места, определяемого указателем текущей позиции в файле. Значение указателя увеличивается на количество реально прочитанных или записанных байт. При чтении информации из файла она не пропадает из него. Если системный вызов read возвращает значение 0, то это означает, что достигнут конец файла. Операция изменения указателя текущей позиции. Системный вызов lseek(). С точки зрения процесса все регулярные файлы являются файлами прямого доступа. В любой момент процесс может изменить положение указателя текущей позиции в открытом файле с помощью системного вызова lseek(). Особенностью этого системного вызова является возможность помещения указателя текущей позиции в файле за конец файла (т.е. возможность установления значения указателя большего, чем длина файла). При любой последующей операции записи в таком положении указателя файл будет выглядеть так, как будто возникший промежуток от конца файла до текущей позиции, где начинается запись, был заполнен нулевыми байтами. Если операции записи в таком положении указателя не производится, то никакого изменения файла, связанного с необычным значением указателя, не произойдет (например, операция чтения будет возвращать нулевое значение для количества прочитанных байтов).
Операция добавления информации в файл. Флаг O_APPEND. Хотя эта операция по сути дела является комбинацией двух уже рассмотренных операций, мы считаем нужным упомянуть ее особо. Если открытие файла системным вызовом open() производилось с установленным флагом O_APPEND, то любая операция записи в файл будет всегда добавлять новые данные в конец файла, независимо от предыдущего положения указателя текущей позиции (как если бы непосредственно перед записью был выполнен вызов lseek() для установки указателя на конец файла).
Операции создания связей. Команда ln, системные вызовы link() и symlink(). С операциями, позволяющими изменять логическую структуру файловой системы, такими как создание файла, мы уже сталкивались в этом разделе. Однако операции создания связи служат для проведения новых именованных ребер в уже существующей структуре без добавления новых узлов или для опосредованного проведения именованного ребра к уже существующему узлу через файл типа "связь" и неименованное ребро. Такие операции мы до сих пор не рассматривали, поэтому давайте остановимся на них подробнее. Допустим, что несколько программистов совместно ведут работу над одним и тем же проектом. Файлы, относящиеся к этому проекту, вполне естественно могут быть выделены в отдельную директорию так, чтобы не смешиваться с файлами других пользователей и другими файлами программистов, участвующих в проекте. Для удобства каждый из разработчиков, конечно, хотел бы, чтобы эти файлы находились в его собственной директории. Этого можно было бы добиться, копируя по мере изменения новые версии соответствующих файлов из директории одного исполнителя в директорию другого исполнителя. Однако тогда, во-первых, возникнет ненужное дублирование информации на диске. Во-вторых, появится необходимость решения тяжелой задачи: синхронизации обновления замены всех копий этих файлов новыми версиями.
Существует другое решение проблемы. Достаточно разрешить файлам иметь несколько имен. Тогда одному физическому экземпляру данных на диске могут соответствовать различные имена файла, находящиеся в одной или в разных директориях. Подобная операция присвоения нового имени файлу (без уничтожения ранее существовавшего имени) получила название операции создания связи. В операционной системе LINUX связь может быть создана двумя различными способами. Первый способ, наиболее точно следующий описанной выше процедуре, получил название способа создания жесткой связи (hard link). С точки зрения логической структуры файловой системы этому способу соответствует проведение нового именованного ребра из узла, соответствующего некоторой директории, к узлу, соответствующему файлу любого типа, получающему дополнительное имя. С точки зрения структур данных, описывающих строение файловой системы, в эту директорию добавляется запись, содержащая дополнительное имя файла и номер его индексного узла (уже существующий!). При таком подходе и новое имя файла, и его старое имя или имена абсолютно равноправны для операционной системы и могут взаимозаменяемо использоваться для осуществления всех операций. Использование жестких связей приводит к возникновению двух проблем. Первая проблема связана с операцией удаления файла. Если мы хотим удалить файл из некоторой директории, то после удаления из ее содержимого записи, соответствующей этому файлу, мы не можем освободить логические блоки, занимаемые файлом, и его индексный узел, не убедившись, что у файла нет дополнительных имен (к его индексному узлу не ведут ссылки из других директорий), иначе мы нарушим целостность файловой системы. Для решения этой проблемы файлы получают дополнительный атрибут – счетчик жестких связей (или именованных ребер), ведущих к ним, который, как и другие атрибуты, располагается в их индексных узлах. При создании файла этот счетчик получает значение 1. При создании каждой новой жесткой связи, ведущей к файлу, он увеличивается на 1. Когда мы удаляем файл из некоторой директории, то из ее содержимого удаляется запись об этом файле, и счетчик жестких связей уменьшается на 1. Если его значение становится равным 0, происходит освобождение логических блоков и индексного узла, выделенных этому файлу. Вторая проблема связана с опасностью превращения логической структуры файловой системы из ациклического графа в циклический и с возможной неопределенностью толкования записи с именем ".." в содержимом директорий. Для их предотвращения во всех существующих вариантах операционной системы LINUX запрещено создание жестких связей, ведущих к уже существующим директориям (несмотря на то, что POSIX-стандарт для операционной системы LINUX разрешает подобную операцию для пользователя root). Поэтому мы и говорили о том, что в узел, соответствующий файлу типа " директория ", не может вести более одного именованного ребра. (В операционной системе Linux по непонятной причине дополнительно запрещено создание жестких связей, ведущих к специальным файлам устройств.)
Для создания жестких связей применяются команда операционной системы ln без опций и системный вызов link(). Надо отметить, что системный вызов link() является одним из немногих системных вызовов, совершающих операции над файлами, которые не требуют предварительного открытия файла, поскольку он подразумевает выполнение единичного действия только над содержимым индексного узла, выделенного связываемому файлу.
Второй способ создания связи получил название способа создания мягкой (soft) или символической (symbolic) связи (link). В то время как жесткая связь файлов является аналогом использования прямых ссылок (указателей) в современных языках программирования, символическая связь, до некоторой степени, напоминает косвенные ссылки (указатель на указатель). При создании мягкой связи с именем symlink из некоторой директории к файлу, заданному полным или относительным именем linkpath, в этой директории действительно создается новый файл типа "связь" с именем symlink со своими собственными индексным узлом и логическими блоками. При тщательном рассмотрении можно обнаружить, что все его содержимое составляет только символьная запись имени linkpath. Операция открытия файла типа "связь" устроена таким образом, что в действительности открывается не сам этот файл, а тот файл, чье имя содержится в нем (при необходимости рекурсивно!). Поэтому операции над файлами, требующие предварительного открытия файла (как, впрочем, и большинство команд операционной системы, совершающих действия над файлами, где операция открытия файла присутствует, но скрыта от пользователя), в реальности будут совершаться не над файлом типа "связь", а над тем файлом, имя которого содержится в нем (или над тем файлом, который, в конце концов, откроется при рекурсивных ссылках). Отсюда, в частности, следует, что попытки прочитать реальное содержимое файлов типа "связь" с помощью системного вызова read() обречены на неудачу. Как видно, создание мягкой связи, с точки зрения изменения логической структуры файловой системы, эквивалентно опосредованному проведению именованного ребра к уже существующему узлу через файл типа "связь" и неименованное ребро. Создание символической связи не приводит к проблеме, связанной с удалением файлов. Если файл, на который ссылается мягкая связь, удаляется с физического носителя, то попытка открытия файла мягкой связи (а, следовательно, и удаленного файла) приведет к ошибке "Файла с таким именем не существует", которая может быть аккуратно обработана приложением. Таким образом, удаление связанного объекта, как упоминалось ранее, лишь отчасти и не фатально нарушит целостность файловой системы. Неаккуратное применение символических связей пользователями операционной системы может привести к превращению логической структуры файловой системы из ациклического графа в циклический граф. Это, конечно, нежелательно, но не носит столь разрушительного характера, как циклы, которые могли бы быть созданы жесткой связью, если бы не был введен запрет на организацию жестких связей к директориям. Поскольку мягкие связи принципиально отличается от жестких связей и связей, возникающих между директорией и файлом при его создании, мягкая связь легко может быть идентифицирована операционной системой или программой пользователя. Для предотвращения зацикливания программ, выполняющих операции над файлами, обычно ограничивается глубина рекурсии по прохождению мягких связей. Превышение этой глубины приводит к возникновению ошибки "Слишком много мягких связей ", которая может быть легко обработана приложением. Поэтому ограничения на тип файлов, к которым может вести мягкая связь, в операционной системе LINUX не вводятся. Для создания мягких связей применяются уже знакомая нам команда операционной системы ln с опцией -s и системный вызов symlink(). Надо отметить, что системный вызов symlink() также не требует предварительного открытия связываемого файла, поскольку он вообще не рассматривает его содержимое.
Операция удаления связей и файлов. Системный вызов unlink(). При рассмотрении операции связывания файлов мы уже почти полностью рассмотрели, как производится операция удаления жестких связей и файлов. При удалении мягкой связи, т.е. фактически файла типа "связь", все происходит, как и для обычных файлов. Единственным изменением, с точки зрения логической структуры файловой системы, является то, что при действительном удалении узла, соответствующего файлу типа "связь", вместе с ним удаляется и выходящее из него неименованное ребро. Дополнительно необходимо отметить, что условием реального удаления регулярного файла с диска является не только равенство 0 значения его счетчика жестких связей, но и отсутствие процессов, которые держат этот файл открытым. Если такие процессы есть, то удаление регулярного файла будет выполнено при его полном закрытии последним использующим файл процессом. Для осуществления операции удаления жестких связей и/или файлов можно задействовать уже известную вам команду операционной системы rm или системный вызов unlink(). Заметим, что системный вызов unlink() также не требует предварительного открытия удаляемого файла, поскольку после его удаления совершать над ним операции бессмысленно.
Функция opendir() Прототип функции #include <sys/types.h>#include <dirent.h>DIR *opendir(char *name);Описание функции Функция opendir служит для открытия потока информации для директории, имя которой расположено по указателю name. Тип данных DIR представляет собой некоторую структуру данных, описывающую такой поток. Функция opendir подготавливает почву для функционирования других функций, выполняющих операции над директорией, и позиционирует поток на первой записи директории. Возвращаемое значение При удачном завершении функция возвращает указатель на открытый поток директории, который будет в дальнейшем передаваться в качестве параметра всем другим функциям, работающим с этой директорией. При неудачном завершении возвращается значение NULL. С точки зрения программиста в этом интерфейсе директория представляется как файл последовательного доступа, над которым можно совершать операции чтения очередной записи и позиционирования на начале файла. Перед выполнением этих операций директорию необходимо открыть, а после окончания – закрыть. Для открытия директории используется функция opendir(), которая подготавливает почву для совершения операций и позиционирует нас на начале файла. Чтение очередной записи из директории осуществляет функция readdir(), одновременно позиционируя нас на начале следующей записи (если она, конечно, существует). Для операции нового позиционирования на начале директории (если вдруг понадобится) применяется функция rewinddir(). После окончания работы с директорией ее необходимо закрыть с помощью функции closedir().
Напишите, откомпилируйте и прогоните программу, распечатывающую список файлов, входящих в директорию, с указанием их типов. Имя директории задается как параметр командной строки. Если оно отсутствует, то выбирается текущая директория. Задача повышенной сложности: напишите программу, распечатывающую содержимое заданной директории в формате, аналогичном формату выдачи команды ls -al. Для этого вам дополнительно понадобится самостоятельно изучить в LINUX Manual функцию ctime(3) и системные вызовы time(2), readlink(2). Цифры после имен функций и системных вызовов – это номера соответствующих разделов для LINUX Manual. Лабораторная работа № 5. Организация файловой системы в LINUX. Работа с файлами и директориями. Понятие о memory mapped файлах ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ
|
|||||||||||||||||
Последнее изменение этой страницы: 2021-11-27; просмотров: 43; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.14.142.115 (0.033 с.) |