| fcntl(2) | System Calls Manual | fcntl(2) |
fcntl - manipuluje deskryptorem pliku
Standardowa biblioteka C (libc, -lc)
#include <fcntl.h>
int fcntl(int fd, int op, ... /* arg */ );
fcntl dokonuje jednej z operacji opisanych poniżej na otwartym deskryptorze pliku fd. Wykonywana operacja jest określona przez op.
fcntl() opcjonalnie może przyjąć trzeci argument. To, czy argument ten jest wymagany, zależy od op. Wymagany typ argumentu jest wskazany w nawiasie po każdej nazwie op (zwykle wymaganym typem jest int, a argument jest identyfikowany określeniem arg) lub podane jest void, gdy argument nie jest wymagany.
Niektóre z poniższych operacji są obsługiwane jedynie w określonej wersji jądra Linux. Preferowaną metodą sprawdzenia, czy działające aktualnie jądro obsługuje daną operację, jest przywołanie fcntl() z daną wartością op i sprawdzenie, czy wywołanie zawiedzie z błędem EINVAL wskazując, że jądro nie rozpoznało tej wartości.
Następujące operacje kontrolują znaczniki powiązane z deskryptorem pliku. Obecnie zdefiniowano jedynie jeden taki znacznik: FD_CLOEXEC, znacznik zamknięcia-przy-wykonaniu. Jeśli ustawiony jest bit FD_CLOEXEC, to deskryptor pliku zostanie automatycznie zamknięty podczas pomyślnego wykonania execve(2) (jeśli execve(2) zawiedzie, deskryptor pliku jest pozostawiany otwarty). Jeśli bit FD_CLOEXEC nie jest ustawiony, deskryptor pliku pozostanie otwarty podczas wykonania execve(2).
W programach wielowątkowych, użycie F_SETFD fcntl() do ustawienia znacznika zamknięcia przy uruchomieniu w tym samym czasie, w którym inny wątek wykonuje fork(2) i execve(2) jest narażone na wystąpienie sytuacji wyścigu, która może niezamierzenie prowadzić do wycieku deskryptora pliku do programu wykonującego proces potomny. Szczegóły i sposób na uniknięcie tego problemu opisano przy znaczniku O_CLOEXEC, w podręczniku open(2).
Z każdym opisem otwartego pliku stowarzyszonych jest kilka znaczników inicjowanych przez open(2), które mogą ewentualnie być modyfikowane przez fcntl(2). Zduplikowane deskryptory pliku (utworzone za pomocą dup(2), fork(2), itp.) odnoszą się do tego samego opisu otwartego pliku, dzieląc zatem te same znaczniki stanu pliku.
Znaczniki stanu pliku i ich znaczenie są opisane w open(2).
Linux implementuje tradycyjne („powiązane z procesem”) blokady rekordów UNIX, zgodnie ze standardem POSIX. Istnieje również typowo linuksowa alternatywa używająca lepszej semantyki — blokady opisów otwartego pliku, które są opisane nieco niżej.
F_SETLK, F_SETLKW i F_GETLK służą do zakładania, zwalniania i sprawdzania obecności blokad rekordów (znanych również jako blokady zakresu bajtów, segmentów pliku lub obszarów pliku). Trzeci argument, lock, jest wskaźnikiem do struktury zawierającej co najmniej następujące pola (kolejność nie jest określona).
struct flock {
...
short l_type; /* Rodzaj blokady: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* Sposób interpretacji l_start:
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Początek (przesunięcie) blokady */
off_t l_len; /* Liczba blokowanych bajtów */
pid_t l_pid; /* PID procesu uniemożliwiającego blokadę
(ustawiane przez F_GETLK i F_OFD_GETLK) */
...
};
Pola l_whence, l_start i l_len w tej strukturze określają zakres bajtów, które mają ulec zablokowaniu. Bajty po końcu pliku mogą być blokowane, nie mogą to być natomiast bajty przed jego początkiem.
l_start jest początkowym przesunięciem blokady i jest interpretowane w odniesieniu do: początku pliku (gdy l_whence wynosi SEEK_SET), bieżącego przesunięcia pliku (gdy l_whence wynosi SEEK_CUR) albo końca pliku (gdy l_whence wynosi SEEK_END). W ostatnich dwóch przypadkach, l_start może być liczbą ujemną zakładając, że przesunięcie nie prowadzi przed początek pliku.
l_len określa liczbę bajtów do zablokowania. Jeśli l_len jest dodatnie, to przedział do zablokowania pokrywa bajty od l_start do l_start+l_len-1 włącznie. Podanie 0 w l_len ma specjalne znaczenie: blokowane są wszystkie bajty od położenia określonego przez l_whence i l_start, aż do końca pliku, niezależnie od tego, jak duży staje się plik.
POSIX.1-2001 zezwala implementacji (lecz tego nie wymaga) na obsługę ujemnych wartości l_len; jeśli l_len jest ujemna, to przedział, którego dotyczy lock obejmuje bajty od l_start+l_len do l_start-1 włącznie. Jest to obsługiwane od Linuksa 2.4.21 i Linuksa 2.5.49.
Pole l_type może służyć do założenia blokady dla odczytu (F_RDLCK) lub dla zapisu (F_WRLCK) do pliku. Dowolna liczba procesów może utrzymywać blokadę dla odczytu pliku (blokada wspólna) w pewnym jego obszarze, ale tylko jeden proces może utrzymywać blokadę dla zapisu do pliku (blokada wyłączna). Blokada wyłączna wyklucza wszelkie inne blokady, zarówno wspólne, jak i wyłączne. Pojedynczy proces może w danym obszarze pliku utrzymywać blokadę tylko jednego rodzaju; gdy w aktualnie zablokowanym obszarze zakładana jest nowa blokada, to istniejąca blokada jest przekształcana w blokadę nowego typu (takie przekształcenie może pociągać za sobą podział, skrócenie lub połączenie z istniejącą blokadą, gdy zakres bajtów podany dla nowej blokady nie pokrywa się dokładnie z zakresem istniejącej blokady).
Aby założyć blokadę do odczytu, deskryptor fd musi być otwarty do odczytu. Aby założyć blokadę do zapisu, deskryptor fd musi być otwarty do zapisu. Aby założyć obydwa rodzaje blokad, należy otworzyć plik do odczytu i zapisu.
Przy umieszczaniu blokady z F_SETLKW, jądra wykrywa zakleszczenia, gdy żądania blokad dwóch lub więcej procesów są wzajemnie zablokowane przez blokady utrzymywane przez inne procesy. Przykładowo, przypuśćmy, że proces A utrzymuje blokadę zapisu na bajcie 100. pliku, a proces B utrzymuje blokadę zapisu na bajcie 200. Jeśli każdy z procesów spróbuje następnie zablokować bajt już zablokowany przez drugie proces za pomocą F_SETLKW, to — bez wykrywania zakleszczeń — oba procesy pozostałyby stale zablokowane. Jeśli jądro wykryje takie zakleszczenie, to spowoduje natychmiastowe niepowodzenie jednego z żądań blokady, z błędem EDEADLK; aplikacja napotykająca na taki błąd powinna zwolnić niektóre ze swoich blokad, aby pozwolić działać inny aplikacjom, przed ponowną próbą odzyskania wymaganych blokad. Wykrywane są również koliste zakleszczenia, z więcej niż dwoma procesami. Proszę jednak zauważyć, że algorytm wykrywania zakleszczeń jądra ma swoje ograniczenia, zob. USTERKI.
Oprócz usunięcia za pomocą wyraźnego F_UNLCK, blokady rekordów są zwalniane automatycznie po zakończeniu procesu.
Blokady rekordów nie są dziedziczone przez procesy potomne poprzez fork(2), ale są zachowywane poprzez execve(2).
Ze względu na wykonywane przez bibliotekę stdio(3) buforowanie, należy unikać blokowania rekordów w połączeniu z funkcjami z tego pakietu; zamiast tego należy używać read(2) i write(2).
Opisane wyżej blokady rekordów są związane z procesem (w przeciwieństwie do blokad opisu otwartego pliku, opisanych niżej). Ma to pewne niefortunne konsekwencje:
Blokady opisu otwartego pliku rozwiązują oba te problemy.
Blokady opisu otwartego pliku są blokadami doradczymi, definiowanymi w zakresie bajtów, których działanie jest w większości identyczne do tradycyjnych blokad rekordów opisanych wyżej. Ten typ blokad jest typowo linuksowy i jest dostępny od Linuksa 3.15 (w Austin Group istnieje propozycja włączenia tego typu blokady do następnej rewizji POSIX.1). Wyjaśnienie opisu otwartego pliku znajduje się w podręczniku open(2).
Podstawową różnicą, pomiędzy dwoma typami blokad jest fakt, że o ile tradycyjne blokady rekordów są związane z procesem, to blokady opisu otwartego pliku są związane z opisem otwartego pliku, na którym je uzyskano, podobnie jak to wygląda w przypadku blokad uzyskanych za pomocą flock(2). W efekcie (inaczej niż przy tradycyjnych blokadach doradczych rekordów) blokady opisu otwartego pliku są dziedziczone przy fork(2) (i clone(2) ze znacznikiem CLONE_FILES), a także są automatycznie zwalniane po ostatnim zamknięciu opisu otwartego pliku, zamiast zwalniania przy jakimkolwiek zamknięciu pliku.
Kolidujące kombinacje blokad (tj. blokada odczytu z blokadą zapisu lub dwie blokady zapisu) gdy jedna blokada jest blokadą opisu otwartego pliku, a druga jest tradycyjną blokadą rekordu prowadzą do konfliktu nawet wówczas, gdy są uzyskane przez ten sam proces na tym samym deskryptorze pliku.
Blokady opisu otwartego pliku umieszczone na tym samym opisie otwartego pliku (tj. za pomocą tego samego deskryptora pliku lub za pomocą duplikatu deskryptora pliku utworzonego przez fork(2), dup(2), F_DUPFD z fcntl() itp.) są zawsze kompatybilne: jeśli nowa blokada jest umieszczona na już zablokowanym rejonie pliku, to istniejące blokada jest konwertowana na nowy typ blokady (takie konwersje mogą prowadzić to podziału, zmniejszenia lub złączenia z dotychczasową blokadą, jak opisano to wyżej)
Z drugiej strony, blokady opisu otwartego pliku mogą być w konflikcie, gdy są uzyskane przez różne opisy otwartego pliku. Z tego względu, program wielowątkowy może korzystać z blokad opisu otwartego pliku do synchronizowania dostępu do jakiegoś miejsca w pliku, otwierając (open(2)) plik z różnych wątków i zakładając blokady za pomocą wynikowego deskryptora pliku.
Podobnie jak w przypadku tradycyjnych blokad doradczych, trzeci argument do fcntl() — lock, jest wskaźnikiem do struktury flock. W odróżnieniu do tradycyjnych blokad rekordów, pole l_pid tej struktury musi być ustawione na zero za pomocą operacji opisanych niżej.
Operacje działające z blokadami opisu otwartego pliku są analogiczne do tych używanych z tradycyjnymi blokadami:
W bieżącej implementacji, dla blokad opisu otwartego pliku nie zachodzi wykrywanie zakleszczeń (w odróżnieniu od blokad rekordów związanych z procesem, dla których jądro wykonuje wykrywanie zakleszczeń).
Ostrzeżenie: linuksowa implementacja blokowania obowiązującego jest zawodna (zob. USTERKI poniżej). Z powodu opisanych błędów i faktu, że funkcjonalność ta nie była często wykorzystywana, od Linuksa 4.5, tego typu blokowanie stało się opcjonalne i zależy od ustawienia opcji konfiguracyjnej (CONFIG_MANDATORY_FILE_LOCKING). Od Linuksa 5.15 blokowanie obowiązujące (przymusowe) nie jest już w ogólne obsługiwane.
Domyślnie, zarówno tradycyjne blokady (związane z procesem) jak i blokady opisu otwartego pliku (OFD) są doradcze. Blokady doradcze nie są wymuszane i są przydatne tylko w przypadku współdziałających procesów.
Oba te typy mogą być również obowiązujące. Blokady obowiązujące są wymuszane dla wszystkich procesów. Jeśli proces spróbuje uzyskać niezgodny dostęp (tj. odczyt — read(2) lub zapis — write(2)) w obszarze pliku, który posiada niezgodną blokadę obowiązującą, to wynik zależy od tego, czy dla jego opisu otwartego pliku włączono znacznik O_NONBLOCK. Jeśli znacznik O_NONBLOCK nie jest włączony, to dane wywołanie systemowe jest blokowane do momentu usunięcia blokady lub jej przekształcenia w tryb, który jest zgodny z uzyskiwanym dostępem. Jeśli znacznik O_NONBLOCK jest włączony, to wywołanie systemowe zawodzi z błędem EAGAIN.
Aby skorzystać z obowiązujących blokad, blokowanie obowiązujące musi być włączone zarówno na systemie plików zawierającym blokowany plik, jak i na samym pliku. Blokowanie obowiązujące w systemie plików włącza się za pomocą opcji „-o mand” programu mount(8) lub za pomocą znacznika MS_MANDLOCK do mount(8). Blokowanie obowiązujące na pliku włącza się poprzez wyłączenie prawa uruchamiania dla grupy i włączenie bitu set-group-ID (zob. chmod(1) i chmod(2)).
Blokowanie obowiązujące nie jest określone normą POSIX. Niektóre inne systemy również obsługują blokowanie obowiązujące, choć szczegóły ich włączenia również się między systemami.
Gdy blokada doradcza jest uzyskiwana na sieciowym systemie plików, takim jak NFS, możliwe jest zagubienie blokady. Może się tak stać ze względu na działanie administracyjne na serwerze lub z powodu podziału sieci (tzn. utraty połączenie z serwerem), które będzie trwało na tyle długo, że serwer może przyjąć brak dalszego funkcjonowania przez klienta.
Gdy system plików stwierdzi, że blokada została zagubiona, przyszły odczyt (read(2)) lub zapis (write(2)) może zawieść z błędem EIO. Błąd ten będzie występował do momentu usunięcia blokady lub zamknięcia deskryptora pliku. Od Linuksa 3.12, dzieje się tak przynajmniej w NFSv4 (we wszystkich jego pomniejszych wydaniach).
Niektóre wersje Uniksa wysyłają w takiej sytuacji sygnał (SIGLOST). Linux nie definiuje tego sygnału i nie zapewnia żadnego asynchronicznego powiadamiania o zagubionych blokadach.
F_GETOWN, F_SETOWN, F_GETOWN_EX, F_SETOWN_EX, F_GETSIG i F_SETSIG służą do zarządzania sygnałami dostępności wejścia/wyjścia:
struct f_owner_ex {
int type;
pid_t pid;
};
Za pomocą tych mechanizmów program może zaimplementować w pełni asynchroniczne wejście/wyjście, nie używając przez większość czasu select(2) i poll(2).
Opisane powyżej korzystanie z O_ASYNC jest specyficzne dla BSD i Linuksa. Jedynym użyciem F_GETOWN i F_SETOWN podanym w POSIX.1 jest użycie ich w połączeniu z sygnałem SIGURG na gniazdach (POSIX nie określa sygnału SIGIO). F_GETOWN_EX, F_SETOWN_EX, F_GETSIG i F_SETSIG są typowo linuksowe. POSIX posiada asynchroniczne wejście/wyjście i strukturę aio_sigevent służącą do podobnych celów; w Linuksie są one również dostępne jako część biblioteki GNU C (glibc).
F_SETLEASE i F_GETLEASE (od Linuksa 2.4 wzwyż) służą do ustanowienia nowe dzierżawy i pobrania aktualnej dzierżawy na opisie otwartego pliku, do którego odnosi się deskryptor pliku fd. Dzierżawa pliku zapewnia mechanizm, w którym proces utrzymujący dzierżawę („dzierżawca”) jest zawiadamiany (poprzez dostarczenie sygnału) o tym, że inny proces („zrywający dzierżawę”) próbuje wykonać open(2) lub truncate(2) na pliku, do którego odnosi się dany deskryptor pliku.
Dzierżawy są związane z opisem otwartego pliku (zob. open(2)). Oznacza to, że zduplikowane deskryptory pliku (utworzone np. za pomocą fork(2) lub dup(2)) odnoszą się do tej samem dzierżawy i można ją zmodyfikować lub zwolnić za pomocą dowolnego z tych deskryptorów. Co więcej, dzierżawa jest zwalniana przez operację F_UNLCK na dowolnym z tych zduplikowanych deskryptorów plików albo gdy wszystkie takie deskryptory pliku zostaną zamknięte.
Dzierżawy można ustanawiać tylko na zwykłych plikach. Proces nieuprzywilejowany może ustanawiać jedynie dzierżawy na plikach, których UID (właściciela) odpowiada UID-owi systemu plików dla danego procesu. Proces z przywilejem CAP_LEASE (ang. capability) może ustanawiać dzierżawy na dowolnych plikach.
Gdy proces („zrywający dzierżawę”) wykona operację open(2) lub truncate(2) kolidującą z dzierżawą ustanowioną poprzez F_SETLEASE, wywołanie funkcji systemowej jest blokowane przez jądro, a jądro zawiadamia dzierżawcę poprzez wysłanie sygnału (domyślnie SIGIO). Dzierżawca powinien odpowiedzieć na otrzymanie tego sygnału wykonując porządki niezbędne dla przygotowania pliku do dostępu przez inny proces (np. zrzucenie buforów) a następnie usunięcie lub zmniejszenie swojej dzierżawy. Dzierżawa jest usuwana poprzez wykonanie operacji F_SETLEASE podając jako arg F_UNLCK. Jeśli dzierżawca aktualnie utrzymuje dzierżawę zapisu na pliku, a zrywający dzierżawę otwiera plik do odczytu, to wystarczające jest zmniejszenie dzierżawy przez dzierżawcę do dzierżawy odczytu. Dokonuje się tego poprzez wykonanie operacji F_SETLEASE podając jako arg F_RDLCK.
Jeśli dzierżawca nie zmniejszy lub nie zwolni dzierżawy w ciągu podanej w /proc/sys/fs/lease-break-time liczby sekund, to jądro przymusowo usunie lub zmniejszy dzierżawę dzierżawcy.
Po zainicjowaniu zerwania dzierżawy, F_GETLEASE zwraca docelowy typ dzierżawy (F_RDLCK albo F_UNLCK, w zależności od tego, co byłoby zgodne ze zrywającym dzierżawę) do momentu, gdy dzierżawca dobrowolnie nie zmniejszy lub nie zwolni dzierżawy albo do momentu, gdy jądro tego nie wymusi po upłynięciu czasu zerwania dzierżawy.
Po dobrowolnym lub wymuszonym usunięciu lub zmniejszeniu dzierżawy, przy założeniu, że wywołanie funkcji systemowej przez zrywającego dzierżawę nie jest nieblokujące, jądro pozwala na kontynuację funkcji systemowej wywołanej przez zrywającego dzierżawę.
Jeśli zablokowane wywołanie open(2) lub truncate(2) zrywającego dzierżawę zostanie przerwane przez procedurę obsługi sygnału, to wywołanie systemowe zawiedzie z błędem EINTR, lecz inne kroki opisane wyżej, wciąż zostaną przeprowadzone. Jeśli zrywający dzierżawę zostanie zabity sygnałem w trakcie blokowania open(2) lub truncate(2), to inne kroki opisane wyżej, wciąż zostaną przeprowadzone. Jeśli zrywający dzierżawę poda znacznik O_NONBLOCK przy wywoływaniu open(2), to wywołanie zawiedzie natychmiast z błędem EWOULDBLOCK, lecz inne kroki opisane wyżej, wciąż zostaną przeprowadzone.
Domyślnym sygnałem stosowanym do zawiadamiania dzierżawcy jest SIGIO, lecz można go zmienić za pomocą operacji F_SETSIG w fcntl(). Jeśli wydano operację F_SETSIG (nawet podając SIGIO), a funkcja obsługi sygnału została określona za pomocą SA_SIGINFO, to ta funkcja obsługi otrzyma jako drugi argument strukturę siginfo_t, której pole si_fd będzie zawierać deskryptor pliku dzierżawionego pliku, do którego uzyskuje dostęp inny proces (jest to przydatne, gdy wywołujący utrzymuje dzierżawy na wielu plikach).
Pieczęcie pliku ograniczają zestaw dozwolonych operacji na danym pliku. Od momentu ustawienia pieczęci na pliku, określony zestaw operacji na tym pliku zawiedzie z błędem EPERM. Taki plik określany jest jako zapieczętowany (ang. sealed). Domyślny zestaw pieczęci zależy od typu pliku i systemu plików. Przegląd pieczętowania plików, opis celowości i nieco przykładów kodu zawarto w podręczniku memfd_create(2).
Obecnie, pieczęcie pliku można zastosować tylko na deskryptorze pliku zwróconym przez memfd_create(2) (jeśli użyto MFD_ALLOW_SEALING). W innych systemach plików, wszystkie operacje fcntl() działające na pieczęciach zwrócą EINVAL.
Pieczęcie są właściwością i-węzła. Z tego powodu, wszystkie opisy otwartego pliku (OFD) odnoszące się do tego samego i-węzła dzielą ten sam zestaw pieczęci. Co więcej, pieczęci nigdy nie można usunąć, można je jedynie dodawać.
Dostępne są następujące pieczęcie:
Do poinformowania jądra o oczekiwanym, względnym czasie istnienia zapisów do danego i-węzła lub za pomocą opisu otwartego pliku (OFD; więcej informacji o opisach otwartego pliku w podręczniku open(2)) można użyć wskazówek czasu istnienia zapisu. W tym kontekście, pojęcie „czas istnienia zapisu” oznacza oczekiwany czas, jaki dane będą istniały na nośniku, przed ich nadpisaniem lub usunięciem.
Aplikacja może korzystać z różnych, podanych niżej, wartości wskazówek, aby podzielić zapisy na różne klasy zapisów, dzięki czemu wielu użytkowników lub aplikacji działających na stacji z pojedynczym nośnikiem może zbierać swoje wejścia/wyjścia w spójny sposób. Jednak znaczniki te nie udostępniają żadnego funkcjonalnego zachowania, a różne klasy wejścia/wyjścia mogą używać wskazówek o czasie trwania zapisu w arbitralny sposób dopóki tylko wskazówki są stosowane konsekwentnie.
Do deskryptora fd można zastosować następujące operacje:
Jeśli opisowi otwartego pliku nie przypisano wskazówki odczytu/zapisu, powinien on użyć wartości przypisanej i-węzłowi, jeśli taka istnieje.
Od Linuksa 4.13 prawidłowe są następujące wskazówki odczytu/zapisu:
Wszystkie wskazówki dotyczące zapisu są jedynie relatywne względem siebie i nie należy przypisywać im bezwzględnego znaczenia.
Wartość zwracana po pomyślnym zakończeniu funkcji zależy od operacji:
W razie wystąpienia błędu zwracane jest -1 i ustawiane errno wskazując błąd.
POSIX.1-2008.
F_GETOWN_EX, F_SETOWN_EX, F_SETPIPE_SZ, F_GETPIPE_SZ, F_GETSIG, F_SETSIG, F_NOTIFY, F_GETLEASE i F_SETLEASE są typowo linuksowe (należy zdefiniować makro _GNU_SOURCE aby pozyskać te definicje).
F_OFD_SETLK, F_OFD_SETLKW i F_OFD_GETLK są typowo linuksowe (i konieczne jest zdefiniowanie _GNU_SOURCE do pozyskania ich definicji), lecz trwają prace nad włączeniem ich do następnej wersji POSIX.1.
F_ADD_SEALS i F_GET_SEALS są typowo linuksowe.
SVr4, 4.3BSD, POSIX.1-2001.
Jedynie operacje F_DUPFD, F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_GETLK, F_SETLK i F_SETLKW są określone w POSIX.1-2001.
F_GETOWN i F_SETOWN są określone w POSIX.1-2001 (do pozyskania ich definicji konieczne jest zdefiniowanie _XOPEN_SOURCE z wartością 500 lub większą albo _POSIX_C_SOURCE z wartością 200809L lub większą).
F_DUPFD_CLOEXEC jest określone w POSIX.1-2008 (do pozyskania jego definicji konieczne jest zdefiniowanie _POSIX_C_SOURCE z wartością 200809L lub większą albo _XOPEN_SOURCE z wartością 700 lub większą).
Błędy zwracane przez dup2(2) są inne niż zwracane przez F_DUPFD.
Pierwotne linuksowe wywołanie systemowego fcntl() nie było zaprojektowane do obsługi dużych przesunięć plików (w strukturze flock). W konsekwencji, Linux 2.4 dodał wywołanie systemowe fcntl64(). Nowsze wywołanie systemowe korzysta z odmiennej struktury do blokowania plików, flock64 i odpowiadających operacji, F_GETLK64, F_SETLK64 i F_SETLKW64. Detale te mogą być jednak zignorowane przez aplikacje używające glibc, ponieważ jej funkcja opakowująca fcntl() obsługuje nowsze wywołanie systemowe, gdy tylko jest dostępne, w sposób przezroczysty.
Od Linuksa 2.0, nie ma oddziaływania pomiędzy typami blokad zakładanych przez flock(2) i przez fcntl().
W niektórych systemach struktura struct flock zawiera dodatkowe pola, takie jak np. l_sysid (do identyfikacji komputera, na którym utrzymywana jest blokada). Oczywiście, samo l_pid jest mało przydatne, gdy proces utrzymujący blokadę może działać na innym komputerze; w Linuksie, choć pole to jest obecne na niektórych architekturach (np. MIPS32), to jednak nie jest używane.
Pierwotne linuksowe wywołanie systemowego fcntl() nie było zaprojektowane do obsługi dużych przesunięć plików (w strukturze flock). W konsekwencji, Linux 2.4 dodał wywołanie systemowe fcntl64(). Nowsze wywołanie systemowe korzysta z odmiennej struktury do blokowania plików, flock64 i odpowiadających operacji, F_GETLK64, F_SETLK64 i F_SETLKW64. Detale te mogą być jednak zignorowane przez aplikacje używające glibc, ponieważ jej funkcja opakowująca fcntl() obsługuje nowsze wywołanie systemowe, gdy tylko jest dostępne, w sposób przezroczysty.
Przed Linuksem 3.12, jeśli klient NFSv4 utraci kontakt z serwerem na pewien czas (zdefiniowany jako ponad 90 sekund bez żadnej komunikacji), to może utracić i odzyskać blokadę bez uświadamiania sobie tego faktu (czas, po którym przyjmuje się utratę połączenia jest znany jako czas dzierżawy NFSv4 (leasetime); na serwerze NFS można go sprawdzić w pliku /proc/fs/nfsd/nfsv4leasetime, który podaje go w sekundach; domyślną wartością w pliku jest 90). Ten scenariusz grozi uszkodzeniem danych, ponieważ inny proces mógł w międzyczasie posiąść blokadę i dokonać operacji wejścia/wyjścia na pliku.
Od Linuksa 3.12, jeśli klient NFSv4 utraci połączenie z serwerem, każda operacja wejścia/wyjścia na pliku, przez proces który „sądzi”, że utrzymuje blokadę zawiedzie, dopóki ten proces nie zamknie i nie otworzy pliku ponownie. Można ustawić parametr jądra nfs.recover_lost_locks na 1, aby powrócić do zachowania sprzed wersji 3.12, gdy klient próbuje odzyskać zagubione blokady po odzyskaniu połączenia z serwerem. Ze względu na towarzyszące temu ryzyko uszkodzenia danych, domyślnie parametr ten wynosi 0 (jest wyłączony).
Nie da się użyć F_SETFL do zmiany statusu znaczników O_DSYNC i O_SYNC. Takie próby zostaną po cichu zignorowane.
Ograniczenie konwencji linuksowego wywołania systemowego na niektórych architekturach (przede wszystkim i386) oznacza, że jeśli (ujemny) identyfikator grupy procesu mający być zwrócony przez F_GETOWN znajduje się w przedziale od -1 do -4095, to zwracana wartość jest przez glibc nieprawidłowo interpretowana jako błąd w wywołaniu systemowym; to jest zwrócona przez fcntl() wartość będzie wynosiła -1, a errno będzie zawierać (dodatni) identyfikator grupy procesu. Typowo linuksowa operacja F_GETOWN_EX unika tego problemu. Od glibc 2.11, glibc czyni problem jądra F_GETOWN niewidzialnym, przez zaimplementowanie F_GETOWN za pomocą F_GETOWN_EX.
W Linuksie 2.4 i wcześniejszych istnieje błąd, który może się ujawnić, gdy proces nieuprzywilejowany używa F_SETOWN do podania właściciela deskryptora pliku gniazda, będące procesem (grupą) inną niż wywołujący. W tym przypadku fcntl() może zwrócić -1 z errno ustawionym na EPERM nawet wówczas, gdy właściciel procesu (grupy) był tym, do którego wywołujący ma prawo wysyłania sygnałów. Pomimo zwracania błędu, własność deskryptora pliku jest ustawiana, a sygnały będą wysyłane do właściciela.
Algorytm wykrywania zakleszczeń używany przez jądro przy przetwarzaniu żądań F_SETLKW może skutkować wykryciami zarówno fałszywie negatywnymi (niewykryciem zakleszczeń, pozostawiając zestaw zakleszczonych procesów stale zablokowanymi), jak i fałszywie pozytywnymi (błąd EDEADLK, gdy zakleszczenie nie występuje). Przykładowo, jądro ogranicza głębokość poszukiwania zależności blokad do 10 kroków co oznacza, że łańcuch kolistych zakleszczeń większy od tego rozmiaru nie zostanie wykryty. Dodatkowo, jądro może nieprawidłowo wskazywać zakleszczenie, gdy dwa lub więcej procesów utworzonych za pomocą znacznika CLONE_FILES z clone(2), wyglądają (dla jądra) na będące w konflikcie.
Linuksowa implementacja blokowania obowiązującego (przymusowego) jest podatna na sytuację wyścigu, co czyni ją nierzetelną: wywołanie write(2), które nachodzi na blokadę może zmodyfikować dane po uzyskaniu blokady, wywołanie read(2) które nachodzi na blokadę może wykryć zmianę danych, dokonaną jedynie po uzyskaniu blokady zapisu. Podobny wyścig istnieje pomiędzy blokadami obowiązującymi a mmap(2). Z tego powodu nie należy polegać na blokowaniu obowiązującym.
dup2(2), flock(2), open(2), socket(2), lockf(3), capabilities(7), feature_test_macros(7), lslocks(8)
locks.txt, mandatory-locking.txt i dnotify.txt w katalogu Documentation/filesystems/ źródeł jądra Linux (w starszych jądrach pliki te znajdują się bezpośrednio w katalogu Documentation/, a plik mandatory-locking.txt ma nazwę mandatory.txt)
Tłumaczenie niniejszej strony podręcznika: Przemek Borys <pborys@dione.ids.pl>, Andrzej Krzysztofowicz <ankry@green.mf.pg.gda.pl> i Michał Kułach <michal.kulach@gmail.com>
Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać zapoznając się z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje się ŻADNEJ ODPOWIEDZIALNOŚCI.
Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres listy dyskusyjnej manpages-pl-list@lists.sourceforge.net.
| 2 maja 2024 r. | Linux man-pages 6.9.1 |