vnode y descriptor de archivo en xnu, donde se almacena el vector de operación de archivo

En xnu tenemos la entidad vnode_t que representa el archivo globalmente.

Cada proceso puede acceder al archivo (asumiendo que tiene permisos correctos) configurando un nuevo descriptor de archivo y configurando el vnode bajo fg_data

 fp->f_fglob->fg_data = vp; 

El vnode contiene una lista de acciones básicas para todas las operaciones relevantes y se configura de acuerdo con el FS del archivo. es decir, el controlador HFS + implementa dicho vector y establece su vnode en consecuencia.

 int (**v_op)(void *); /* vnode operations vector */ 

este es un vector para los punteros de función para todas las acciones que pueden operar en vnode.

Además, tenemos la estructura de archivos que forma parte del descriptor de archivos (fg_global) que describe un subconjunto mínimo de estas funciones:

Aquí hay una definición típica:

 const struct fileops vnops = { .fo_type = DTYPE_VNODE, .fo_read = vn_read, .fo_write = vn_write, .fo_ioctl = vn_ioctl, .fo_select = vn_select, .fo_close = vn_closefile, .fo_kqfilter = vn_kqfilt_add, .fo_drain = NULL, }; 

Y lo ponemos aquí:

 fp->f_fglob->fg_ops = &vnops; 

Vi que al leer un archivo normal en un sistema de archivos local (HFS +), funciona a través del file_descriptor y no del vnode …

  * frame #0: 0xffffff801313c67c kernel`vn_read(fp=0xffffff801f004d98, uio=0xffffff807240be70, flags=0, ctx=0xffffff807240bf10) at vfs_vnops.c:978 [opt] frame #1: 0xffffff801339cc1a kernel`dofileread [inlined] fo_read(fp=0xffffff801f004d98, uio=0xffffff807240be70, flags=0, ctx=0xffffff807240bf10) at kern_descrip.c:5832 [opt] frame #2: 0xffffff801339cbff kernel`dofileread(ctx=0xffffff807240bf10, fp=0xffffff801f004d98, bufp=140222138463456, nbyte=282, offset=, flags=, retval=) at sys_generic.c:365 [opt] frame #3: 0xffffff801339c983 kernel`read_nocancel(p=0xffffff801a597658, uap=0xffffff801a553cc0, retval=) at sys_generic.c:215 [opt] frame #4: 0xffffff8013425695 kernel`unix_syscall64(state=) at systemcalls.c:376 [opt] frame #5: 0xffffff8012e9dd46 kernel`hndl_unix_scall64 + 22 

Mi pregunta es por qué se necesita esta dualidad, y en qué casos la operación funciona a través del vector file_descriptor (fg_ops) y en qué casos la operación funciona a través del vector vnode (vp-> v_op).

Gracias

[…] En qué casos la operación funciona a través del vector file_descriptor (fg_ops) y en qué casos la operación funciona a través del vector vnode (vp-> v_op).

Comenzaré por responder primero a esta segunda parte de la pregunta: si rastrea más a través de tu stack de llamadas y miras dentro de la función vn_read , encontrarás que contiene esta línea:

  error = VNOP_READ(vp, uio, ioflag, ctx); 

La función VNOP_READ (kpi_vfs.c) a su vez tiene esto:

 _err = (*vp->v_op[vnop_read_desc.vdesc_offset])(&a); 

Entonces, la respuesta a su pregunta es que para su archivo típico, ambas tablas se utilizan para las operaciones de envío.

Con eso fuera del camino,

Mi pregunta es ¿por qué esta dualidad es necesaria […]

No todo lo que un proceso puede contener un descriptor de archivo también se representa en el sistema de archivos. Por ejemplo, las tuberías no necesariamente tienen que ser nombradas. Un vnode no tiene ningún sentido en ese contexto. Entonces, en sys_pipe.c, verás una tabla de archivos diferente:

 static const struct fileops pipeops = { .fo_type = DTYPE_PIPE, .fo_read = pipe_read, .fo_write = pipe_write, .fo_ioctl = pipe_ioctl, .fo_select = pipe_select, .fo_close = pipe_close, .fo_kqfilter = pipe_kqfilter, .fo_drain = pipe_drain, }; 

Oferta similar para sockets

Los descriptores de archivos rastrean el estado de la vista de un proceso de un archivo u objeto que permite operaciones de tipo archivo. Cosas como la posición en el archivo, etc. – diferentes procesos pueden tener el mismo archivo abierto, y cada uno debe tener su propia posición de lectura / escritura, por lo que vnode: fileglob es una relación 1: muchos.

Mientras tanto, usar objetos vnode para rastrear cosas que no sean objetos dentro de un sistema de archivos tampoco tiene sentido. Además, la tabla v_op es específica del sistema de archivos, mientras que vn_read / VNOP_READ contiene código que se aplica a cualquier archivo que esté representado en un sistema de archivos.

Entonces, en resumen, en realidad son solo capas diferentes en la stack de E / S.