Do spisu tresci tematu 6
6.4.1 Opis dzialania blokad plikow i rekordow
Spis tresci
Opis dzialania blokad
Motywacja wprowadzenia blokad
Najlepszym przykladem motywujacym koniecznosc istnienia mechanizmu blokad jest
system rezerwacji miejsc w samolotach. Bez mozliwosci blokowania plikow
mogloby dojsc do takiej sytuacji:
Biuro w Nowym Jorku Biuro w Warszawie
Przychodzi klient proszac Przychodzi klient proszac
o bilet z Paryza do Nicei o bilet z Paryza do Nicei
Pani w okienku sprawdza
wolne miejsca w centrali
przez siec komputerowa Pani w okienku sprawdza
dowiaduje sie, ze jest jedno wolne miejsca w centrali
przez siec komputerowa
dowiaduje sie, ze jest jedno
Pani wypisuje bilet klientowi
i zmniejsza ilosc wolnych miejsc.
Pani wypisuje bilet klientowi
i zmniejsza ilosc wolnych miejsc.
W wyniku takiego przeplotu mamy sprzedane dwa bilety na jedno miejsce, dwoch
niezadowolonych klientow i informatyka, ktory napisal system, na liscie dymisji.
Drugim przykladem niezbednosci blokad jest wspolbiezne uzupelnianie i odczyt
bazy danych. Jesli na przyklad w czasie generowania raportu zawierajacego
wydatki firmy, zostanie usuniety rekord z wydatkiem, ktory zostal juz wydrukowany,
a suma wydatkow liczona jest dopiero po wydrukowaniu wszystkich pozycji, to
dochodzimy do sytuacji, w ktorej szef firmy stwierdza, ze lepiej bylo dawniej.
Ksiegowa sumowala wolniej, ale nie mylila sie w rachunkach. Aby zapobiec
takim sytuacjom wymyslono system blokowania plikow i rekordow.
Krotka historia blokad
Na poczatku blokady byly jedynie blokadami doradzanymi (ang. advisory),
ktore nie mialy wplywu na dzialanie funkcji pisania i czytania. W tej sytuacji
program nie uzywajacy mechanizmu blokad mial dalej pelny dostep do calego pliku
pomimo zalozonych blokad. Wtedy juz istnialy dwa typy blokad.
Pierwszy to propagowany przez standard POSIX blokowanie tylko
rekordow pliku. Konkurencyjnym podejsciem jest blokowanie od razu calego
pliku, a metode ta preferowal standard BSD. W Linuxie zaimplementowane
zostaly obydwa standardy blokowania. Na poczatku istniala tylko jedna
osobowosc blokady, ale po pewnym czasie, kiedy semantyka blokad nieco sie zmienila,
zaimplementowane zostaly dwie rozdzielne osobowosci blokad. Rozdzielne oznacza
takie, ktore nie moga jednoczesnie byc zakladane na jeden plik. Aby rozroznic
obydwa typy blokad nazwano je POSIX i FLOCK.
Byly to jednak nadal blokady doradzane, nie blokujace operacji wejscia/wyjscia.
Poniewaz nie wszystkie programy sa pisane dobrze, a niektore sa wrecz
beznadziejne, Linus zdecydowal o
wpowadzeniu mozliwosci blokady pliku w sposob naprawde zakazujacy operacji.
Wprowadzono obowiazkowe (and. mandatory) blokady wyspecyfikowane w Systemie V.
Aby jednak zachowac zgodnosc ze starymi specyfikacjami nie zmieniono sposobu
zakladania i zdejmowania blokad. Blokowanie obowiazkowe jest w Linuxie
rozszerzeniem standardu POSIX. Zadna blokada FLOCK nie
jest obowiazkowa.
Aby uzyskac obowiazkowe blokady nalezy ustawic odpowiednia opcje kompilacji
jadra i potem ustawic odpowiednie flagi dla pliku, ktory ma byc blokowany tym
mechanizmem. Pozwala to na posiadanie plikow blokowanych w sposob standardowy
i obowiazkowy w jednym systemie.
Aby blokady dzialaly w sposob prawidlowy, powinny byc zaimplementowalne w
sposob niepodzielny (ang. atomic) co od razu podsuwa mysl zlokalizowania
funkcji blokujacych w jadrze systemu.
Struktury danych uzywane przy blokadach
Wewnetrzne struktury przechowujace informacje o blokadach zdefiniowane sa
w pliku include/linux/fs.h
:
struct file_lock {
struct file_lock *fl_next; /* nastepna na liscie blokada pliku */
struct file_lock *fl_nextlink; /* nastepna na liscie wszystkich blokad */
struct file_lock *fl_prevlink; /* poprzednia blokada */
struct file_lock *fl_block; /* lista blokad spiacych na tej
uzywane w blokadach FLOCK */
struct task_struct *fl_owner; /* wlasciciel blokady
uzywane w blokadach POSIX */
struct wait_queue *fl_wait; /* struktura do usypiania procesow */
struct file *fl_file; /* wskaznik pliku, na ktory zalozono blokade
uzywane w blokadach FLOCK */
char fl_flags; /* typ blokady : POSIX lub FLOCK */
char fl_type; /* typ blokady: czytanie, pisanie .. */
off_t fl_start; /* poczatek blokowanego rekordu
uzywane w blokadach POSIX */
off_t fl_end; /* koniec blokowanego rekordu
uzywane w blokadach POSIX */
};
Instancje tej struktury sa podzielone na grupy dotyczace konkretnych plikow
(i-wezlow) i podpiete sa pod pole i_flock
w i-wezle. Dodatkowo
wszystkie blokady wstawiane sa do listy file_lock_table
wykorzystywanej na przyklad przy sprawdzaniu zastoju.
Semantyka blokad typu POSIX.
- Blokady zakladane sa na pewien obszar pliku
- Blokada moze byc typu: blokada zapisu lub blokada odczytu
- Blokada zwiazana jest z konkretnym procesem
- Lista blokad przechowywana jest i-wezle
- Blokady nalezace do jednego procesu nie moga sie pokrywac
- Blokady nalezace do jednego procesu nie koliduja ze soba
- Blokady dotyczace rozlacznych rekordow nie koliduja ze soba
- Dwie blokady czytania nie koliduja ze soba
- Blokady pisania koliduja ze soba jesli dotycza tego samego obszaru
- Dwie blokady pisania i czytania koliduja ze soba jesli dotycza tego samego obszaru
- Dwie blokady przecinajace sie nalezace do jednego wlasciciela i bedace
jednego typu sa zamieniane na jedna blokade obejmujaca obszar obydwu blokad.
- Jesli wlasciciel starej blokady zaklada nowa to z obszaru przeciecia
zostaje zdjeta stara blokada, a nowa zakladana jest na zadany obszar
- Tylko wlasciciel moze zdjac blokade.
- Dopuszczalne jest zdjecie blokady z czesci zajmowanego obszaru
- Nie mozna uspic procesu, jesli spowoduje to zastoj
- Zamkniecie pliku powoduje anulowanie wszystkich blokad na pliku zalozonych
przez proces
Jak widac blokady POSIX sa bardzo poteznym narzedziem, pozwalajacym
na zakladanie wielu blokad na jeden plik z zapewnieniem, ze nie doprowadzimy
do zastoju. Operacje na blokadach POSIX wykonywane
sa za pomoca funkcji systemowej fcntl()
. Dawniej istniala funkcja
lockf()
bedaca nakladka (ang. wrapper) na funkcje fcntl()
operujaca tylko na blokadach, jednak w najnowszych wersjach Linuxa nie
zaimplementowano jej.
Semantyka blokad typu FLOCK.
- Blokady zakladane sa na caly plik
- Blokada moze byc dzielona (ang. shared) lub wylaczna (ang. exclusive)
- Blokada jest zwiazana ze wskaznikiem pliku (ang. file pointer, flick)
- Lista blokad przechowywana jest w i-wezle
- Na jeden wskaznik pliku (nie na plik) moze zostac zalozona tylko jedna blokada
- Dwie blokady dzielone nie koliduja ze soba
- Blokady wylaczne koliduja ze soba, jesli dotycza jednego pliku
- Blokady wylaczne i dzielone koliduja ze soba jesli dotycza tego samego pliku
- Jesli na wskaznik pliku zakladana jest nowa blokada, to stara jest kasowana
- Tylko proces majacy dostep do danego wskaznika pliku, moze zdjac blokade
- Blokada jest zdejmowana z calego pliku
- Zamkniecie pliku nie powoduje usuniecia blokad
Zwiazanie blokady ze wskaznikiem pliku powoduje, ze prawo zmiany blokady jest
dziedziczone na potomkow. Nie mamy wiec mozliwosci stwierdzenia, kto jest
faktycznym wlascicielem blokady. To wlasnie bylo powodem zabronienia uzywania
roznych typow blokad na jednym pliku. A oto przyklad sytuacji, kiedy jestemy
w zastoju i nie wiemy nawet o tym (zakladajac mozliwosc mieszania blokad) :
- Proces 100 zaklada blokade POSIX na czesc pliku A
- Proces 200 zaklada blokade FLOCK na plik B
- Proces 200 uruchamia proces 201 i konczy sie
- Proces 201 probuje zalozyc blokade FLOCK na plik A i zostaje uspiony
- Proces 100 probuje zalozyc blokade POSIX na plik B i zostaje uspiony
W momencie proby zakladania blokady na plik B nie mamy mozliwosci dowiedzenia
sie, kto naprawde sprawuje wladze nad blokada, gdyz nie ma juz procesu
ktory zalozyl blokade na plik A, a jednak istnieje proces mogacy zdjac blokade.
Ze specyfikacji wynika, ze nie da sie wprowadzic prostego mechanizmu kontroli,
czy nie tworzymy zastoju procesow dla blokad typu FLOCK.
Algorytm wykrywajacy taka sytuacje musialby posiadac bardzo wiele informacji
o procesach, ktore kiedys istnialy. Poza tym czas potrzebny na sprawdzenie
znacznie obnizylby wydajnosc systemu.
Do operacji na blokadach typu FLOCK
sluzy funkcja systemowa
flock()
.
Blokady obowiazkowe
Blokady obowiazkowe sa naturalnym rozszerzeniem blokad doradzanych.
Zalozeniem implementacji bylo nie zmienianie istniejacych funkcji a jednoczesnie
umozliwienie stosowania rownolegle blokad doradzanych i obowiazkowych.
Zrealizowane zostalo to za pomoca zaznaczania pojedynczych plikow jako
kandydatow do stosowania blokad obowiazkowych. Aby uzyskac blokady obowiazkowe
nalezy ustawic bit group_id (ATTR_GID) flagi pliku oraz skasowac
bit group_execute z praw dostepu. Od tej chwili blokada POSIX zalozona
na ten plik bedzie obowiazujaca.
A oto semantyka blokad obowiazkowych:
- Blokada moze byc tylko typu POSIX
- Obszar zablokowany blokada
F_RDLCK
moze zostac odczytany przez
inny proces, ale nie moze byc do niego nic zapisane. Proba zapisu spowoduje
uspienie procesu do czasu zdjecia blokady.
- Obszar zablokowany blokada
F_WRLCK
nie moze zostac odczytany
ani zapisany przez inny proces niz wlasciciel blokady. Proby zapisu/odczytu
spowoduja uspienie procesu do czasu zdjecia blokady.
- Jesli plik zostal otwarty z flaga
O_NONBLOCK
, operacje
pisania i czytania nie beda powodowaly uspienia tylko powrot z wynikiem EAGAIN.
- Wywolanie funkcji
open()
z flaga O_TRUNC
lub
funkcji creat()
na istniejacym pliku z zalozona blokada obowiazkowa
zakonczy sie wynikiem EAGAIN
Po wlaczeniu opcji blokad obowiazkowych zmienia sie dzialanie funkcji:
- read()
- write()
- open()
- creat()
- truncate()
Bibliografia
- Pliki zrodlowe Linuxa:
- Dokumentacja do linuxa:
Autor: Andrzej Boczek