# El sistema de ficheros virtual (VFS) de Linux

Autor: Juan Antonio Martínez Castaño
E-mail: jantonio@dit.upm.es
Web: http://www.dit.upm.es/~jantonio

### Copyright

Este artículo es Copyright 1999 de Juan Antonio Martínez Castaño y se distribuye bajo las siguientes condiciones:

- Su distribución mediante medios electrónicos es libre, siempre y cuando se conserve el texto íntegro y en su formato HTML original, haciendo especial mención a la conservación del mensaje de copyright
- El autor y dueño del copyright cede los derechos de publicación impresa a Prensa Técnica S.L., autorizando a ésta a realizar las modificaciones al texto que considere oportunas para su publicación
- La distribución o copia, total o parcial, en cualquier medio impreso por parte ajena a Prensa Técnica S.L. Está expresamente prohibida
- Ningún particular podrá poner este fichero en un servidor Web de acceso público sin el consentimiento expreso del autor y de Prensa Técnica S.L.

### Indice

- Entradilla
- Introducción
- Estructura del VFS
- Registro y montaje de sistemas de ficheros
- El Super-Bloque
- Los inodos
- Resumen
- Referencias

### Entradilla

Iniciamos aquí una nueva serie de artículos sobre uno de los componentes esenciales del núcleo Linux, y una de las razones de su potencia y versatilidad: la estructura y funcionamiento del sistema de ficheros, conocido como Virtual File System ( VFS )

### Introducción

Los lectores de Linux Actual conocen sobradamente conceptos como "montar" y "desmontar" sistemas de ficheros, sistemas de ficheros "tipo XXX": ext2, vfat iso9660, nfs...., dispositivos, particiones...

El VFS esconde las peculiaridades de cada sistema de ficheros y unifica el manejo a través de un API común

A pesar de ello pocas veces nos ponemos a pensar cómo es posible unificar conceptos tan dispares como una partición de MS-Dos con un directorio de un servidor de red, de manera que podemos utilizar los mismos comandos, y de la misma forma en estructuras tan distintas.

La razón de esta aparente "magia" reside en un conjunto de estructuras que el núcleo Linux mantiene, y que sirve para unificar de cara al usuario todos y cada uno de los posibles tipos de estructuras de almacenamiento de datos. Esta capa intermedia se denomina Sistema de ficheros virtual, aunque todos lo conocemos por sus siglas en inglés: VFS , o Virtual File System

Esta serie de artículos trata sobre el VFS de Linux. Describiremos las estructuras que lo componen y su funcionamiento, explicando los conceptos de superbloque, inodo, fichero... analizando las operaciones de registro de un sistema de ficheros, el proceso de montaje y desmontaje, y las operaciones conocidas de open, close, read... etc
Para ilustrar el texto, recurriremos con frecuencia al código fuente del núcleo Linux. Salvo anotación expresa, los ejemplos están extraídos del código correspondiente a la versión 2.2.5 del núcleo Linux. Del mismo modo, y para ilustrar al lector sobre cómo desarrollar sus propios sistemas de ficheros ( no es difícil, una vez se sabe cómo :-) ) hemos incluído en el CD-Rom que acompaña a la revista dos ejemplos: el primero es un sistema de ficheros tipo tarfs que permite "montar" y examinar directamente desde el árbol de directorios ficheros tar. El segundo corresponde al esqueleto de un sistema de ficheros cachefs que hace caché bajo demanda del contenido de otro sistema de ficheros sobre el disco local. Este último ejemplo no es funcional, pero sirve para ilustrar las operaciones de registro y montaje de un sistema de ficheros, y su interacción con el /proc filesystem

En un número anterior de Linux Actual se describió la estructura del sistema de ficheros Ext2fs. En este mismo número se describe el funcionamiento y estructura del /proc filesystem

La serie de artículos sobre el Virtual File System se divide en tres entregas. Este artículo describe los elementos componentes del VFS de Linux, describiendo las estructuras y su interrelación. El siguiente número de la serie describe los diversos procesos y funcionamiento del VFS, y constituye una guía para el programador de sistemas de ficheros. El tercer y último artículo profundiza en el concepto del manejo del caché de Linux y de cómo el núcleo optimiza la gestión de recursos en los accesos a los diferentes sistemas de ficheros.

Cojamos pues los fuentes de los programas descritos, y tengamos a mano los fuentes del núcleo Linux, especialmente el directorio /usr/src/linux/fs, y empecemos a navegar por el VFS....

### Estructura del VFS

Todos sabemos que Linux soporta multitud de tipos de sistemas de ficheros, cada uno de ellos sobre diversos soportes físicos. La figura 1 muestra algunos de ellos. Además Linux permite programar e insertar dentro del núcleo muchos otros más, incluso los programados por el usuario. Todo ello exige un API unificado y un modelo de programación

Figura 1: Algunos tipos de sistemas de ficheros de Linux

¿ Cómo se lleva a cabo este soporte?. Una primera aproximación la encontramos en la figura 2, donde se describe el diagrama de bloques de las diferentes estructuras relacionadas con el manejo de ficheros. Podemos ver que:

- Todas las rutinas que se refieren al manejo de ficheros pasan a través de la capa del VFS ( VFS Layer
- Existen una serie de mecanismos de caché en el sistema:
  - Un caché de inodos que guarda las referencias a las entradas del VFS más recientes
  - Un caché de directorios para acelerar la búsqueda y mapeado de nombres de ficheros a sus correspondientes inodos
  - Un caché de disco que guarda los últimos sectores de disco accedidos, y permite las operaciones de "read ahead". ( aquí, el concepto de disco debe entenderse como dispositivo genérico de almacenamiento, no como dispositivo físico )
  El VFS caché será explicado con detalle en la última entrega de esta serie.
- ¿Cómo se consigue un API transparente en el VFS?. Los programadores de Java y C++ entenderán perfectamente el proceso si piensan en el VFS como una clase, y en un sistema de ficheros específico como en una clase derivada del VFS. En efecto, el VFS proporciona métodos genéricos para las diversas operaciones que se pueden realizar con inodos y ficheros, funciones que luego son personalizadas para cada sistema en particular. En lenguaje "C", esto se traduce en estructuras formadas por arrays de punteros a funciones. Lo veremos en un momento al describir las estructuras
- La filosofía de funcionamiento es:
  1. Se produce una llamada al sistema que implica un acceso a un sistema de ficheros.
  2. El VFS determina a qué sistema de ficheros pertenece
  3. Se comprueba si existe ya una entrada en el caché y si dicha entrada es válida
  4. En el caso de que se requiera, se accede a la función del filesystem específico que realiza dicha función a través de las estructuras del VFS
  5. La función puede, o bien completar la llamada, o bien requerir una operación de E/S sobre un dispositivo, que en su caso puede provocar el paso a modo "sleep" del proceso que invocó la operacion sobre el filesystem , hasta que la operación se complete

- El VFS proporciona también mecanismos de bloqueo y control de accesos. No obstante estas operaciones son fuertememte dependientes de cada sistema de ficheros en particular, por lo que no las describiremos en detalle

  Figura 2: Diagrama de bloques del sistema de ficheros de Linux

  ### Registro y montaje de un sistema de ficheros

  En todo momento, el usuario puede consultar el fichero /proc/filesystems para obtener la lista de sistemas de ficheros registrados, y que el VFS comprende. La operación de registro es aquella por la que se informa al VFS de que se ha instalado un nuevo manejador de sistemas de ficheros. Esta operación se realiza bien en el arranque del núcleo, o bien en la carga de un módulo. Evidentemente existe la operación inversa al registro, para decir que un sistema de ficheros ya no es reconocido por el VFS. las llamadas a las funciones son:

      extern int register_filesystem(struct file_system_type *);
      extern int unregister_filesystem(struct file_system_type *);

  A nivel de código, los diversos sistemas de ficheros se agrupan como una lista encadenada de estructuras que definen cada sistema de ficheros. La figura 3 ilustra esta lista:

  Figura 3: Organización de la lista de sistemas de ficheros

  El lector podrá observar que la estructura de un descriptor de sistemas de ficheros es simple. No se requiere más que

  - El nombre del sistema de ficheros
  - Un indicador de si accede o no a dispositivos físicos -entradas del /dev- ( por ejemplo, el /proc filesystem no requiere de dispositivos especiales )
  - Un método que permita la lectura del superbloque ( el concepto de superbloque en un VFS lo definiremos más adelante )

  Un registro sin más nos vale de poco: la esencia de los sistemas de ficheros es que se pueden montar dentro del árbol de directorios. La operación de montaje se puede definir como la creación de una terna dispositivo - sistema de ficheros - punto de anclaje. La figura 4 despliega las estructuras que intervienen en la operación de "mount". Básicamente son:

  - una lista encadenada de puntos de montaje, accesible desde el fichero /proc/mounts
  - Un descriptor del superbloque
  - Un puntero al descriptor del sistema de ficheros asociado
  - Un puntero al inodo sobre el que se monta el dispositivo

  Figura 4: estructura de una entrada en la tabla de montajes

  ### El super-bloque. Estructura y operaciones

  En todo momento hemos estado hablando del concepto de superbloque . Es hora ya de definirlo y explicar sus funciones

  Por superbloque entendemos la estructura de datos que define el contenido de un sistema de ficheros. El listado 1 muestra la definición de un VFS superblock. En él se incluye información sobre:

  - Tamaño del bloque de datos
  - Punteros a estructuras de manejadores
  - Punteros al inodo raíz de dicho sistema de ficheros
  - Punteros al registro del sistema de ficheros
  - Información especifica dependiente del sistema de ficheros: lista de bloques libres, modos de acceso, etc

      struct super_block {
      	struct list_head	s_list;		/* Keep this first */
      	kdev_t			s_dev;
      	unsigned long		s_blocksize;
      	unsigned char		s_blocksize_bits;
      	unsigned char		s_lock;
      	unsigned char		s_rd_only;
      	unsigned char		s_dirt;
      	struct file_system_type	*s_type;
      	struct super_operations	*s_op;
      	struct dquot_operations	*dq_op;
      	unsigned long		s_flags;
      	unsigned long		s_magic;
      	unsigned long		s_time;
      	struct dentry		*s_root;
      	struct wait_queue	*s_wait;

      	struct inode		*s_ibasket;
      	short int		s_ibasket_count;
      	short int		s_ibasket_max;
      	struct list_head	s_dirty;	/* dirty inodes */

      	union {
      		struct minix_sb_info	minix_sb;
      		struct ext2_sb_info	ext2_sb;
      		struct hpfs_sb_info	hpfs_sb;
      		struct ntfs_sb_info     ntfs_sb;
      		struct msdos_sb_info	msdos_sb;
      		struct isofs_sb_info	isofs_sb;
      		struct nfs_sb_info	nfs_sb;
      		struct sysv_sb_info	sysv_sb;
      		struct affs_sb_info	affs_sb;
      		struct ufs_sb_info	ufs_sb;
      		struct romfs_sb_info	romfs_sb;
      		struct smb_sb_info	smbfs_sb;
      		struct hfs_sb_info	hfs_sb;
      		struct adfs_sb_info	adfs_sb;
      		struct qnx4_sb_info	qnx4_sb;
      		void			*generic_sbp;
      	} u;
      };
  Listado 1: Estructura del VFS superblock

  La operación de mount crea en el VFS una nueva estructura tipo superbloque. El VFS llama a la rutina read_super() definida en la descripción del sistema de ficheros y le pasa como parámetro el VFS superblock recien creado. read_super() completa los datos de esta estructura, especialmente los referidos a los punteros a las operaciones a realizar con dicho sistema de ficheros ( campo *s_op de la estructura ) y retorna de nuevo el superbloque con los datos completos. En lo sucesivo, el VFS cada vez que requiera de alguna operación con este sistema de ficheros recien montado, no tiene sino que seguir los enlaces...

  La mejor manera de seguir este proceso es la lectura del código fuente de algún sistema de ficheros sencillo. El lector puede estudiar los ejemplos que se incluyen en el CD-Rom, o bien directamente en el kernel estudiar -por ejemplo- el código del Romfs

  ### Los inodos. Manejo de inodos

  Toda entrada en un sistema de ficheros tiene asociado un descriptor, que denominamos inodo

  Otro concepto que hemos estado manejando hasta ahora y que no hemos definido es el de inodo. Lo podemos definir como un descriptor de una entrada de un sistema de ficheros. Existe la tendencia de asociar el concepto de inodo con el de fichero dado que "en UNIX todos los dispositivos son ficheros" y "todo fichero tiene asociado un único inodo". Esto no es exactamente así por:

  - Si bien es cierto que todos los ficheros tienen asociado un inodo, el recíproco no es cierto: los dispositivos especiales, sockets, y enlaces simbólicos que ciertamente son pertenecientes a un sistema de ficheros, no tienen ficheros asociados
  - Existen inodos especiales que no se reflejan en el sistema de ficheros: el comando badblocks crea un fichero invisible ( aunque accesible por el sistema ) que contiene los sectores erróneos de un disco
  - Para evitar problemas definiremos fichero como una secuencia de bloques en un sistema de ficheros, direccionada por un inodo. Podríamos decir en lenguaje C, que el inodo es el puntero y el fichero es el contenido... ( aunque existen multitud de punteros Null :) ... )
  - Recordamos al lector que un directorio no es sino un fichero que contiene asociaciones nombre-inodo
  - Existen operaciones específicas sobre inodos y operaciones sobre ficheros

  El listado 2 describe la estructura de un VFS inode. Los comentarios describen los campos más importantes

      struct inode {
      	struct list_head	i_hash;
      	struct list_head	i_list;
      	struct list_head	i_dentry; /*datos utilizados por el VFS cache*/

      	unsigned long		i_ino;	  /* inode number */
      	unsigned int		i_count;
      	kdev_t			i_dev;	  /* dispositivo asociado */
      	umode_t			i_mode;	  /* flags */
      	nlink_t			i_nlink;  /* número de enlaces */
      	uid_t			i_uid;	  /* user id del propietario */
      	gid_t			i_gid;	  /* group id del propietario */
      	kdev_t			i_rdev;   /* si special file, major y minor */
      	off_t			i_size;	  /* tamaño del fichero asociado */
      	time_t			i_atime;  /* fecha de ultimo acceso */
      	time_t			i_mtime;  /* fecha de ultima modificacion */
      	time_t			i_ctime;  /* fecha de creacion */
      	unsigned long		i_blksize;/* tamaño del bloque */
      	unsigned long		i_blocks; /* numero de bloques */
      	unsigned long		i_version;
      	unsigned long		i_nrpages;
      	struct semaphore	i_sem;
      	struct semaphore	i_atomic_write;
      	struct inode_operations	*i_op;	/* operaciones sobre el inodo */
      	struct super_block	*i_sb;  /* puntero al superbloque */
      	struct wait_queue	*i_wait;
      	struct file_lock	*i_flock;
      	struct vm_area_struct	*i_mmap;
      	struct page		*i_pages;
      	struct dquot		*i_dquot[MAXQUOTAS];

      	unsigned long		i_state;

      	unsigned int		i_flags;
      	unsigned char		i_pipe;
      	unsigned char		i_sock;

      	int			i_writecount;
      	unsigned int		i_attr_flags;
      	__u32			i_generation;
      	union { /* datos especificos dependientes del tipo de filesystem */
      		struct pipe_inode_info		pipe_i;
      		struct minix_inode_info		minix_i;
      		struct ext2_inode_info		ext2_i;
      		struct hpfs_inode_info		hpfs_i;
      		struct ntfs_inode_info          ntfs_i;
      		struct msdos_inode_info		msdos_i;
      		struct umsdos_inode_info	umsdos_i;
      		struct iso_inode_info		isofs_i;
      		struct nfs_inode_info		nfs_i;
      		struct sysv_inode_info		sysv_i;
      		struct affs_inode_info		affs_i;
      		struct ufs_inode_info		ufs_i;
      		struct romfs_inode_info		romfs_i;
      		struct coda_inode_info		coda_i;
      		struct smb_inode_info		smbfs_i;
      		struct hfs_inode_info		hfs_i;
      		struct adfs_inode_info		adfs_i;
      		struct qnx4_inode_info		qnx4_i;
      		struct socket			socket_i;
      		void				*generic_ip;
      	} u;
      };
  Listado 2: Estructura del VFS inode

  Mención especial requiere el puntero i_op, pues es el que define las operaciones que se pueden realizar con cada inodo. Al igual que hemos diferenciado inodo de fichero, existen una serie de operacione que se pueden realizar con inodos ( link, unlink, mkdir, mknod, rename, etc ) y una serie de operaciones que se pueden realizar con los ficheros asociados a cada inodo, -caso de que existan- ( read, write, mmap, readdir, seek ). En la próxima entrega de esta serie detallaremos su manejo

  ### Resumen

  Hemos descrito en este artículo a grandes rasgos, la estructura y elementos del Virtual File System de Linux, detallando los conceptos de filesystem, superbloque, inodo y fichero. En el próximo número veremos mediante ejemplos su funcionamiento, asi como daremos guías para que el lector pueda crear sus propios sistemas de ficheros, o modificar los existentes

  ### Referencias

  En el CD-Rom que se acompaña a esta revista se incluye la mayor parte de la documentación disponible sobre el Virtual File System:
  - The Kernel hackers Guide ( obsoleto, pero todavía útil )
  - The Linux Kernel
  - Linux kernel Modules programmer's guide
  - The Linux Virtual File System ( transparencias )
  Y por supuesto "Use the source, Luke", el código fuente del núcleo Linux es , como siempre, la mejor referencia.