| open(2) | System Calls Manual | open(2) |
open, creat - otwiera i ewentualnie tworzy plik
Standardowa biblioteka C (libc, -lc)
#include <fcntl.h>
int open(const char *pathname, int flags, ...
/* mode_t mode */ );
int creat(const char *pathname, mode_t mode);
int openat(int dirfd, const char *pathname, int flags, ...
/* mode_t mode */ );
/* Udokumentowane oddzielnie, w openat2(2): */ int openat2(int dirfd, const char *pathname, const struct open_how *how, size_t size);
openat():
Od glibc 2.10:
_POSIX_C_SOURCE >= 200809L
Przed glibc 2.10:
_ATFILE_SOURCE
Wywołanie systemowe open() otwiera plik określony ścieżką pathname. Jeśli podany plik nie istnieje, może być opcjonalnie (jeśli we flags podano O_CREAT) utworzony przez open().
Wartością zwracaną przez open() jest deskryptor pliku: niewielka, całkowita liczba nieujemna, będąca indeksem wpisu w tablicy otwartych deskryptorów plików procesu. Deskryptor pliku jest używany w kolejnych wywołaniach systemowych (read(2), write(2), lseek(2), fcntl(2) itp.), w celu odniesienia się do otwartego pliku. Deskryptor pliku zwracany przez pomyślne wywołanie będzie najniższym numerem deskryptora pliku, który nie jest aktualnie otwarty przez proces.
Domyślnie, nowy deskryptor pliku jest ustawiany jako pozostający otwarty po wykonaniu przez execve(2) (tj. znacznik deskryptora pliku FD_CLOEXEC opisany w fcntl(2) jest początkowo wyłączony); można użyć opisanego poniżej znacznika O_CLOEXEC aby zmienić to zachowanie. Przesunięcie pliku jest ustawiane na początek pliku (zob. lseek(2)).
Wywołanie open() tworzy nowy opis otwartego pliku, wpis w systemowej tablicy otwartych plików. Opis otwartego pliku zawiera przesunięcie pliku i znaczniki statusu pliku (zob. niżej). Deskryptor pliku odnosi się do opisu otwartego pliku i na to odniesienie nie ma wpływu późniejsze usunięcie ścieżki pathname lub jej modyfikacja, w celu wskazania innego pliku. Więcej informacji o opisach otwartego pliku zawarto w rozdziale UWAGI.
Argument flags musi zawierać jeden z następujących trybów dostępu: O_RDONLY, O_WRONLY lub O_RDWR. Stanowią one, odpowiednio, żądania otwarcia tylko do odczytu, tylko do zapisu, lub do odczytu i zapisu.
Ponadto, we flags może zsumować bitowo (OR) zero lub więcej znaczników tworzenia pliku i znaczników statusu pliku. Znaczniki tworzenia pliku to: O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_TMPFILE i O_TRUNC. Znaczniki statusu pliku to wszystkie pozostałe znaczniki, wypisane poniżej. Rozróżnieniem pomiędzy tymi dwoma grupami znaczników jest fakt, że znaczniki tworzenia pliku wpływają na zachowanie samej operacji otwarcia, natomiast znaczniki statusu pliku wpływają na zachowanie kolejnych operacji wejścia/wyjścia. Znaczniki statusu pliku można pobrać i (w niektórych przypadkach) zmodyfikować; więcej szczegółów w podręczniku fcntl(2).
Oto pełna lista znaczników tworzenia pliku i znaczników statusu pliku:
char buf[PATH_MAX];
fd = open("jakiś_program", O_PATH);
snprintf(buf, PATH_MAX, "/proc/self/fd/%d", fd);
execl(buf, "jakiś_program", (char *) NULL);
char path[PATH_MAX];
fd = open("/ścieżka/do/katalogu", O_TMPFILE | O_RDWR,
S_IRUSR | S_IWUSR);
/* Wejście/wyjście na 'fd'... */
linkat(fd, "", AT_FDCWD, "/ścieżka/do/pliku", AT_EMPTY_PATH);
/* Jeśli wywołujący nie ma przywileju CAP_DAC_READ_SEARCH
(potrzebnego do użycia AT_EMPTY_PATH z linkat(2)),
i system plików proc(5) jest zamontowany, to powyższe
wywołanie linkat(2) można zastąpić przez:
snprintf(path, PATH_MAX, "/proc/self/fd/%d", fd);
linkat(AT_FDCWD, path, AT_FDCWD, "/ścieżka/do/pliku",
AT_SYMLINK_FOLLOW);
*/
Wywołanie creat() jest równoważne wywołaniu open() z argumentem flags ustawionym na O_CREAT|O_WRONLY|O_TRUNC.
Wywołanie systemowe openat() operuje w dokładnie taki sam sposób jak open(), z wyjątkiem różnic opisanych tutaj.
Argument dirfd jest używany w połączeniu z argumentem pathname, w następujący sposób:
Jeśli ścieżka podana w pathname jest względna, a dirfd nie jest prawidłowym deskryptorem pliku, to wystąpi błąd (EBADF; można zatem posłużyć się tym mechanizmem do upewnienia się, że ścieżka pathname jest bezwzględna, podając w dirfd nieprawidłowy numer deskryptora).
Wywołanie systemowe openat2(2) jest rozszerzeniem openat() i udostępnia nadzbiór funkcji wobec openat(). Jest udokumentowane oddzielnie, w podręczniku openat2(2).
W przypadku powodzenia, open(), openat() i creat() zwracają nowy deskryptor pliku (liczbę nieujemną). W razie wystąpienia błędu zwracane jest -1 i ustawiane errno wskazując błąd.
open(), openat() i creat() mogą zawieść z powodu następujących błędów:
(Niezdefiniowany) wynik O_RDONLY | O_TRUNC różni się w zależności od implementacji. W wielu systemach plik jest faktycznie przycinany.
Opcja POSIX.1-2008 „synchronized I/O” określa różne warianty synchronizowanego wejścia/wyjścia i podaje znaczniki O_SYNC, O_DSYNC i O_RSYNC open() jako przeznaczone do kontrolowania oczekiwanego zachowania. Niezależnie od tego, czy dana implementacja obsługuje tę opcję, obowiązkowa jest obsługa co najmniej O_SYNC w przypadku zwykłych plików.
Linux implementuje O_SYNC i O_DSYNC, lecz nie O_RSYNC. Poniekąd niewłaściwie, glibc definiuje O_RSYNC jako mające tę samą wartość co O_SYNC (O_RSYNC jest zdefiniowane w linuksowym pliku nagłówkowym <asm/fcntl.h> na architekturze HP PA-RISC, lecz nie jest używane).
O_SYNC zapewnia kompletność zsynchronizowanego wejścia/wyjścia pliku w sposób gwarantujący spójność tzn. operacje zapisu przenoszą dane i wszystkie powiązane metadane na przedmiotowe urządzenie. O_DSYNC zapewnia kompletność zsynchronizowanego wejścia/wyjścia danych w sposób gwarantujący spójność tzn. operacje zapisu przenoszą dane na przedmiotowe urządzenie, lecz przeniosą jedynie te aktualizowane metadane, które są konieczne do pomyślnego zakończenia kolejnych operacji odczytu. Ta druga metoda pozwala zredukować liczbę operacji dysku wymaganych w przypadku aplikacji, które nie wymagają gwarancji spójności pliku.
Aby zrozumieć różnicę pomiędzy tymi dwoma typami kompletności, proszę rozważyć dwie cząstki metadanych pliku: znacznik czasu ostatniej modyfikacji pliku (st_mtime) oraz długość pliku. Wszystkie operacje zapisu zaktualizują znacznik czasu ostatniej modyfikacji pliku, lecz jedynie zapisy dodające dane na końcu pliku, spowodują zmianę jego długości. Znacznik czasu ostatniej modyfikacji nie jest wymagany do pomyślnego zakończenia operacji odczytu, w przeciwieństwie do długości pliku. Zatem O_DSYNC zagwarantuje jedynie zaktualizowanie metadanej długości pliku (podczas gdy O_SYNC również metadanej znacznika czasu ostatniej modyfikacji pliku).
Przed Linuksem 2.6.33, Linux implementował dla open() jedynie znacznik O_SYNC. Jednak gdy podało się ten znacznik, większość systemów plików w rzeczywistości zapewniało równoważnik kompletności zsynchronizowanego wejścia/wyjścia danych w sposób zapewniający spójność (tj. O_SYNC był zaimplementowany w rzeczywistości jako ekwiwalent O_DSYNC).
Od Linuksa 2.6.33, zapewniona jest prawidłowa obsługa O_SYNC. Jednak aby zapewnić wsteczną kompatybilność binarną, O_DSYNC zdefiniowano z tą samą wartością co historyczne O_SYNC, a O_SYNC zdefiniowano jako nową (dwubitową) wartość znacznika, która zawiera wartość znacznika O_DSYNC. W ten sposób aplikacje skompilowane z nowymi nagłówkami, otrzymają co najmniej zachowanie O_DSYNC przed Linuksem 2.6.33.
Od glibc 2.26, funkcja opakowująca open() z glibc korzysta z wywołania systemowego openat(), zamiast z wywołania systemowego jądra open(). Na niektórych architekturach działo się to także przed glibc 2.26.
openat2(2) Linux.
Znaczniki O_DIRECT, O_NOATIME, O_PATH i O_TMPFILE są typowo linuksowe. Aby uzyskać ich definicje, należy zdefiniować _GNU_SOURCE.
Znaczniki O_CLOEXEC, O_DIRECTORY i O_NOFOLLOW nie są określone w POSIX.1-2001, lecz są określone w POSIX.1-2008. Od glibc 2.12, można uzyskać ich definicje definiując albo _POSIX_C_SOURCE z wartością większą lub równą 200809L, albo _XOPEN_SOURCE z wartością większą lub równą 700. W glibc 2.11 i wcześniejszych, można uzyskać te definicje definiując _GNU_SOURCE.
W Linuksie, znacznik O_NONBLOCK jest niekiedy używany w przypadkach, gdy chce się otworzyć plik, ale niekonieczne ma się zamiar odczytu lub zapisu z niego. Przykładowo można go użyć do otworzenia urządzenia, w celu uzyskania deskryptora pliku do wykorzystania z ioctl(2).
Należy zauważyć, że open() może otwierać specjalne pliki urządzeń, lecz creat() nie może ich tworzyć. Zamiast niego należy używać mknod(2).
Jeśli plik jest nowoutworzony, to jego pola st_atime, st_ctime, st_mtime (odpowiednio: czas ostatniego dostępu, czas ostatniej zmiany statusu i czas ostatniej modyfikacji, zob. stat(2)) są ustawione na czas bieżący i to samo dotyczy pól st_ctime i st_mtime katalogu nadrzędnego. Natomiast gdy plik jest modyfikowany z powodu użycia znacznika O_TRUNC, jego pola st_ctime i st_mtime są ustawiane na czas bieżący.
Pliki w katalogu /proc/pid/fd pokazują otwarte deskryptory pliku procesu o PID równym pid. Pliki w katalogu /proc/pid/fdinfo pokazują jeszcze więcej informacji o tych deskryptorach pliku. Więcej informacji o obu tych katalogach znajduje się w podręczniku proc(5).
Linuksowy plik nagłówkowy <asm/fcntl.h> nie definiuje O_ASYNC; definiowany jest jednak (wywodzący się z BSD) synonim FASYNC.
Termin „opis otwartego pliku ” (ang. „open file description”) jest używany przez POSIX w odniesieniu do wpisów w systemowej tablicy otwartych plików. W innych kontekstach, obiekt ten miewa również następujące określenia (w nawiasach określenia angielskie): „obiekt otwartego pliku” („open file object”), „uchwyt pliku” („file handle”), „wpis tablicy otwartych plików” („open file table entry”) albo — w żargonie deweloperów jądra — plik struct („struct file”).
Gdy deskryptor pliku jest duplikowany (za pomocą dup(2) lub podobnego), to duplikat odnosi się do tego samego opisu otwartego pliku, co pierwotny deskryptor pliku; oba deskryptory dzielą zatem przesunięcie pliku i znaczniki statusu pliku. Takie dzielenie może również nastąpić między procesami: proces potomny utworzony za pomocą fork(2) dziedziczy duplikaty deskryptorów pliku swojego rodzica, a te duplikaty odnoszą się do tych samych opisów otwartego pliku.
Każde otwarcie pliku za pomocą open() tworzy nowy opis otwartego pliku; zatem może istnieć wiele opisów otwartego pliku odnoszących do i-węzła pliku.
W Linuksie, można użyć operacji KCMP_FILE kcmp(2) do sprawdzenia, czy dwa deskryptory pliku (w tym samym procesie lub w dwóch różnych procesach) odnoszą się do tego samego opisu otwartego pliku.
Jest wiele niedogodności w protokole podległym NFS, dotykających między innymi O_SYNC i O_NDELAY.
Na systemach NFS z włączonym mapowaniem UID-ów, open() może zwrócić deskryptor pliku, dla którego np. żądania read(2) są zabronione przy ustawionym EACCES. Jest to związane ze sprawdzaniem uprawnień odbywającym się na kliencie, ale to serwer wykonuje mapowanie UID-ów podczas żądań odczytu i zapisu.
Otwarcie końca do odczytu lub końca do zapisu FIFO blokuje, do momentu otwarcia również drugiego z końców (przez inny proces lub wątek). Więcej informacji w podręczniku fifo(7).
W przeciwieństwie do innych wartości, jakie można podać we flags, wartości trybu dostępu: O_RDONLY, O_WRONLYi O_RDWR nie określają pojedynczych bitów. Definiują one dwa bity niższego rzędu flags i są zdefiniowane jako, odpowiednio, 0, 1 i 2. Innymi słowy, połączenie O_RDONLY | O_WRONLY jest błędem logicznym, w szczególności nie ma takiego samego znaczenia jak O_RDWR.
Linux rezerwuje specjalny, niestandardowy tryb dostępu 3 (binarne 11) we flags, do następującego znaczenia: sprawdź uprawnienia odczytu i zapisu pliku i zwróć deskryptor pliku, który nie może być użyty do odczytu i do zapisu. Ten niestandardowy tryb dostępu jest wykorzystywany przez niektóre linuksowe sterowniki w celu zwrócenia deskryptora pliku, przeznaczonego tylko do typowo „sterownikowych” działań ioctl(2).
openat() oraz inne wywołania systemowe i funkcje biblioteczne, które przyjmują jako argument deskryptor pliku odnoszący się do katalogu (tj. execveat(2), faccessat(2), fanotify_mark(2), fchmodat(2), fchownat(2), fspick(2), fstatat(2), futimesat(2), linkat(2), mkdirat(2), mknodat(2), mount_setattr(2), move_mount(2), name_to_handle_at(2), open_tree(2), openat2(2), readlinkat(2), renameat(2), renameat2(2), statx(2), symlinkat(2), unlinkat(2), utimensat(2), mkfifoat(3) i scandirat(3)) rozwiązują dwa problemy starszych interfejsów, które je poprzedzały. Tu podano wyjaśnienie odnoszące się do wywołania openat(), jednak analogicznie można je zastosować do pozostałych interfejsów.
Przede wszystkim openat() pozwala uniknąć aplikacjom wystąpienia sytuacji wyścigu, która może zachodzić przy otwieraniu za pomocą open() plików w katalogach innych, niż bieżący katalog roboczy. Wyścig wynika z tego, że pewna składowa ścieżki podanej open() mogła ulec zmianie równolegle z wywołaniem open(). Proszę założyć na przykład, że próbujemy utworzyć plik kat1/kat2/xxx.zal, jeśli plik kat1/kat2/xxx istnieje. Jednak pomiędzy sprawdzeniem istnienia i krokiem utworzenia pliku, kat1 lub kat2 (które mogą być dowiązaniami symbolicznymi) mogą być zdefiniowane, aby wskazywać na inne położenia. Wyścigu można uniknąć, otwierając deskryptor pliku katalogu docelowego, a następnie podając ten deskryptor pliku jako argument dirfd do (przykładowo) fstatat(2) i openat(). Użycie deskryptora pliku dirfd ma także inne zalety:
Po drugie, openat() pozwala na implementację „bieżącego katalogu roboczego” przypisanego wątkowi, za pomocą deskryptora(-ów) pliku(-ów) zarządzanych przez aplikację (tę funkcjonalność można też uzyskać za pomocą sztuczek opartych na korzystaniu z /proc/self/fd/dirfd, lecz jest to mniej wydajne).
Argument dirfd do tych API można pozyskać za pomocą open() lub openat() do otworzenia katalogu (ze znacznikiem O_RDONLY albo O_PATH). Alternatywnie, taki deskryptor pliku można uzyskać stosując dirfd(3) do strumienia katalogu utworzonego za pomocą opendir(3).
W omawianych API, gdy poda się argument dirfd równy AT_FDCWD albo podana ścieżka jest bezwzględna, API obsługują swój argument ścieżki w taki sam sposób, jak odpowiadające im tradycyjne API. Jednak w tym przypadku, wiele z API posiada argument flags, pozwalający na dostęp do funkcjonalności, która nie jest dostępna w odpowiadającym im tradycyjnym API.
Znacznik O_DIRECT może nakładać ograniczenia (związane z wyrównaniem) na długości i adresie buforów definiowanych w przestrzeni użytkownika oraz przesunięciu pliku w wejściu/wyjściu. W Linuksie, ograniczenia związane z wyrównaniem różnią się w zależności od systemu plików i wersji jądra, mogą też wcale nie występować. Obsługa niewyrównanych wejść/wyjść O_DIRECT również jest zróżnicowana; mogą one albo zawieść z błędem EINVAL, albo awaryjnie skorzystać z buforowanego wejścia/wyjścia.
Od Linuksa 6.1, obsługa O_DIRECT i ograniczenia wyrównania związane z danym plikiem można sprawdzić za pomocą statx(2), wykorzystując znacznik STATX_DIOALIGN. Obsługa STATX_DIOALIGN różni się w zależności od systemu plików; więcej szczegółów w podręczniku statx(2).
Niektóre systemy plików zapewniają swoje interfejsy do odpytywania ograniczeń wyrównania O_DIRECT, przykładowo jest to operacja XFS_IOC_DIOINFO w xfsctl(3). Gdy jednak tylko jest dostępny, należy korzystać z STATX_DIOALIGN.
Jeśli żadne w powyższych nie jest dostępne, to obsługa bezpośredniego wejścia/wyjścia oraz ograniczenia związane z wyrównaniem można odgadnąć jedynie na podstawie znanej charakterystyki systemu plików, danego pliku, nośnika danych i wersji jądra. W Linuksie 2.4, większość systemu plików opartych na urządzeniach blokowych wymagało, aby długości i adresy pamięci wszystkich segmentów wejścia/wyjścia, były wielokrotnościami rozmiaru bloku systemu plików (zwykle 4096 bajtów). W Linuksie 2.6.0, to ograniczenie poluzowano do logicznego rozmiaru bloku (zwykle 512 bajtów). Rozmiar logicznego bloku urządzenia blokowego można sprawdzić za pomocą operacji BLKSSZGET ioctl(2) lub z powłoki, poleceniem:
blockdev --getss
Wejścia/wyjścia O_DIRECT nigdy nie należy uruchamiać równolegle z wywołaniem systemowym fork(2), jeśli bufor pamięci jest przypisaniem prywatnym (obejmuje to wszystkie przypisania utworzone za pomocą znacznika MAP_PRIVATE mmap(2); w tym pamięć przydzieloną do kopca oraz bufory przydzielone statycznie). Każde takie wejście/wyjście, niezależnie od tego, czy zostanie przesłane poprzez interfejs asynchronicznego wejścia/wyjścia, czy od innego wątku procesu, powinno być ukończone przed wywołaniem fork(2). Jeśli tak się nie stanie, może dojść do uszkodzenia danych oraz niezdefiniowanego zachowania w procesie macierzystym i potomnym. To ograniczenie nie ma zastosowania w przypadku, gdy bufory pamięci do wejścia/wyjścia O_DIRECT utworzono za pomocą shmat(2) lub mmap(2) ze znacznikiem MAP_SHARED. Nie ma zastosowania również wtedy, gdy w stosunku do bufora pamięci udzielono wskazówki MADV_DONTFORK za pomocą madvise(2), co zapewnia, że nie będzie on dostępny dla potomka po wykonaniu fork(2).
Znacznik O_DIRECT wprowadzono w SGI IRIX, gdzie ograniczenia związane z wyrównaniem były podobne do Linuksa 2.4. IRIX ma również wywołanie fcntl(2) do odpytywania o poprawne wyrównania i rozmiary. We FreeBSD 4.x wprowadzono znacznik o tej samej nazwie, ale nieposiadający ograniczeń wyrównania.
Obsługę O_DIRECT dodano w Linuksie 2.4.10. Starsze jądra Linux ignorują ten znacznik. Niektóre systemy plików mogą go nie implementować; wówczas zastosowanie znacznika spowoduje, że open() zawiedzie z błędem EINVAL.
Aplikacje powinny unikać mieszania O_DIRECT i zwykłego wejścia/wyjścia w tym samym pliku, szczególnie w nachodzących na siebie obszarach pliku. Nawet gdy system plików poprawnie obsługuje zagadnienia związane ze spójnością danych w tej sytuacji, sumaryczna przepustowość wejścia/wyjścia będzie prawdopodobnie gorsza, niż przy zdecydowaniu się na któryś z trybów. Aplikacje powinny również unikać mieszania korzystania z mmap(2) na plikach, przy używaniu bezpośredniego wejścia/wyjścia do tych samych plików.
Zachowanie O_DIRECT w systemie NFS różni się od lokalnych systemów plików. Starsze jądra oraz jądra skonfigurowane w pewien sposób mogą nie obsługiwać tego połączenia. Protokół NFS nie obsługuje przekazywania znacznika serwerowi, zatem wejście/wyjście O_DIRECT pominie buforowanie strony tylko po stronie klienta; serwer wciąż może buforować wejście/wyjście. Klient prosi serwer o uczynienie wejścia/wyjścia synchronicznym, aby zachować synchroniczne zachowanie O_DIRECT. Niektóre serwery nie będą się zachowywały wydajnie w takim przypadku, szczególnie jeśli rozmiar wejścia/wyjścia jest niewielki. Niektóre serwery mogą być również skonfigurowane w ten sposób, aby informować klientów nieprawidłowo (przedwcześnie) o osiągnięciu stabilnego nośnika przez wejście/wyjście; ta metoda unika uszczerbku wydajności kosztem pewnego ryzyka utraty spójności danych w przypadku awarii zasilania serwera. Linuksowy klient NFS nie narzuca ograniczeń związanych z wyrównaniem w przypadku wejścia/wyjścia O_DIRECT.
Podsumowując, O_DIRECT jest narzędziem o potencjalnie dużych możliwościach, którego należy używać ze sporą dawką ostrożności. Zaleca się, aby aplikacje korzystające z O_DIRECT, traktowały go jako, domyślnie wyłączoną, opcję poprawiającą wydajność.
Obecnie nie da się włączyć wejścia/wyjścia sterowanego sygnałem podając znacznik O_ASYNC przy wywołaniu open(); należy użyć fcntl(2), aby włączyć ten znacznik.
Przy próbie określenia, czy jądro obsługuje funkcję O_TMPFILE, należy sprawdzać dwa różne kody błędu: EISDIR i ENOENT.
Jeśli we flags poda się O_CREAT oraz O_DIRECTORY, a plik podany w ścieżce pathname nie istnieje, open() utworzy zwykły plik (tj. O_DIRECTORY zostanie zignorowany).
chmod(2), chown(2), close(2), dup(2), fcntl(2), link(2), lseek(2), mknod(2), mmap(2), mount(2), open_by_handle_at(2), openat2(2), read(2), socket(2), stat(2), umask(2), unlink(2), write(2), fopen(3), acl(5), fifo(7), inode(7), path_resolution(7), symlink(7)
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 |