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 )
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...
|
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....
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:
Figura 2: Diagrama de bloques del sistema de ficheros de Linux |
---|
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
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:
Figura 4: estructura de una entrada en la tabla de montajes |
---|
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:
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
|
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:
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 |
---|