Idea VFSIstnieje bardzo duża różnorodność w świecie systemów komputerowych.
Współcześnie istnieje bardzo wiele odmian systemów operacyjnych. Każdy
z nich może, używać własnego, dla siebie charakterystycznego, systemu plików.
Ta różnorodność powoduje problemy z przenośnością danych między systemami.
Większość współczesnych systemów operacyjnych obsługuje jedynie swój system
plików i ew. kilka dodatkowych (np. starsze wersje aktualnie używanego,
jak FAT16 w Windows 98). W związku z tym np. w Windows 98, nie można
w łatwy sposób dostać się do danych zapisanych np. w systemie z Macintoshem.
Twórcy sytemu Linux chcąc uniknąć tego typu problemów wprowadzili
Wirtualny
System Plików (inna nazwa: Virtual Filesystem Switch) Ogólny opis VFSWirtualny system plików jest programową warstwą jądra systemu
LINUX.
Jest odpowiedzialny za obsługę wszystkich żądań dotyczących plików z jakiegoś
systemu plików obsługiwanego przez VFS.
![]() Warstwa Wirtualnego Systemu Plików.
VFS należy postrzegać jako warstwę pośrednią między procesami, a konkretnymi
systemami plików.
Z powyższego opisu wynika, że Wirtualny System Plików musi zawierać
informacje (wskaźniki)) o podstawowych funkcjach obsługujących każdy z dostępnych
przez niego systemów plików (patrz. Struktury danych i dostępne na nich operacje.).
Jak można się domyślać wewnętrzna struktura i implementacja każdego z systemu plików obsługiwanego
przez VFS może być właściwie dowolna. Dzięki temu można korzystać z bardzo
różnorodnych systemów plików.
Systemy plików dostępne przez VFS można podzielić na trzy podstawowe
grupy. Dalej zostanie przedstawiony ten podział wraz z przykładami.
Systemy plików dostępne standardowo w jądrze 2.4.7: minix, ext2, ext3, hpfs, ntfs, msdos, umsdos, iso, nfs, sysv, affs, ufs, efs, romfs, shmem, coda, smb, hfs, adfs, qnx4, reiserfs, bfs, udf, ncp, proc, socket, usbdev, xfs. Bardzo ważne, z punktu widzenia Wirtualnego systemu plików, jest pojęcie
wspólnego modelu pliku(common file model) Model ten umożliwia tożsame przedstawianie plików
z różnych systemów.
UWAGA!W pewnych sytuacjach VFS może wykonywać pewne operacje bez odwoływania się do funkcji niższego poziomu (np. close(...) nie wymaga zazwyczaj dostępu do pliku). Można traktować VFS jako "ogólny" system plików uruchamiający, jeśli potrzeba, funkcje dla konkretnych systemów. Przykładowe cechy Wspólnego modelu pliku w Linuksie:
Na wspólny model pliku składają się obiekty następujących typów: Opisy tych struktur zostaną podane w następnej sekcji. ![]() Na rysunku został przedstawiony ogólny schemat powiązań obiektów vfs. Struktury danych i dostępne na nich operacje.Każdy obiekt superbloku odnosi się do konkretnego zamontowanego systemu plików.
Obiekt superbloku składa się ze struktury super_block zdefiniowanej w pliku include/linux/fs.h.
Wszystkie obiekty są połączone w dwukierunkową listę cykliczną, której definicja znajduje się w pliku: struct list_head { struct list_head *next, *prev; };Tego typu listy są przez VFS używane bardzo często, w wielu miejscach. Struktura super_block wraz z opisami pól: struct super_block { /* Wskaźniki do następnego i poprzedniego obiektu superbloku */ struct list_head s_list; /* Identyfikator urządzenia */ kdev_t s_dev; /* Rozmiar bloku w bajtach */ unsigned long s_blocksize; /* Liczba bitów rozmiaru bloku */ unsigned char s_blocksize_bits; /* Flaga zmodyfikowania */ unsigned char s_dirt; unsigned char s_posix_acl_flag; /* Maksymalny rozmiar pliku */ unsigned long long s_maxbytes; /* Wskaźnik do obiektu opisującego typ systemu plików */ struct file_system_type *s_type; /* Wskaźnik do struktury zawierającej metody dla superbloku (struktura ta zawiera wskaźniki do funkcji) */ struct super_operations *s_op; /* Metody limitu dyskowego */ struct dquot_operations *dq_op; /* Flagi montowania */ unsigned long s_flags; /* "Magiczny" numer systemu plików */ unsigned long s_magic; /* Obiekt pozycji katalogu dla katalogu montowania */ struct dentry *s_root; struct rw_semaphore s_umount; /* Semafor blokady dostępu */ struct semaphore s_lock; /* Lista zmodyfikowanych i-węzłów, wymagających odświeżenia */ struct list_head s_dirty; /* Lista zablokowanych i-węzłów */ struct list_head s_locked_inodes; /* Lista otwartych obecnie plików. Jej elementami są obiekty plików. (patrz. Obiekty plików) */ struct list_head s_files; struct block_device *s_bdev; /* Wskaźniki do obiektów zamontowanych systemów plików */ struct list_head s_mounts; /* Specyficzne opcje limitu dyskowego */ struct quota_mount_options s_dquot; /* Unia zawierająca specyficzne informacje dla systemu plików */ union u; }Dalej zostanie przedstawiony bardziej szczegółowy opis dwóch pól, a mianowicie pola s_op oraz u. Pole s_op Pole to jest wskaźnikiem do struktury: super_operations zawierającej wskaźniki do metod (funkcji)) związanych z superblokiem. System plików może zdefiniować własne metody superbloku. W przypadku gdy dana metoda nie ma zastosowania dla konkretnego systemu plików wskaźnik do niej jest ustawiany na NULL. Przykład Niech superB będzie obiektem superbloku. Gdy VFS chce wykonać jakąś operację superbloku wywołuje po prostu: superB->s_op->nazwa_funkcji(...) Przykłady dostępnych operacji dla superbloku zawartych w strukturze super_operations (nie podaję wszystkich możliwych funkcji) void (*read_inode) (struct inode *); Wypełnia pola obiektu i-węzła, którego adres jest przekazany jako parametr, danymi z dysku. Pole i_ino obiektu i-węzła identyfikuje konktetny i-węzeł na dysku. void (*read_inode2) (struct inode *, void *); To samo co wyżej, dla jednego z systemów plików: reiserfs (rozwiązanie chyba tymczasowe, sprzeczne z filozofią VFS). void (*write_inode) (struct inode *, int); Uaktualnia i-węzeł systemu plików, którego obiekt został podany jako parametr. Pole i_ino obiektu i-węzła identyfikuje konktetny i-węzeł na dysku. void (*put_inode) (struct inode *); Zwalnia obiekt i-węzła, którego adres jest podany w parametrze. Pamięć nie musi być zwolniona. void (*delete_inode) (struct inode *); Usuwa bloki danych zawierające plik, i-węzeł na dysku oraz i-węzeł VFS void (*put_super) (struct super_block *); Używane przy demontowaniu systemu plików. Uwalnia obiekt superbloku przekazywanego jako parametr. void (*write_super) (struct super_block *); Aktualizuje superblok systemu plików za pomocą zawartości parametru. void (*clear_inode) (struct inode *); Zwalnia obiekt i-węzła, którego adres jest podany w parametrze.Pamięć zostaje zwolniona. void (*umount_begin) (struct super_block *); Przerywa operację montowania (zaczęła się operacja demontowania). Pole u Jest to pole unii, które zawiera informacje dotyczące konkretnego systemu plików. W polu tym mogą być przechowywane np. struktury: struct ext2_sb_info ext2_sb; struct ext3_sb_info ext3_sb; struct ntfs_sb_info ntfs_sb; Jeśli obiekt superbloku odnosi się do konkretnego systemu plików to struktura taka zawiera dane dotyczące tego systemu, niezależnie od ogólnego modelu VFS. (np. jeśli obiekt superbloku odnosi się do systemu plików Ext3 to w u jest przechowywana struktura ext3_sb_info). Można powiedzieć, że obiekt pozycji katalogu wiąże (logicznie, gdy jakiś proces odniesie się do danego pliku)
konkretny plik (mający nazwę, należący do jakiejś ścieżki)
z systemu plików z odpowiednim i-węzłem (obiektem odpowiadającym faktycznemu miejscu na np. dysku).
Obiekty pozycji katalogu odpowiadają każdemu składnikowi ścieżki, przeszukiwanej przez proces.
W szczególności katalogi są traktowane przez Wirtualny system plików jak zwykłe pliki
(patrz: Wspólny model pliku.). Jeśli np. proces przeszukuje ścieżkę
/root/cos to jądro stworzy trzy połączone obiekty pozycji katalogów (dla /,dla root
w katalogu /, dla cos w katalogu /root/).
Obiekty pozycji katalogu opierają się na strukturze dentry zdefiniowanej w pliku
include/linux/dcache.h.
Obiekty pozycji katalogu nie mają odpowiadających im obiektów na dysku (choć oczywiście mogą być powiązane
z jakimś i-węzłem), istnieją tylko w pamięci (dokładniej w pamięci podręcznej alokatora płytowego dentry_cache).
struct dentry { /* Licznik użycia obiektu */ atomic_t d_count; /* Flagi obiektu */ unsigned int d_flags; /* Wskaźnik do i-węzła związanego z obiektem (nazwą pliku) */ struct inode * d_inode; /* Wskaźnik do obiektu pozycji katalogu rodzica */ struct dentry * d_parent; /* Wskaźniki do miejsca w liście przemieszania */ struct list_head d_hash; /* Wskaźniki do miejsca w liście nie używanych obiektów, jeśli d_count=0 */ struct list_head d_lru; /* Wskaźniki do listy obiektów pozycji katalogu w katalogu nadrzędnym */ struct list_head d_child; /* Jeśli obiekt pozycji katalogu jest katalogiem to pole to zawiera wskaźniki do listy obiektów pozycji podkatalogów */ struct list_head d_subdirs; /* Wskaźniki do listy związanych i-węzłów (aliasy) */ struct list_head d_alias; int d_mounted; /* Nazwa pliku */ struct qstr d_name; /* Zmienna pomocnicza, używana przez metodę d_revalidate */ unsigned long d_time; /* Wskaźnik dla struktury opisującej metody dla obiektu pozycji katalogu */ struct dentry_operations *d_op; /* Wskaźnik dla obiektu superbloku dla pliku */ struct super_block * d_sb; /* Dodatkowe flagi dotyczące VFS */ unsigned long d_vfs_flags; /* Dane zależne od systemu plików */ void * d_fsdata; /* Miejsce dla krótkiej nazwy */ unsigned char d_iname[DNAME_INLINE_LEN]; }; Istnieją cztery stany w jakich mogą znajdować się obiekty pozycji katalogu:
Niezależnie od używanego systemu plików każdy plik ma jednoznacznie przyporządkowany tzw. i-węzeł.
I-węzeł zawiera wszystkie potrzebne informacje na temat pliku. Pozostaje niezmieniony tak
długo jak długo plik istnieje. Jest on reprezentowany jako obiekt będący strukturą typu
inode. Jest ona zdefiniowana w pliku include/linux/fs.h. struct inode { /* Wskaźniki do miejsca na liście przemieszania (patrz. dalej)*/ struct list_head i_hash; /* Wskaźniki do, jednej z trzech (patrz. dalej), list i-węzłów (do poprzedniego i następnego obiektu na danej liście) */ struct list_head i_list; /* Wskaźniki do miejsca na liscie pozycji katalogu */ struct list_head i_dentry; /* Wskaźniki do listy zmodyfikowanych buforów */ struct list_head i_dirty_buffers; /* Jednoznaczny numer i-węzła */ unsigned long i_ino; /* Licznik użycia i-węzła */ atomic_t i_count; /* Identyfikator urządzenia */ kdev_t i_dev; /* Typ pliku wraz z prawami dostępu */ umode_t i_mode; /* Liczba sztywnych dowiązań */ nlink_t i_nlink; /* Identyfikator właściciela i-węzła */ uid_t i_uid; /* Identyfikator grupy */ gid_t i_gid; /* Identyfikator prawdziwego urządzenia */ kdev_t i_rdev; /* Długość pliku w bajtach (liczba 64-bitowa)*/ loff_t i_size; /* Czas ostatniego dostępu */ time_t i_atime; /* Czas ostatniego zapisu */ time_t i_mtime; /* Czas ostatniej zmiany i-węzła */ time_t i_ctime; /* Rozmiar bloku w bajtach */ unsigned long i_blksize; /* Liczba bloków pliku */ unsigned long i_blocks; /* Numer wersji (jest zwiększany po każdym użyciu) */ unsigned long i_version; /* Semafory dostępu i-węzła */ struct semaphore i_sem; struct rw_semaphore i_truncate_sem; struct semaphore i_zombie; /* Wskaźnik do struktury zawierającej metody związane z obiektem i-węzła (struktura ta zawiera wskaźniki do funkcji) */ struct inode_operations *i_op; /* Wskaźnik do struktury zawierającej metody związane z plikiem. Dawniej ->i_op->default_file_ops */ struct file_operations *i_fop; /* Wskaźnik do odpowiedniego obiektu superbloku */ struct super_block *i_sb; /* Kolejka oczekiwania i-węzła */ wait_queue_head_t i_wait; /* Wskaźnik do listy blokad pliku */ struct file_lock *i_flock; /* Wskaźnik do struktury dotyczącej regionów pamięci odwzorowujących plik */ struct address_space *i_mapping; /* Struktura dotycząca regionu pamięci z danymi */ struct address_space i_data; /* Limity dyskowe i-węzła */ struct dquot *i_dquot[MAXQUOTAS]; /* Wskaźnik do informacji o potoku, jeśli plik jest potokiem */ struct pipe_inode_info *i_pipe; /* Wskaźnik do struktury opisu urządzenia blokowego, jeśli plik nim jest */ struct block_device *i_bdev; /* Wskaźnik do struktury opisu urządzenia znakowego, jeśli plik nim jest */ struct char_device *i_cdev; unsigned long i_dnotify_mask; struct dnotify_struct *i_dnotify; /* Flaga statusu i-węzła Przykłady możliwych wartości: I-DIRTY - węzeł jest "brudny I-LOCK - węzeł jest zablokowany I-FREEING - obiekt i-węzła jest zwalniany */ unsigned long i_state; /* Flaga montowania systemu plików */ unsigned int i_flags; /* Prawdziwe jeśli plik jest gniazdem */ unsigned char i_sock; /* Licznik dla procesu zapisującego */ atomic_t i_writecount; /* Flagi utworzenia pliku */ unsigned int i_attr_flags; /* Zarezerwowane */ __u32 i_generation; /* Informacje specyficzne dla danego systemu plików */ union u; }; Każdy obiekt i-node należy do jednej z trzech list (cykliczne, dwukierunkowe).
"Brudny" i-węzeł to taki, który trzeba uaktualnić na dysku. Aby przyśpieszyć wyszukiwanie obiektów i-węzłów "brudnych" i używanych znajdują się one dodatkowo w tablicy przemieszania inode_hashtable. Podobnie ja w przypadku obiektów superbloków pole u jest polem unii, które zawiera informacje dotyczące konkretnego systemu plików. W polu tym mogą być przechowywane np. struktury: struct ext2_inode_info ext2_i; struct ext3_inode_info ext3_i; struct ntfs_inode_info ntfs_i; Pole i_op dla obiektu i-węzła pełni podobną funkcję jak pole s_op obiektów superbloków. Jest ono wskaźnikiem do struktury: inode_operations zawierającej wskaźniki do metod (funkcji)) związanych z obiektem i-węzła. W przypadku gdy dana metoda nie ma zastosowania dla konkretnego systemu plików oraz i-węzła wskaźnik do niej jest ustawiany na NULL. Pole i_fop jest wskaźnikiem do struktury: file_operations zawierającej wskaźniki do metod (funkcji)) związanych z obiektem pliku (patrz Obiekty plików). W poprzednich wersjach jądra rolę tego pola stanowiło pole default_file_ops struktury inode_operations. Przykłady dostępnych operacji dla i-węzłów zawartych w strukturze inode_operations (nie podaję wszystkich możliwych funkcji) int (*create) (struct inode * ind,struct dentry * dnt,int); Tworzy nowy i-węzeł dla zwykłego pliku związanego z pozycją katalogu dnt. struct dentry * (*lookup) (struct inode * ind,struct dentry * dnt); Przeszukuje katalog związany z indw celu znalezienia i-węzła związanego z nazwą pliku z dnt int (*link) (struct dentry *old,struct inode *ind,struct dentry *new); Tworzy nowe sztywne dowiązanie odnoszące się do pliku określonego przez old w katalogu odnoszącym się do ind;Nowe sztywne dowiązanie ma nazwę określoną przez new. int (*unlink) (struct inode *ind,struct dentry *dnt); Usuwa sztywne dowiązanie. int (*mkdir) (struct inode *ind,struct dentry *dnt,int); Tworzy nowy i-węzeł dla katalogu związanego z obiektem pozycji katalogu dnt int (*rmdir) (struct inode *,struct dentry *dnt); Usuwa podkatalog (związany dnt) z katalogu. int (*mknod) (struct inode *ind ,struct dentry *dnt,int mode,int rdev); Tworzy nowy i-węzeł dla pliku specjalnego związanego z obiektem dnt. (mode - typ pliku, rdev - główny numer urządzenia). int (*rename) (struct inode *olddir, struct dentry *olddentry,struct inode *newdir, struct dentry *newdentry); Przenosi plik określony przez olddentry (z katalogu olddir) do katalogu określonego przez newdir; Nowa nazwa pliku jest w obiekcie wskazywanym przez newdentry (*setattr) (struct dentry * dnt, struct iattr *); Ustala nowe atrybuty dla pliku związanego z obiektem dnt.
Obiekt pliku jest tworzony przy otwieraniu pliku. Opisuje on współdziałanie jakiegoś procesu z otwartym plikiem.
Składa się on ze struktury file. Jest ona zdefiniowana w pliku include/linux/fs.h. struct file { /* Wskaźnik do następnego i poprzedniego obiektu pliku w jednej z dwóch list plików (patrz. dalej) */ struct list_head f_list; /* Wskaźnik do struktury dentry odpowiadającej pozycji pliku w katalogu */ struct dentry *f_dentry; /* Wskaźnik do struktury zawierającej informacje dotyczące zamontowanego systemu plików, do którego dany plik należy */ struct vfsmount *f_vfsmnt; /* Wskaźnik do struktury zawierającej metody związane z plikiem. (struktura ta zawiera wskaźniki do funkcji) */ struct file_operations *f_op; /* Licznik użycia obiektu pliku */ atomic_t f_count; /* Flagi określone przy otwieraniu pliku */ unsigned int f_flags; /* Tryb dostępu do pliku przez proces */ mode_t f_mode; /* Zmienna określająca aktualną pozycję w pliku (od niej zaczynają się dalsze operacje na pliku). Ponieważ plik może być używany przez wiele procesów nie może być trzymana w i-węźle. */ loff_t f_pos; /* Zmienne dotyczące obsługi czytania z wyprzedzeniem: f_reada - flaga odczytu (z wyprzedzeniem), f_ramax - max. liczba czytanych stron f_raend - wsk. pliku po ostatnim czytaniu, f_ralen - liczba przeczytanych bajtów, f_rawin - liczba przeczytanych stron */ unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin; /* Dane dla asynchronicznych operacji WE-WY za pomocą sygnałów */ struct fown_struct f_owner; /* UID i GID użytkownika pliku */ unsigned int f_uid, f_gid; /* Dla sieciowych operacji zapisu - Kod błędu */ int f_error; /* Numer wersji (jest zwiększany po każdym użyciu) */ unsigned long f_version; /* Wymagane przez sterowniki terminala tty i może coś jeszcze */ void *private_data; }; Każdy obiekt pliku znajduje się na jednej z dwóch list (cyklicznych, dwukierunkowych). Niezależnie od tego, do której listy plików obiekty plików należą, pole i_list zawiera wskaźnik do następnego i poprzedniego obiektu pliku w danej liście. Te listy to:
Idea wspólnego modelu plików zakłada, że każdy system plików udostępnia własny zestaw operacji na pliku. Na zestaw możliwych operacji na pliku wskazuje zmienna f_op. Jest to wskaźnik do struktury file_operations zawierającej wskaźniki do właściwych funkcji obsługi pliku. Standardowo (przy otwieraniu pliku), zestaw tych operacji jest inicjowany zestawem, na który wskazuje i_fop danego obiektu i-węzła. W razie konieczności VFS może modyfikować zestaw tych operacji poprzez zmianę f_op. Jeśli dla konkretnego systemu plików jakaś funkcja nie ma zastosowania wskaźnik do niej jest ustawiany na NULL. Przykłady dostępnych operacji dla pliku zawartych w strukturze flie_operations (nie podaję wszystkich możliwych funkcji) loff_t (*llseek) (struct file *f, loff_t, int); Służy do uaktualnienia wskaźnika pliku f (pola f_pos w obiekcie pliku). ssize_t (*read) (struct file *f, char *, size_t count, loff_t * offset); Służy do czytania count bajtów z pliku f zaczynając od pozycji offset. ssize_t (*write) (struct file *f, const char *, size_t count, loff_t *offset); Zapisuje count bajtów do pliku f zaczynając od pozycji offset. int (*readdir) (struct file *f, void *d, filldir_t); Zwraca następną pozycję w katalogu d. int (*open) (struct inode *, struct file *f); Otwiera plik. Tworzy nowy obiekt pliku f i łączy go z odpowiednim obiektem i-węzła. int (*flush) (struct file *f); Przeznaczenie funkcji zależy od systemu plików. Wywoływana jest przy zwalnianiu odniesienia do pliku f (zmniejszenie pola f_count obiektu pliku f). int (*release) (struct inode *, struct file *f); Wywoływana jest gdy nie ma już odniesień do pliku f. Zwalnia obiekt pliku. int (*lock) (struct file *f, int, struct file_lock *); Blokuje plik określony przez obiekt f. Opis rejestracji i montowania systemu plików.Aby w systemie Linux móc używać jakiegoś systemu plików (oczywiście pośrednio przez VFS) trzeba najpierw przeprowadzić dwie operacje:
Rejestracja Aby zarejestrować system plików używa się funkcji int register_filesystem(struct file_system_type * fs) (definicja jej jest w fs/super.c). Parametrem tej funkcji jest wskaźnik do struktury file_system_type opisującej dany system plików (definicja jej jest w include/linux/fs.h). Struktura file_system_type wraz z opisem pól: struct file_system_type { /* Nazwa systemu plików */ const char *name; /* Flagi montowania */ int fs_flags; /* Wskaźnik do metody odczytującej superblok, używanej przy montowaniu systemu plików. */ struct super_block *(*read_super) (struct super_block *, void *, int); struct module *owner; struct vfsmount *kern_mnt; /* Wskaźnik do następnego elementu listy. Obiekty file_system_type są połączone w prostą listę. */ struct file_system_type * next; }; Standardowo, najważniejsze systemy plików są rozpoznawane w trakcie kompilacji jądra. W czasie inicjacji systemu, systemy plików podane przy kompilacji są rejestrowane przy użyciu funkcji filesystem_setup. Funkcja ta dla każdego takiego systemu plików wywołuje register_filesystem(...) z odpowiednim parametrem. Dany system plików można wyrejestrować używając unregister_filesystem(..). Montowanie Nie zostanie tutaj przedstawiony szczegółowy opis montowania systemu plików gdyż nie jest to ściśle związane z głównym tematem (VFS). Opisana zostanie tutaj jedynie idea. Można rozróżnić dwa rodzaje montowania: Montowanie głównego systemu plików i Montowanie innych systemów plików. Główny system plików jest montowany przy inicjalizacji systemu. Zaraz po wywołaniu filesystem_setup jest wywoływana funkcja mount_root. Funkcja ta używa min. metody read_super z obiektu typu file_system_type odpowiadającemu systemowi plików znajdującym się na głównym urządzeniu. Metoda ta wypełnia odpowiedni obiekt superbloku. Po zamontowaniu głównego systemu plików można zamontować inne. Mogą one mieć różne punkty montowania będące podkatalogami głównego katalogu, głównego systemu plików. Do montowania służy funkcja mount, zawierająca podprogram obsługi sys_mount. Funkcja ta również korzysta z metody read_super z obiektu typu file_system_type odpowiadającego montowanemu systemowi plików. Do odmontowywania systemu plików służy funkcja umount. |