WIRTUALNY SYSTEM PLIKÓW

Dotyczy wersji jądra: 2.4.7

Autor: Jan Domański
nr. indeksu: 171767




Idea VFS


Istnieje 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)
Idea VFS jest następująca: Mamy pośrednią warstwę w jądrze systemu ukrywającą przed użytkownikiem (a właściwie jego procesem) szczegóły dotyczące systemu plików z jakim pracuje. Udostępniony jest jedynie wspólny interfejs do różnych systemów plików (z punktu widzenia użytkownika nie ważne jest z jakim systemem plików pracuje - ma dostęp jedynie do funkcji z interfejsu). Dzięki VFS Linux umożliwia współpracę właściwie z dowolnym systemem plików, dowolnego systemu operacyjnego (nawet np: AMIGI). Możliwy jest dostęp do danych właściwie niezależnie od formatu ich przechowywania.
Rozwiązanie takie jest wzorowane na innych systemach Unixowych.



Ogólny opis VFS



Jak działa VFS?

Wirtualny 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.
Załóżmy, że jakiś proces chce uzyskać dostęp do pliku istniejącego w systemie i danych, które ten plik zawiera. Niżej przedstawię ogólny schemat obsługi takiego żądania:

  1. Proces wywołuje jedną ze standardowych funkcji systemowych (np: read(), write()).
  2. Żądanie trafia do VFS.
  3. VFS uruchamia funkcję odpowiadającą systemowi plików, w którym był zapisany dany plik.




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.


Rodzaje 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.
 

Rodzaj Przykłady Odpowiedzialny za: Korzysta z przestrzeni dyskowej
Dyskowe systemy plików
 
  • Ext2 (Second Extended Filesystem - oficjalny system plików Linuxa)
  • VFAT (Windows 98)
  • FFS (Amiga)
  • systemy plików odmian Unixa

 

 

zarządzanie przestrzenią dostępną na dyskach TAK (lokalnej)
Sieciowe systemy plików
  • NFS (Network File System)
  • SMB (MS Windows i IBM OS/2 Lan Manager
  • NCP

 
umożliwienie dostępu do plików znajdujących się na innych komputerach w sieci TAK (sieciowej)
Specjalne systemy plików /proc zależne od systemu plików, pełnią funkcje specjalne NIE

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.

Wspólny model pliku.

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.
Obsługą operacji związanych z systemami plików nie są przyporządkowane na stałe jakieś funkcje. Dla każdej operacji jest wskaźnik odsyłający do konkretnej funkcji dla konkretnego systemu plików.

Przykład: Wywołanie funkcji write(plik,bufor,4096).
Gdzie:
plik - otwarty plik należący do systemu plików MS-DOS.
bufor - dane do zapisu w pliku.
Kolejne etapy obsługi wywołania:

  1. Wywołanie write(...) powoduje wywołanie przez jądro sys_write(..)
  2. sys_write(...) znajduje wskaźnik do odpowiedniej funkcji przeznaczonej dla systemu plików MS-DOS
  3. sys_write(...) wywołuje odpowiednią funkcję
Wskaźnik do odpowiedniej funkcji znajduje się w polu f_op struktury danych file. (będzie o tym później)

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:
  • Odzwierciedla, tradycyjny system plików Unixa (w Linuksie zakłada się jak najefektywniejsze obsługiwanie własnego systemu plików).
  • Podobieństwo wspólnego modelu plików z modelem obiektowym (obiekt - programowa konstrukcja zawierająca zarówno dane jak i operacje na nich). Uwaga! VFS nie jest napisany w języku obiektowym np. C++ (wydajność).
  • Obiekty są realizowane jako struktury danych zawierające wskaźniki do funkcji, odpowiadające metodą w filozofii obiektowej.
  • Każdy katalog jest uważany za zwykły plik zawierający listę plików i innych katalogów.

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.



Obiekty superbloków

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:
include/linux/list.h

   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).



Obiekty pozycji katalogów

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).

Struktura dentry wraz z opisami pól:

 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:
  • "Wolny" - obiekt nie jest wykorzystywany przez VFS. Obszarem pamięci gdzie znajduje się obiekt zarządza alokator płytowy.
  • "Nie używany" - Licznik użycia d_count jest równy 0. Obiekt aktualnie nie jest używany przez jądro (ale d_inode dalej wskazuje na i-węzeł).
  • "Używany" - Licznik użycia d_count jest większy ostro od 0. Obiekt aktualnie jest używany przez jądro (d_inode wskazuje na odpowiedni i-węzeł).
  • "Ujemny" - obiekt odnosi się do nieistniejącego i-węzła (został skasowany z dysku). Pole d_inode jest ustawiane na NULL.


Obiekty i-węzłów

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).
  • Listy nieużywanych i-węzłów
    Lista jest dostępna przez zmienną inode_unused zdefiniowaną w pliku fs/inode.c (jest to "głowa" listy).
  • Listy używanych (aktualnie) i-węzłów
    Lista jest dostępna przez zmienną inode_in_use zdefiniowaną w pliku fs/inode.c (jest to "głowa" listy).
  • Listy "brudnych" i-węzłów ("głowa" listy jest w polu s_dirty odpowiedniego superbloku)
W wymienionych wyżej listach obiekty są połączone przy użyciu pól i_list odpowiednich obiektów i-węzłów.
"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.




Obiekty plików

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.
Uwaga! Obiekty plików nie mają swoich odpowiedników na dysku, są obecne jedynie w pamięci. Struktura file jest zdefiniowana następująco:

 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:
  • Lista używanych obiektów pliku.
    Z każdym używanym plikiem jest związany co najmniej jeden proces, pole f_count nie jest równe zero.
    Lista jest dostępna przez zmienną s_files będącą polem obiektu suberbloku związanego z systemem plików do których dane pliki należą.
  • Lista nie używanych obiektów pliku.
    Lista ta zawiera "rezerwowe" obiekty plików. Pole f_count dla tych obiektów jest puste. Jest wykorzystywana jako pamięć podręczna obiektów plików oraz jako rezerwa dla superużytkownika (pozwala na otwarcie pliku gdy wykorzystano całą pamięć dynamiczną w systemie).
    Lista ta zawiera zawsze przynajmniej NR_RESERVED_FILES elementów.
    Lista jest dostępna przez zmienną free_list zdefiniowaną w pliku fs/file_table.c (jest to "głowa" listy).

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:

  1. Zarejestrować dany system plików (po zarejestrowaniu funkcje systemu plików są dostępne dla jądra).
  2. Zamontować system plików w jakimś katalogu (po zamontowaniu można używać danego systemu plików).
Opiszę teraz ogólnie procesy rejestracji i montowania systemu plików.

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.