
- •Глава 6
- •Мал. 6.3. Сегментований процес до відображення файлів на адресний простір (а); процес після відображення існуючого файлу abc на один сегмент і створення нового сегменту для файлу xyz (б)
- •Мал. 6.5. Дворівнева каталогова система
- •Мал. 6.11. Таблиця розміщення файлів
- •Мал. 6.13. Простий каталог, що містить записи фіксованої довжини з атрибутами і дисковими адресами (а); каталог, в якому кожен запис являється просто посиланням на I-вузол (б)
- •Мал. 6.15. Файлова система, що містить сумісно використовуваний файл.
- •Мал. 6.16. Ситуація до зв’язку (а); після створення зв’язку (б); власник видалив свій файл (в)
- •Мал. 6.19. Майже повний блок покажчиків вільних блоків в пам'яті і три блоки покажчиків на диску (а); результат видалення трьохблокового файлу (б); альтернативна стратегія (в)
- •Мал. 6.21. Файлова система, що архівується
- •Мал. 6.23. Стани файлової системи : несуперечливе (а); зниклий блок (б); дублікат блоку в списку вільних блоків (в); дублікат блоку даних (г)
- •Мал. 6.24.Структура даних блочного кеша
- •Мал. 6.25.1-вузли, розміщені на початку диска (а); диск, розділений на групи циліндрів, кожна зі своїми власними блоками і і-вузлами (б)
- •Мал.6.29. Формат каталогового запису в системі ms – dos/
- •Мал. 6. 32. Приклад збереження довгого імені файлу в системі Windows 98.
- •Мал. 6.34. І-вузол файлової системи unix v7.
- •Мал. 6. 35. Етапи пошуку файлу /usr/ast/mbox
Глава 6
Файлові системи
Всім комп'ютерним програмам потрібно зберігати і отримувати інформацію. Під час роботи процес може зберігати обмежену кількість даних у власному адресному просторі. Однак ємність такого сховища обмежена розмірами віртуального адресного простору. Для деяких програм такого розміру цілком достатньо, але для інших, наприклад систем резервування авіаквитків, систем банківського або корпоративного обліку, одного тільки віртуального адресного простору буде недостатньо.
Крім того, після завершення роботи процесу інформація, що зберігається в його адресному просторі, втрачається. Для більшості додатків (наприклад, баз даних) ця інформація повинна зберігатися тижнями, місяцями або навіть вічно. Зникнення даних після завершення роботи процесу для таких додатків неприйнятно. Інформація повинна зберігатися навіть при аварійному завершенні процесу у разі збою комп'ютера.
Третя проблема полягає в тому, що часто виникає необхідність кільком процесам одночасно отримати доступ до одних і тих же даних (або частини даних). Якщо інтерактивний телефонний довідник буде зберігатися в адресному просторі одного процесу, то доступ до нього буде тільки у цього процесу. Для вирішення цієї проблеми необхідно відокремити інформацію від процесу.
Таким чином, до довготривалих пристроїв зберігання інформації пред'являють три наступні важливі вимоги:
1. Пристрої повинні дозволяти зберігати дуже великі обсяги даних.
2. Інформація повинна зберігатися після припинення роботи процесу, що використовує її.
3. Кілька процесів повинні мати можливість отримання одночасного доступу до інформації.
Звичайне вирішення всіх цих проблем полягає у зберіганні інформації на дисках і інших зовнішніх хранителях в модулях, званих файлами. Процеси можуть у міру потреби читати їх і створювати нові файли. Інформація, що зберігається у файлах, повинна володіти стійкістю (в даному контексті іноді застосовується термін персистентність), тобто на неї не повинні впливати створення або припинення роботи якого-небудь процесу. Файл повинен зникати тільки тоді, коли його власник дає команду видалення файлу.
Файлами керує операційна система. Їх структура, іменування, використання, захист, реалізація і доступ до них є важливими пунктами влаштування операційної системи. Частина операційної системи, що працює з файлами, називається файловою системою. Їй і присвячена дана глава.
З точки зору користувача найбільш важливим аспектом файлової системи є її зовнішнє уявлення, тобто іменування та захист файлів, операції з файлами і т. д. Такі деталі внутрішнього устрою, як використання пов'язаних списків або біт-карт для стеження за вільними й зайнятими блоками диска, число фізичних секторів в логічному блоці, представляють для користувача менший інтерес, хоча і вкрай важливі для розробників файлової системи. З цієї причини ми розбили главу на кілька розділів. Перші два розділи присвячені користувацькому інтерфейсу файлів і каталогів. В наступних розділах ми розглянемо способи реалізації файлової системи. Наостанок, будуть приведені кілька прикладів існуючих файлових систем.
Файли
У наступних декількох розділах ми розглянемо файли з точки зору користувача, тобто обговоримо їх використання та їх властивості.
Іменування файлів
Файли відносяться до абстрактного механізму. Вони надають спосіб зберігати інформацію на диску і прочитувати її знову пізніше. При цьому від користувача повинні ховатися такі деталі, як спосіб і місце зберігання інформації, а також деталі роботи дисків.
Ймовірно, найбільш важливою характеристикою будь-якого механізму абстракції є те, як називаються керовані об'єкти, тому ми почнемо вивчення файлової системи з іменування файлів. При створенні файлу процес дає файлу ім'я. Коли процес завершує роботу, файл продовжує своє існування і за його іменем до нього можуть отримати доступ інші процеси.
Точні правила іменування файлів варіюються від системи до системи, але всі сучасні операційні системи підтримують використання в якості імен файлів 8-символьні текстові рядки. Таким чином, andrea, bruce і cathy є допустимими іменами файлів. Часто в іменах файлів також дозволяється використання цифр і спеціальних символів, тому можуть застосовуватися і такі імена файлів, як 2, urgent! і Fig.2-14. Багато файлових систем підтримують імена файлів довжиною до 255 символів.
У деяких файлових системах, наприклад UNIX, розрізняються прописні і рядкові символи, тоді як в інших, таких як MS-DOS, ні. Таким чином, імена файлів maria, Maria і MARIA будуть означати в системі UNIX три різних файла, тоді як в MS-DOS всі ці імена будуть відповідати одному файлу.
Операційні системи Windows 95 і Windows 98 використовують файлову систему MS-DOS і успадковують багато її властивостей, включаючи іменування файлів. Операційні системи Windows NT і Windows 2000 також підтримують файлову систему MS-DOS і успадковують її властивості. Однак, в останніх двох операційних системах є своя файлова система (NTFS), що володіє відмінними властивостями (наприклад, іменами файлів в кодуванні Unicode). У цьому розділі при згадці файлової системи Windows ми будемо мати на увазі файлову систему MS-DOS, яка є єдиною файловою системою, підтримуваної всіма версіями Windows. Файлова система NTFS, використовувана в Windows 2000, буде обговорюватися в розділі 11.
У багатьох операційних системах ім'я файлу може складатися з двох частин, розділених крапкою, наприклад prog.c. Частина імені файлу після крапки називається розширенням файлу і зазвичай означає тип файлу. Так, в MS-DOS ім'я файлу може містити від 1 до 8 символів плюс розширення від 0 до 3 символів. У системі UNIX розмір розширення файлу залежить від користувача. Крім того, у файлу може бути кілька розширень, наприклад progx.Z, де Z зазвичай використовується, щоб вказати, що файл (prog.c) був стиснутий за допомогою алгоритму Зіва-Лемпеля. Деякі розширення файлів часто зустрічаються і їх значення наведені в табл.6.1.
Розширення |
Значення |
file.bak |
Резервна копія файлу |
file.c |
Початковий текст на С |
file.gif |
Зображення формату GIF |
file.hip |
Файл довідки |
file.html |
Документ у форматі HTML (web-сторінка) |
file.jpg |
Нерухоме зображення стандарту JPEG |
file.mp3 |
Музика у форматі MPEG-1 рівень 3 |
file.mpg |
Відео у форматі MPEG |
file.o |
Об’єктний файл (ще не скомпонований вихідний файл компілятора) |
file.pdf |
Документ формату PDF (програми Adobe Acrobat) |
file.ps |
Документ формату PostScript |
file.tex |
Вхідний файл для програми форматування ТЕХ |
file.txt |
Текстовий файл загального призначення |
file.zip |
Архів, зжатий за допомогою алгоритму Зіва~Лемпеля |
У деяких системах (наприклад, в UNIX) розширення файлів є просто угодами, і операційна система не примушує користувача їх суворо дотримуватися. Файл file.txt може бути текстовим файлом, але це скоріше нагадування користувачеві, а не керівництво до дії для операційної системи. З іншого боку, компілятор мови С може відмовитися компілювати файли з розширеннями, відмінними від С.
Подібні угоди особливо корисні, коли одна і та ж програма повинна керувати різними типами файлів. Наприклад, компілятору мови С може бути наданий список файлів, які він повинен відкомпілювати і скомпонувати, причому деякі з цих файлів можуть містити програми на мові С, тоді як інші бути ассемблерними файлами. У цьому випадку саме по розширенню файлів компілятор зможе відрізнити одні файли від інших.
Система Windows, навпаки, знає про розширення файлів і призначає кожному розширенню певне значення. Користувачі (або процеси) можуть реєструвати розширення в операційній системі, вказуючи програму, «що володіє» даними розширенням. При подвійному клацанні миші на імені файлу запускається програма, призначена цьому розширенню, з ім'ям файлу як параметра. Наприклад, подвійне клацання миші на імені file.doc запускає Microsoft Word, який відкриває файл file.doc.
Структура файлу
Файли можуть бути структуровані кількома різними способами. Три типи структур показані на мал. 5.1. Файл на мал. 5.1, а являє собою Неструктуровану послідовність байтів. У цьому випадку операційна система не цікавиться вмістом файлу. Все, що вона бачить - це байти. Значення цим байтам надається програмами рівня користувача. Такий підхід використовується як в системі UNIX, так і в Windows.
1 Байт 1 Запис
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|


















а б в
Мал. 6.1. Три типи файлів: послідовність байтів (а); послідовність записів (б); дерево (в)
Розгляд операційною системою файлів, як просто послідовності байтів забезпечує максимальну гнучкість. Програми користувача можуть розміщувати у файли все що завгодно і називати їх будь-яким зручним для них способом. Операційна система не втручається в цей процес, що може бути особливо цінним для користувачів, які збираються зробити що-небудь незвичайне.
Перший крок у напрямку до структури зображеної на мал. 6.1, б. У даній моделі файл являє собою послідовність записів фіксованої довжини, кожна зі своєю внутрішньою структурою. Для файлів, що складаються із записів, важливим є те, що операція читання повертає один запис, а операція запису перезаписує або доповнює один запис. Кілька десятиліть тому, коли на повну силу застосовувалися перфокарти, що складаються з 80 колонок отворів, багато операційних систем (на мейнфреймах) оперували файлами, що складаються з 80-символьних записів, що представляють собою образи перфокарт. Цими операційними системами підтримувалися також файли, що складаються з 132-символьних записів, що призначаються для строкових принтерів (які в ті дні друкували по 132 символи в рядку). Програми читали з вхідних файлів 80-символьні блоки і записували їх у вигляді 132-символьних блоків, хоча 52 символи, що залишилися, можуть бути пропусками. Жодна сучасна універсальна система не працює так само.
Третій варіант файлової структури свідчень на мал. 6.1, в. При такий організації файл є деревом записів, не обов'язково однієї і тієї ж довжини. Кожен запис у фіксованій позиції містить поле ключа. Дерево сортоване по ключовому полю, що забезпечує швидкий пошук заданого ключа.
Основною файловою операцією тут є не отримання наступного запису, хоча це також можливо, а отримання запису з вказаним значенням ключа. Для файлу зоопарку, показаного на мал. 6.1, б, можна, наприклад, запросити у системи запис з ключем поні, не турбуючись про точне положення цього запису у файлі. При додаванні нових записів операційна система, а не користувач повинна вирішувати, куди її помістити. Такий тип файлів принципово відрізняється від неструктурованих потоків байтів, вживаних в UNIX і Windows, але вони широко застосовуються на великих мейнфреймах, ще використовуваних для комерційної обробки даних.
Типи файлів
Багато операційних систем підтримують різні типи файлів. Наприклад, в системах UNIX і Windows проводитися відмінність між регулярними (звичайними) файлами і каталогами. У системі UNIX також розрізняються символьні і блокові спеціальні файли. До регулярних файлів відносяться усі файли, що містять інформацію користувача. Усі файли на мал. 6.1 являються регулярними. Каталоги - це системні файли, що забезпечують підтримку структури файлової системи. Ми розглянемо їх детальніше нижче. Символьні спеціальні файли мають відношення до введення-виведення і використовуються для моделювання послідовних пристроїв введення-виведення, таких як термінали, принтери і мережі. Блокові спеціальні файли використовуються для моделювання дисків. У цій главі ми в першу чергу розглядатимемо регулярні файли.
Регулярні файли в основному являються або ASCII-файлами, або двійковими файлами. ASCII-файли складаються з текстових рядків. У деяких системах кожний рядок завершується символом повернення каретки. У інших (наприклад, UNIX) використовується символ перекладу рядка. У деяких системах (наприклад, MS - DOS) використовуються обидва символи. Рядки не зобов'язані мати однієї і тієї ж довжини.
Великою перевагою ASCII-файлів є те, що вони можуть відображатися на екрані і виводитися на друк так, як є, без якого-небудь перетворення, і можуть редагуватися практично будь-яким текстовим редактором. Більше того, якщо велика кількість програм використовує ASCII-файли для входу і виходу, то виявляється нескладним з'єднати вхід однієї програми виходом іншої, як це робиться в конвеєрах оболонки. (Обмін даними між процесами при цьому не стає простішим, але інтерпретація інформації полегшується, якщо для її вираження застосовується стандарт, такий як ASCII.)
Інші файли називаються двійковими, тобто вони не являються ASCII-файлами. При виведенні їх на принтер виходить незрозумілий набір символів, що нагадує випадкове сміття. Зазвичай у них є деяка внутрішня структура, відома програмі, що використовує їх.
Наприклад, на малий. 6.2, а свідчень простий виконуваний двійковий файл однієї з версій системи UNIX. Хоча технічно файл є усього лише послідовністю байтів, операційна система стані виконувати файл тільки у тому випадку, якщо цей файл має відповідний формат. Файл складається з п'яти розділів : заголовка, тексту, даних, релокаційних бітів і таблиці символів. Заголовок починається з так званого "магічного" числа, ідентифікуючого файл як виконуваний (щоб запобігти випадковому виконанню файлу іншого формату). Слідом за "магічним" числом в заголовку розташовуються розміри різних частин файлу, адресу початку виконання файлу і деякі біти прапорів. За заголовком йдуть текст програми і дані. Вони загружаються в оперативну пам'ять і налаштовуються на роботу за адресою завантаження за допомогою бітів релокації. Таблиця символів використовується для відладки.
Ім’я модуля |
Дата |
Власник |
Захист |
Розмір |

М |
Р |
Розмір даних |
Розмір релокаційного блоку |
Розмір таблиці символів |
Т |
|
П рапори |
Текст |
Дані |
Біти релокації |
Таблиця символів |
Заголовки |
О |
Заголовки |
Об’єктний модуль |
Заголовки |
Об’єктний модуль |
Мал. 6.2. Виконуваний файл (а), архів (б).
Другий приклад двійкового файлу є файлом архіву, також з системи UNIX. Він складається з набору бібліотечних процедур (модулів), відкомпільованих, але не зкомпонованих. Кожній процедурі передує заголовок, що містить її ім'я, дату створення, власника, код захисту і розмір. Як і у випадку виконуваного файлу, заголовки модулів містять велику кількість двійкових чисел. Якщо скопіювати їх на принтер, вийде повна тарабарщина.
Усі операційні системи повинні розпізнавати принаймні один тип файлів - свої власні виконувані файли, але деякі операційні системи розпізнають і інші типи файлів. Стара система TOPS - 20 (для комп'ютера DECsystem 20) навіть вивчала година створення кожного файлу, що надається їй на виконання. Потім вона знаходила початковий файл і перевіряла, чи не був він змінений, після того, як був створений виконуваний файл. Якщо виявлялося, що виконуваний файл вже застарів, операційна система автоматично перекомпільовувала початковий файл. Якщо перекласти це на мову, зрозумілу UNIX, то це означало, що програма make була вбудована в оболонку. Розширення файлів були обов'язковими, щоб операційна система могла визначити, яка двійкова програма від якого початкового файлу пішла.
Проте така жорстка заданість типів файлів може виявитися незручною для користувача, що намагається зробити що-небудь, не передбачене проектувальниками операційної системи. Уявіть, наприклад, систему, в якій файли програмного виводу автоматичний отримують розширення .dat (файли даних). Нехай користувач написавши програму, що форматує початкові тексти програм на С. Ця програма читає файл з розширенням .с, обробляє його і потім пише результат у файл із стандартним розширенням .dat. Якщо користувач потім спробує запропонувати цей файл С-компілятору, операційна система не дасть цього зробити, оскільки у файлу для цієї дії невірне розширення. Спроба скопіювати file.dat в file.c також буде знехтувана операційною системою.
Хоча така "дружність" по відношенню до користувача (що захищає користувача від помилок) може бути корисна для новачків, вона ставить досвідчених користувачів в безвихідь, примушуючи їх витрачати масу зусиль на спроби перехитрити операційну систему.
Доступ до файлів
У старих операційних системах надавався тільки один тип доступу до файлів - послідовний доступ. У цих системах процес міг читати байти або записи файлу тільки по порядку від початку до кінця. Такий доступ до файлів з’явився, коли дисків ще не було і комп'ютери оснащувалися магнітофонами. Тому навіть в дискових операційних системах при послідовному доступі до файлу імітувалося його читання або запис на накопичувачі на магнітній стрічці з можливістю багатократного перемотування назад.
З появою дисків стало можливим читати байти або записи файлу в довільному порядку або діставати доступ до записів по ключу. Файли, байти яких можуть бути прочитані в довільному порядку, називаються файлами довільного доступу. Такі файли використовуються багатьма застосуваннями.
Файли довільного доступу дуже важливі для багатьох застосувань, наприклад для баз даних. Якщо клієнт дзвонить в авіакомпанію з метою зарезервувати місце на конкретний рейс, програма резервування авіаквитків повинна мати можливість дістати доступ до потрібного запису, не читаючи усі тисячі попередніх записів, що містять інформацію про інші рейси.
Для вказівки місця початку читання використовуються два методи. У першому випадку кожна операція read задає позицію у файлі. При другому способі використовується спеціальна операція пошуку seek, що встановлює поточну позицію. Після виконання операції seek файл може читатися послідовно з поточною позицією.
У деяких старих операційних системах, що використалися на мейнфреймах, спосіб доступу до файлу (послідовний або довільний) вказувався у момент створення файлу. Це дозволяло операційній системі застосовувати різні методи для зберігання файлів різних класів. У сучасних операційних системах такої відмінності не проводиться. Усі файли автоматично являються файлами довільного доступу.
Атрибути файлу
У кожного файлу є ім'я і дані. Окрім цього усі операційні системи зв’язують з кожним файлом також і іншу інформацію, наприклад дату і час створення файлу, а також його розмір. Ми називатимемо ці додаткові відомості атрибутами файлу. Список атрибутів значно варіюється від системи до системи. У таблиці. 6.2 показані деякі можливі атрибути, проте існують також і інші можливості. Ні у одній існуючій операційній системі не є присутній відразу усі приведені в таблиці атрибути файлів, але кожен з них використовується в тій або іншій системі.
Таблиця 6.2. Деякі можливі атрибути файлів
Атрибут |
Значення |
Захист Пароль Творець Власник Прапорець "тільки читання" Прапорець "прихований" Прапорець "системний" Прапорець "архівний" Прапорець ASCII/двійковий Прапорець вільного доступу Прапорець "тимчасовий" Прапорці блокування Позиція ключа Довжина ключа Час створення Час останнього доступу Час останньої зміни Поточний розмір Максимальний розмір |
Хто і яким чином може отримати доступ до файлу Пароль для отримання доступу до файлу Ідентифікатор користувача, що створив файл Поточний власник 0 - для читання/запису; 1 - тільки для читання 0 - нормальний; 1 - не показувати в переліку файлів каталоги 0 - нормальний; 1 – системний 0 - заархівований; 1 - потрібна архівація 0 - ASCII; 1 – двійковий 0 - тільки послідовний доступ; 1 - вільний доступ 0 - нормальний; 1 - для видалення файлу після закінчення роботи процесу 0 – назаблокований; відмінний від нуля для блокованого Зміщення до ключа в записі Кількість байтів в полі ключа Дата і час створення файлу Дата і час останнього доступу файлу Дата і час останньої зміни файлу Кількість байтів у файлі Кількість байтів, до якої можна збільшувати розмір файлу |
Перші чотири атрибути відносяться до захисту файлу і містять інформацію про той, хто може дістати доступ до файлу, а хто ні. Можливі різні схеми реалізації захисту файлу, декілька з них ми розглянемо нижче. У деяких системах користувач повинен для діставання доступу до файлу вказати пароль. В цьому випадку пароль повинен входити в атрибути файлу.
Прапорці є бітами або короткими полями, що керують деякими специфічними властивостями. Наприклад, приховані файли не з'являються в переліку файлів при друкові каталогу. Прапорець архівації є бітом, що стежить за тим, чи була створена для файлу резервна копія. Цей прапорець очищується програмою архівації і встановлюється операційною системою при зміні файлу. Таким чином програма архівації може визначити, які файли слід архівувати. Прапорець "тимчасовий" дозволяє автоматично видаляти помічений файл після закінчення роботи процесу, що створив його.
Атрибути довжина запису, позиція ключа і довжина ключа є присутній тільки у тих файлів, записи яких можуть шукатися по ключу. Ці атрибути дають необхідну для пошуку ключа інформацію.
Різні атрибути, що зберігають значення часу, дозволяють стежити за тим, коли файл був створений, востаннє змінений і коли до нього востаннє надавався доступ. Ці відомості можна використовувати в різних цілях. Наприклад, якщо початковий файл програми був модифікований після створення відповідного йому об'єктного файлу, то початковий файл має бути перекомпільованим.
Поточний розмір файлу містить кількість байтів у файлі в даний момент. У деяких старих операційних системах, що використалися на мейнфреймах, при створенні файлу вимагалося вказати також максимальну довжину файлу, що дозволяло операційній системі зарезервувати досить місця для наступного збільшення файлу. Сучасні операційні системи, що працюють на персональних комп'ютерах і робочих станціях, уміють обходитися без подібного резервування.
Операції з файлами
Файли дозволяють зберігати інформацію і отримувати її пізніше. У різних операційних системах є різні набори файлових операцій. Нижче ми перерахуємо виклики, які часто зустрічаються, і що відносяться до файлів.
Create (створення). Файл створюється без даних. Цей системний виклик оголошує про появу нового файлу і дозволяє встановити деякі його атрибути.
Delete (видалення). Коли файл вже не потрібніший, його видаляють, щоб звільнити простір на диску. Цей системний виклик є присутній в кожній операційній системі.
Open (відкриття). Перш ніж використовувати файл, процес повинен його відкрити. Системний виклик open дозволяє системі прочитати в оперативну пам'ять атрибути файлу і список дискових адрес для швидкого доступу до вмісту файлу при наступних викликах.
Close (закриття). Коли усі операції з файлом закінчені, атрибути і дискові адреси не потрібні, тому файл слід закрити, щоб звільнити простір у внутрішній таблиці. Багато операційних систем дозволяють одночасно відкрити обмежену кількість файлів. Запис на диск проводиться поблочно, а закриття файлу викликає запис останнього блоку файлу, навіть якщо цей блок ще не заповнений до кінця.
Read (читання). Читання даних з файлу. Зазвичай байти поступають з поточної позиції у файлі. Викликаний процес повинен вказати кількість необхідних даних і надати для них буфер.
Write (запис). Запис даних у файл, також в поточну позицію у файлі. Якщо поточна позиція знаходиться у кінці файлу, розмір файлу автоматично збільшується. Інакше запис проводиться поверх існуючих даних, які втрачаються назавжди.
Append (додавання). Цей системний виклик є вкороченою формою виклику write. Він може тільки додавати дані до кінця файлу. У операційних системах з мінімальним набором системних викликів може не бути цього системного виклику.
Seek (пошук). Для файлів довільного доступу потрібно вказати спосіб, де розташовуються дані у файлі. Цей системний виклик встановлює файловий покажчик в певну позицію у файлі. Посля виконання цього системного виклику дані можуть читатися або записуватися в цій позиції.
Get attributes (отримання атрибутів). Процесам часто для виконання їх роботи буває необхідно отримати атрибути файлу. Наприклад, для збірки програм, що складаються з великого числа окремих початкових файлів, в системі UNIX часто використовується програма make. Ця програма досліджує час зміни усіх початкових і об'єктних файлів, завдяки чому обходиться компіляцією мінімальної кількості файлів. Для виконання цієї роботи їй вимагається отримати атрибути файлів.
Set attributes (установка атрибутів). Деякі атрибути файлу можуть встановлюватись користувачем після створення файлу. Цей системний виклик надає таку можливість. Наприклад, для файлу може бути встановлений код захисту доступу. Більшість інших прапорів також можуть встановлюватись за допомогою цього системного виклику.
Rename (перейменування). Цей системний виклик дозволяє змінити ім'я файлу. Його присутність в операційній системі не є необхідним, оскільки зазвичай файл можна скопіювати з новим ім'ям, а старий файл видалити.
Приклад програми, що використовує файлові системні виклики
У цьому розділі ми розглянемо просту програму для операційної системи UNIX, що копіює один файл в іншій. Вона приведена в лістингу 6.1. Ця про-грамма має мінімальну функціональність і ще гіршу здатність повідомляти про помилки, але проте вона дає достатнє уявлення про роботу деяких файлових системних викликів.
Лістинг 6.1. Проста програма копіювання файлу
/* Програма копіювання файлів. Контроль і повідомлення про помилки мінімальні. */
#include <sys/types.h> /* ввімкнути необхідні файли заголовків */
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main(int args. char * argv[]); /* ANSI прототип */
#define BUF SIZE 4096 /* використовувати буфер розміром 4096 байт */
#define OUTPUT MODE 0700 /* біти захисту для вихідного файлу */
int main(int args. char * argv[]);
{
int in fd. out fd. rd count. wt_count;
char buffer[BUF SIZE];
if (argc != 3) exit(1); /* якщо argv не рівний 3, то синтаксична помилка */ /* Відкрити вхідний файл і створити вихідний файл */
in_fd = open(argv[1]. O_RDONLY); /* відкрити вхідний файл */
if (in_fd < 0) exit(2); /* якщо файл не може бути відкритий, вихід */
out_fd = creat(argv[2]. OUTPUT_MODE); /* створити вихідний файл */
if (in_fd < 0) exit(3); /* якщо файл не можу бути створений, вихід */
/* Цикл копіювання */
while (TRUE) {
rd_count = read(in_fd. buffer. BUF_SIZE); /* прочитати блок даних */
if (rd_count <= 0) break; /* якщо кінець файла або помилка, вийти з циклу */
wt_count = write(out_fd. buffer. Rd_count); /* записати дані */
if (wt_count <= 0) exit (4); /* wt_count <= 0 є помилкою */
}
/* Закрити обидва файла */
close(in_fd);
close(out_fd);
if (rd_count ==0) /* при останньому читанні помилки не було */
exit(0);
else
exit(5); /* помилка при останньому читанні */
}
Програма copy file може викликатися, наприклад, за допомогою командного рядка
copyfile abc xyz
щоб скопіювати файл аbс у файл xyz. Якщо файл xyz вже існує, він буде перезаписаний. Інакше він буде створений. Програма повинна викликатися обов'язково з двома аргументами, кожен з яких є допустиме ім'я файлу.
Чотири оператори #include на початку програми забезпечують включення в программу великої кількості визначень і прототипів функцій. Це потрібно для того, щоб програма відповідала міжнародним стандартам, але не цікавитиме нас надалі. Наступний рядок містить прототип функції main, що вимагається стандартом ANSI З, але також не цікавить нас зараз.
Перший оператор #define є макровизначенням, що визначає рядок BUF_SIZE як макрос, що замінюється в тексті при компіляції числом 4096. Програма читатиме і писатиме дані шматками по 4096 байт. Створювати подібні константи і використовувати їх замість вказівки безпосередньо чисел в програмі вважається хорошим стилем програмування. При цьому програма не лише стає легшою для читання, але її також простіше і змінювати у разі необхідності. Другий оператор #define визначає коло користувачів, здатних отримати доступ до вихідного файлу.
Основна програма називається main. У неї є два аргументи, argc і argv. Цим аргументам привласнюється значення операційною системою при виклику програми. Перший аргумент повідомляє кількість параметрів (слів) в командному рядку, включаючи ім'я програми. Він має бути рівний трьом. Другий аргумент є масивом покажчиків на текстові рядки, що містять параметри командного рядка. У цьому прикладі елементи цього масиву міститимуть покажчики на наступні рядки:
argv[0] = "copyfile"
argv[l] = "abc"
argv[2] = "xyz"
Саме з цього масиву програма може отримати вхідні параметри.
У програмі оголошуються п'ять змінних. Перші дві змінні, in_fd і out_fd, призначаються для зберігання дескрипторів файлів, що є невеликими цілими числами, що повертаються процедурами відкриття або створення файлу. Наступні дві змінні, rd_count і wt_сount, являються байтовими лічильниками, що повертаються, відповідно, процедурами read і write. Остання змінна, buffer, є символьний масив, використовуваний як буфер для читання і запису даних.
Перший виконуваний оператор перевіряє рівність лічильника аргументів argc трьом. Якщо лічильник argc не рівний трьом, програма завершується з кодом 1. Будь-який код завершення програми, відмінний від 0, означає помилку. Код завершення програми є єдиним способом повідомлення про помилки, вживаним в цій програмі.
Потім програма намагається відкрити вхідний файл і створити вихідний файл. Якщо відкриття файлу проходить успішно, операційна система привласнює змінній in_fd позитивне значення, відповідне дескриптору відкритого файлу. При наступних операціях з файлом це число повинне вказувати операційній системі, до якого файлу відноситься цей системний виклик. Відповідно, якщо вдається успішно створити вихідний файл, змінній out_fd привласнюється значення дескриптора файлу, що використовується для його ідентифікації. Другий аргумент процедури creat встановлює код захисту створюваного файлу. Якщо одна з цих операцій завершується невдачею, то замість дескриптора файлу повертається значення і програма виходить з відповідним кодом помилки.
Потім починається цикл копіювання із спроби читання в буфер buffer А Кбайт даних. Читання виконується за допомогою бібліотечної процедури read, що звертається до системного виклику read. Перший параметр ідентифікує файл, другий вказує на буфер, а третій повідомляє, скільки байтів слід прочитати. Змінній rd_count привласнюється значення, рівне кількості прочитаних байтів. У нормальній ситуації це буде 4096 або менше число, рівне величині частини файлу, що залишилася. Досягши кінця файлу змінної rd_count буде присвоєно число 0. Коли значення змінної rd_сount стане від’ємним або нульовим, це означатиме, що операцію копіювання продовжувати більше неможливо і цикл читання і запису буде перерваний оператором break.
Звернення до бібліотечної процедури write записує вміст буфера у вихідний файл. Перший параметр вказує файл, другий - буфер, а третій містить кількість байтів, які необхідно записати, подібно бібліотечній процедурі read. Зверніть увагу, що записується саме стільки байтів, скільки було прочитано, а не увесь буфер BUFJSIZE. Цей момент важливий, оскільки остання операція читання прочитає не 4096 байт, якщо тільки довжина файлу не кратна 4 Кбайт.
Коли увесь файл буде прочитаний, перше звернення до бібліотечної процедури read поверне значення 0, яке буде присвоєно змінній rd_count, і цикл обірветься. Після цього обидва файли закриваються, і програма припиняє свою роботу з нульовим статусом, відповідним нормальному завершенню.
Хоча системні виклики Windows відрізняються від системних викликів UNIX, загальна структура програми копіювання файлів, що працює в режимі командно стрічки в Windows, схожа з програмою, приведеною в лістингу 6.1. Ми оговоримо системні виклики Windows 2000 в главі І.
Файли, що відображуються на адресний простір пам'яті
Багато програмістів вважають описаний в попередньому прикладі доступ незручним, особливо в порівнянні з доступом до звичайної пам'яті. З цієї причини в деяких операційних системах, починаючи з системи MULTICS, був наданий спосіб відображення файлів на адресний простір працюючого процесу. Концептуально можна уявити собі два нових системного виклику, тар і unmap. Перший системний виклик приймає на вході два параметри: ім'я файлу і віртуальна адреса пам'яті, по якій операційна система відображує вказаний файл.
Припустимо, наприклад, що деякий файл розміром 64 Кбайт відображується на віртуальну пам'ять починаючи з адреси 512 К. Після цього будь-яка команда процесора, що читає байт за адресою 512 К, отримає байт 0 цього файлу і т. д. Запис за адресою 512 К+21000 змінить байт 21000 файлу. Коли процес завершує свою роботу, модифікований файл залишається на диску, начебто він був змінений системними викликами seek і write.
Для реалізації відображення файлів на пам'ять змінюються системні внутрішні таблиці. При зверненні до пам'яті за адресою від 512 до 576 К відбувається переривання через відсутність сторінки, обробник якого надає зчитану в пам'ять сторінку 0 файлу. При записі відбувається приблизно теж саме, але сторінка пам'яті, на яку відображується сторінка файлу, позначається як модифікована. Якщо потім ця сторінка видаляється з пам'яті алгоритмом заміни сторінок, вона записується у відповідне місце файлу. Після завершення процесу усі модифіковані сторінки зберігаються в відповідних файлах.
Відображення файлів на пам'ять краще всього працює в операційній системі, що підтримує сегментацію. У такій системі кожен файл може бути відображений на свій власний сегмент, так щоб байт k файлу був також байтом k сегменту. На мал. 6.3, а показаний процес з двома сегментами, виконуваним кодом програми і даними. Припустимо, що процес копіює файли подібно програми з лістингу 6.1. Спочатку він відображує на сегмент початковий файл, наприклад abc. Потім він створює порожній сегмент і відображує його на вихідний файл, xyz. В результаті виходить ситуація, показана на мал. 6.3, б.
Код
програми, що виконується
Виконуваний
код програми
abc
Дані
Дані
___xyz___