Кооперація процесів при роботі з файлами

Коли різні користувачі працюють разом над проектом, вони часто потребують розділення файлів.

Файл, що розділяється, - ресурс, що розділяється. Як і у разі будь-якого спільно використовуваного ресурсу, процеси повинні синхронізувати доступ до спільно використовуваних файлів, каталогів, щоб уникнути тупикових ситуацій, дискримінації окремих процесів і зниження продуктивності системи.

Наприклад, якщо декілька користувачів одночасно редагують який-небудь файл і не прийнято спеціальних мерів, то результат буде непередбачуваний і залежить від того, в якому порядку здійснювалися записи у файл. Між двома операціями read одного процесу інший процес може модифікувати дані, що для багатьох застосувань неприйнятно. Просте вирішення даної проблеми - надати можливість одному з процесів захопити файл, тобто блокувати доступ до файлу інших процесів, що розділяється, на весь час, поки файл залишається відкритим для даного процесу. Проте це було б недостатньо гнучко і не відповідало б характеру поставленого завдання.

Розглянемо спочатку грубий підхід, тобто тимчасове захоплення призначеним для користувача процесом файлу або запису (частини файлу між вказаними позиціями).

Системний виклик, що дозволяє встановити і перевірити блокування на файл, є невід'ємним атрибутом сучасних многопользовательских ОС. В принципі, було б логічне зв'язати синхронізацію доступу до файлу як до єдиного цілого з системним викликом open (т. е., наприклад, відкриття файлу в режимі запису або оновлення могло б означати його монопольне блокування відповідним процесом, а відкриття в режимі читання - сумісне блокування). Так поступають в багатьох операційних системах (починаючи з ОС Multics). У ОС Unix це не так, що має історичні причини.

У першій версії системи Unix, розробленої Томпсоном і Річи, механізм захоплення файлу був відсутній. Застосовувався дуже простий підхід до забезпечення паралельного (від декількох процесів) доступу до файлів: система дозволяла будь-якому числу процесів одночасно відкривати один і той же файл в будь-якому режимі (читання, записи або оновлення) і не робила ніяких дій синхронізацій. Вся відповідальність за коректність сумісної обробки файлу лягала на процеси, що використовують його, і система навіть не надавала яких-небудь особливих засобів для синхронізації доступу процесів до файлу. Проте згодом для того, щоб підвищити привабливість системи для комерційних користувачів, що працюють з базами даних, у версію V системи було включено механізми захоплення файлу і записи, що базуються на системному виклику fcntl.

Допускається два варіанти синхронізації: з очікуванням, коли вимога блокування може привести до відкладання процесу до того моменту, коли ця вимога може бути задоволене, і без очікування, коли процес негайно оповіщається про задоволення вимоги блокування або про неможливість її задоволення в даний момент.

Встановлені блокування відносяться тільки до того процесу, який їх встановив, і не успадковуються процесами-нащадками цього процесу. Більш того, навіть якщо деякий процес користується можливостями синхронізацій системного виклику fcntl, інші процеси як і раніше можуть працювати з тим файлом без всякої синхронізації. Іншими словами, це справа групи процесів, що спільно використовують файл, - домовитися про спосіб синхронізації паралельного доступу.

Тонший підхід полягає в прозорому для користувача блокуванні окремих структур ядра, що відповідають за роботу з файлами частини призначених для користувача даних. Наприклад, в ОС Unix під час системного виклику, що здійснює ту або іншу операцію з файлом, як правило, відбувається блокування індексного вузла, що містить адреси блоків даних файлу. Може здатися, що організація блокувань або заборони більш ніж одному процесу працювати з файлом під час виконання системного виклику є зайвою, оскільки в переважній більшості випадків виконання системних викликів і так не уривається, тобто ядро працює в умовах невитісняючої багатозадачності. Проте в даному випадку це не зовсім так. Операції читання і запису займають тривалий час і лише ініціюються центральним процесором, а здійснюються по незалежних каналах, тому установка блокувань на час системного виклику є необхідною гарантією атомарності операцій читання і запису. На практиці виявляється достатнім заблокувати один з буферів кеша диска, в заголовку якого ведеться список процесів, чекаючих звільнення даного буфера. Таким чином, відповідно до семантики Unix зміни, зроблені одним користувачем, негайно стають "видні" іншому користувачеві, який тримає даний файл відкритим одночасно з першим.