2

在 xnu 我们有vnode_t代表全局文件的实体。

每个进程都可以通过设置新的文件描述符并在 fg_data 下设置 vnode 来访问文件(假设它具有正确的权限)

fp->f_fglob->fg_data = vp;

vnode 包含所有相关操作的基本操作列表,并根据文件的 FS 进行设置。即HFS+ 驱动程序实现这样的向量并相应地设置它的vnode。

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

这是一个用于可能在 vnode 上操作的所有操作的函数指针的向量。

此外,我们还有 fileops 结构,它是文件描述符 (fg_global) 的一部分,它描述了这些函数的最小子集:

这是一个典型的定义:

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,
};

我们在这里设置:

fp->f_fglob->fg_ops = &vnops;

我看到在本地文件系统(HFS +)下读取常规文件时,它通过 file_descriptor 而不是 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=<unavailable>, flags=<unavailable>, retval=<unavailable>) at sys_generic.c:365 [opt]
frame #3: 0xffffff801339c983 kernel`read_nocancel(p=0xffffff801a597658, uap=0xffffff801a553cc0, retval=<unavailable>) at sys_generic.c:215 [opt]
frame #4: 0xffffff8013425695 kernel`unix_syscall64(state=<unavailable>) at systemcalls.c:376 [opt]
frame #5: 0xffffff8012e9dd46 kernel`hndl_unix_scall64 + 22

我的问题是为什么需要这种对偶性,在哪些情况下操作通过 file_descriptor 向量 (fg_ops) 进行,在哪些情况下操作通过 vnode 向量 (vp->v_op) 进行。

谢谢

4

1 回答 1

2

[…] 在哪些情况下,操作通过 file_descriptor 向量 (fg_ops) 进行,在哪些情况下,操作通过 vnode 向量 (vp->v_op) 进行。

我将首先回答问题的第二部分:如果您进一步跟踪调用堆栈,并查看vn_read函数内部,您会发现它包含以下行:

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

函数 ( kpi_vfs.c VNOP_READ) 依次具有:

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

因此,您的问题的答案是,对于您的典型文件,两个表都用于调度操作。

有了这个,

我的问题是为什么需要这种二元性 […]

并非进程可以保存文件描述符的所有内容也都在文件系统中表示。例如,管道不一定要命名。在这种情况下,vnode 没有任何意义。所以在 sys_pipe.c 中,你会看到一个不同的 fileops 表:

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,
};

套接字的类似交易。

文件描述符跟踪允许类似文件操作的文件或对象的进程视图状态。文件中的位置等 - 不同的进程可以打开同一个文件,并且它们必须每个都有自己的读/写位置 - 所以 vnode:fileglob 是 1:many 关系。

同时,使用 vnode 对象来跟踪文件系统中的对象以外的东西也没有任何意义。此外,v_op 表是特定于文件系统的,而 vn_read/VNOP_READ 包含适用于文件系统中表示的任何文件的代码。

所以总而言之,它们实际上只是 I/O 堆栈中的不同层。

于 2017-06-07T15:00:38.897 回答