ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【Linux笔记】Linux虚拟文件系统

【Linux笔记】Linux虚拟文件系统 一、虚拟文件系统(VFS)1.1 VFS的认识VFS 是 Linux 内核中的一个抽象层。它位于用户程序和具体文件系统之间提供一套统一的接口让上层应用不需要关心底层到底是ext4、xfs、NFS还是tmpfs。用户程序cat、ls、cp... │ │ 统一IO调用open()、read()、write()、close()... ▼ ┌─────────────────────────────────┐ │ VFS │ 抽象层定义统一接口屏蔽底层差异 └────┬────────┬────────┬────────┬─┘ │ │ │ │ ▼ ▼ ▼ ▼ ext4 xfs NFS tmpfs ... (磁盘) (日志) (网络) (内存)VFS 是内核中的一套翻译层把用户统一的文件操作请求翻译成具体的文件系统能理解的指令。VFS整体框架描述┌─────────────────────────────────────────────────────────────────────────────┐ │ VFS Framework │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Process Side (Who operates) │ │ │ │ │ │ │ │ task_struct │ │ │ │ └── files_struct ← Per-process File Table │ │ │ │ └── fdtable ← fd-to-file Mapping │ │ │ │ └── fd[n] ──→ struct file │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ struct file is the bridge │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ File Side (What is operated) │ │ │ │ │ │ │ │ struct file ← Open File State (offset, flags) │ │ │ │ ├── f_path.dentry ← Directory Entry (name) │ │ │ │ ├── f_inode ← Inode (metadata) │ │ │ │ └── f_op ← Operation Vtable │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ f_op is the polymorphic dispatch │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Implementation Side (How to operate) │ │ │ │ │ │ │ │ struct file_operations ← Interface Definition │ │ │ │ ├── ext4_file_operations ← ext4 Implementation │ │ │ │ ├── nfs_file_operations ← NFS Implementation │ │ │ │ └── shmem_file_operations ← tmpfs Implementation │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘1.2 为什么需要VFS1.2.1不存在 VFS假设不存在虚拟文件系统VFS对于不同的文件系统需要不同操作接口导致代码冗余已经操作系统代码膨胀。如果每个程序都要自己处理不同文件系统 cat 程序需要写 if (文件系统 ext4) { 用 ext4 的方式读文件 } else if (文件系统 xfs) { 用 xfs 的方式读文件 } else if (文件系统 NFS) { 用 NFS 的方式读文件 } else if (文件系统 btrfs) { ... } ls 程序也要写一遍... cp 程序也要写一遍... 每个程序都要写一遍...1.2.2 存在 VFScat 程序只需要 fd open(/home/user/test.txt, O_RDONLY); read(fd, buf, size); close(fd); VFS 负责 - 判断 /home/user/test.txt 在哪个文件系统上 - 调用那个文件系统的 read 实现 - 把结果返回给 cat cat 完全不需要知道底层是 ext4 还是 xfs。1.3 VFS核心数据结构1.3.1Superblock---超级块Superblock描述的是整个文件系统的全局信息相当于一个文件系统的身份证。Superblock 记录的内容 - 文件系统类型ext4 / xfs / nfs... - 块大小4096 字节 - 总块数、空闲块数 - 总 inode 数、空闲 inode 数 - 根目录的 inode 编号在ext4 中固定为 2 - 挂载标志只读 / 读写 - 指向具体文件系统操作的函数指针表Linux内核中描述的super_block结构体struct super_block { struct file_system_type *s_type; // 文件系统类型ext4_fs_type const struct super_operations *s_op; // 操作函数表 struct dentry *s_root; // 根目录的 dentry unsigned long s_blocksize; // 块大小 unsigned long s_inodes_count; // inode 总数 unsigned long s_free_blocks_count; // 空闲块数 ... };1.3.2Inode---索引节点Inode描述的是一个具体文件的元数据文件属性不包含文件名不包含文件内容。Inode 记录的内容 - 文件类型普通文件 / 目录 / 软链接 / 设备文件 - 权限rwxr-xr-x - 所有者uid / gid - 文件大小 - 时间戳创建、修改、访问时间 - 硬链接计数 - 数据块指针文件内容存在磁盘的哪些块上Linux内核中描述的内存级别struct inode结构体struct inode { umode_t i_mode; // 文件类型 权限 uid_t i_uid; // 所有者 gid_t i_gid; // 所属组 loff_t i_size; // 文件大小 struct timespec i_atime; // 最后访问时间 struct timespec i_mtime; // 最后修改时间 struct timespec i_ctime; // 最后状态变更时间 unsigned int i_nlink; // 硬链接数 unsigned long i_ino; // inode 编号 const struct inode_operations *i_op; // inode 操作函数表 const struct file_operations *i_fop; // 文件操作函数表 struct address_space *i_mapping; // 关联的 Page Cache ... };关键点Inode 不包含文件名一个文件可以有多个名字硬链接 ​ /home/user/file_a ──┐ ├── inode 25000同一份数据 /home/user/file_b ──┘ ​ 文件名存在 dentry 中不在 inode 中。1.3.3Dentry---目录项Dentry 是文件名 和 inode 之间的桥梁解决名字 → 文件的映射问题。dentry 记录的内容 - 文件对应的 inode - 父目录的 dentry - 文件名 - 子目录项链表 - 引用计数Linux内核中描述的Dentry结构体struct dentry { struct inode *d_inode; // 对应的 inode struct dentry *d_parent; // 父目录的 dentry struct qstr d_name; // 文件名 struct list_head d_subdirs; // 子目录项链表 unsigned int d_count; // 引用计数 ... };Dentry解决的问题通过文件名将文件的inode进行关联问题inode 只有编号没有名字。用户输入的是路径名字不是编号。 解决dentry 把名字和编号关联起来。 dentry: name test.txt, d_inode - inode 25000 dentry: name home, d_inode - inode 1000由Dentry 从文件的根目录进行路径解析例如cat /home/user/test.txt第 1 级从根目录 / 开始 - 根目录的 inode 编号固定为 2硬编码在内核中 - 读取 inode 2找到根目录的数据块 - 在根目录的数据块中查找 home找到它的 inode 编号假设是 1000 第 2 级进入 /home/ - 读取 inode 1000找到 /home/ 目录的数据块 - 在其中查找 user找到它的 inode 编号假设是 2000 第 3 级进入 /home/user/ - 读取 inode 2000找到 /home/user/ 目录的数据块 - 在其中查找 test.txt找到它的 inode 编号假设是 25000 第 4 级读取文件 - 读取 inode 25000获取文件属性和数据块指针 - 根据指针读取数据块获得文件内容 - 输出到终端1.3.4struct file---文件对象File 描述的是一个进程打开文件后的状态是进程和 inode 之间的连接。file 记录的内容 - 文件路径 - 文件的inode - 文件操作函数表 - 读/写模式 - 当前读写位置 - 引用计数Linux内核中描述的file结构体struct file { struct path f_path; // 文件路径dentry vfsmount struct inode *f_inode; // 对应的 inode const struct file_operations *f_op; // 文件操作函数表 unsigned int f_flags; // 打开标志O_RDONLY / O_WRONLY... fmode_t f_mode; // 读/写模式 loff_t f_pos; // 当前读写位置文件偏移量 unsigned long f_count; // 引用计数 ... };关键点一个文件可以由多个进程进行打开进程 Afd3 打开 test.txt只读偏移量0 进程 Bfd5 打开 test.txt读写偏移量100 两个 struct file 对象指向同一个 inode 进程 A 的 file 结构 ──┐ ├── inode 25000 进程 B 的 file 结构 ──┘ 各自有独立的 f_pos偏移量互不影响。1.4 由进程到文件Linux操作系统由进程到文件操作的框架图┌─────────────────────────────────────────────────────────────────────────────┐ │ Process (task_struct) │ │ │ │ task-files ───→ ┌─────────────────────┐ │ │ │ struct files_struct│ │ │ └──────────┬──────────┘ │ │ │ │ │ ↓ │ │ ┌─────────────────────┐ │ │ │ struct fdtable │ │ │ └──────────┬──────────┘ │ │ │ │ │ ┌────────┬───────┬─────┼──────┬────────┐ │ │ ↓ ↓ ↓ ↓ ↓ │ │ fd[0] fd[1] fd[2] fd[3] fd[4] │ │ │ │ │ │ │ │ │ ↓ ↓ ↓ ↓ ↓ │ │ struct struct struct struct NULL │ │ file file file file │ │ (stdin) (stdout) (stderr) (test.txt) │ │ │ │ │ ├─ f_path.dentry │ │ ├─ f_inode │ │ ├─ f_pos │ │ └─ f_op │ │ │ │ │ ↓ │ │ ┌──────────────────────┐ │ │ │struct file_operations│ │ │ └──────────┬───────────┘ │ │ │ │ │ ├─ .read ext4_file_read │ │ ├─ .write ext4_file_write │ │ ├─ .open ext4_file_open │ │ └─ ... │ └─────────────────────────────────────────────────────────────────────────────┘1.4.1struct files_struct进程的文件管理器每个进程有且仅有一个files_struct它是该进程所有打开文件的总管。VFS 需要回答一个问题 进程 A 的 fd3 对应哪个文件 答案就存在 files_struct 中。Linux内核中描述的files_structstruct files_struct { atomic_t count; // 引用计数有多少线程共享这个表 struct fdtable __rcu *fdt; // 指向当前的 fdtable核心 struct fdtable fdtab; // 默认的小表内嵌避免额外分配 spinlock_t file_lock; // 保护并发访问的锁 int next_fd; // 下次分配 fd 的起始搜索位置优化 struct file __rcu * fd_array[NR_OPEN_DEFAULT]; // 默认 fd 数组通常 64 个 };关键设计为什么有 fdtab 又有 fdt进程刚创建时打开的文件很少stdin/stdout/stderr最多几个 → 用内嵌的 fdtab 就够了不需要额外分配内存 如果进程打开了大量文件超过 64 个 → 分配一个更大的 fdtablefdt 指向它 → 原来的 fdtab 不再使用 这是一种小对象内嵌大对象外挂的优化策略。files_struct 是 VFS 的进程侧入口。 VFS 处理任何文件操作的第一步 - 从当前进程的 task_struct 中取出 files_struct - 再从 files_struct 中根据 fd 找到 struct file 若没有 files_structVFS 就不知道进程打开了哪些文件。1.4.2struct fdtablefdtable是files_struct内部实际存储 fd → file 映射的数据结构。Linux内核中描述的struct fdtablestruct fdtable { unsigned int max_fds; // 当前数组能容纳的最大 fd 数 struct file __rcu **fd; // fd 数组核心 // fd[0] → struct file* // fd[1] → struct file* // fd[3] → struct file* // fd[4] → NULL unsigned long *close_on_exec; // exec 时需要关闭的 fd 位图 unsigned long *open_fds; // 已打开的 fd 位图快速查找哪些 fd 在用 unsigned long *full_fds_bits; // 二级位图加速空闲 fd 查找 struct rcu_head rcu; // RCU 回收用 };fd 数组的直观理解fdtable-fd 数组 索引: [0] [1] [2] [3] [4] [5] 值: file* file* file* file* NULL NULL │ │ │ │ ▼ ▼ ▼ ▼ stdin stdout stderr test.txt open_fds 位图: 1 1 1 1 0 0 ... 标记哪些 fd 正在使用与 VFS 的关系 当用户调用 read(3, buf, size) 时 VFS 内部 1. 取出 文件描述符fd 2. 得到 struct file* 指针 3. 用这个 file 去执行后续操作 fdtable 就是 VFS 的索引表把用户给的整数 fd 翻译成内核的 struct file 指针。1.4.3struct file每次open()成功内核就创建一个struct file对象它代表一次打开操作的状态。Linux内核中描述的struct filestruct file { struct path f_path; // 文件路径 // f_path.dentry → 目录项名字 和 inode 的桥梁 // f_path.mnt → 挂载点属于哪个文件系统 struct inode *f_inode; // 快捷指针直接指向 inode const struct file_operations *f_op; // 操作函数表核心多态的体现 atomic_long_t f_count; // 引用计数多少个 fd 指向它 unsigned int f_flags; // 打开标志O_RDONLY / O_NONBLOCK... fmode_t f_mode; // 读/写权限FMODE_READ / FMODE_WRITE loff_t f_pos; // 当前读写偏移量核心 struct fown_struct f_owner; // 异步 I/O 的所有者 void *private_data; // 具体文件系统的私有数据 struct address_space *f_mapping; // 关联的 Page Cache ... };关键设计点一同一个 inode 可以有多个 struct file进程 Aopen(test.txt, O_RDONLY) → 创建 file_1f_pos0 进程 Bopen(test.txt, O_RDWR) → 创建 file_2f_pos0 file_1 和 file_2 是两个独立对象 - 各自有独立的 f_pos互不影响 - 各自有独立的 f_flags - 但指向同一个 inode同一份数据 file_1 ──→ inode 25000 ←── file_2关键设计点二多个个 fd 可以指向同一份 struct filedup2() / fork() 的情况 进程 A fd[3] ──┐ ├──→ 同一个 struct filefile结构体中的引用计数f_count 2 fd[5] ──┘ 进程 B 两个 fd 共享同一个 file 对象 - 共享同一个 f_pos一个读了另一个的偏移量也变了 - 共享同一个 f_flags关键设计点三f_op 是什么时候赋值open() 的过程中 1. VFS 路径解析找到 inode 2. 从 inode 中取出默认的文件操作表 file-f_op inode-i_fop; // 例如 ext4_file_operations 3. 具体文件系统的 open 函数可以修改 f_op // 例如如果文件是目录ext4 会把 f_op 换成目录专用的操作表 file-f_op ext4_dir_operations;struct file与VFS的关系struct file 是 VFS 中连接进程和文件系统的核心枢纽。 向上通过 fdtable 被进程引用 向下通过 f_op 连接到具体文件系统的实现 向内通过 f_inode / f_path 连接到 inode 和 dentry 它是 VFS 四大对象之一是打开状态的载体。1.4.4struct file_operations这是 VFS 实现多态的核心机制它定义了一组函数指针每个具体文件系统提供自己的实现。Linux内核中描述的struct file_operationsstruct file_operations { struct module *owner; // 定位 loff_t (*llseek)(struct file *, loff_t, int); // 读写 ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); // 打开 / 关闭 int (*open)(struct inode *, struct file *); int (*release)(struct inode *, struct file *); // 元数据操作 int (*flush)(struct file *, fl_owner_t id); int (*fsync)(struct file *, loff_t, loff_t, int datasync); // 内存映射 int (*mmap)(struct file *, struct vm_area_struct *); // 轮询select/poll/epoll 用 unsigned int (*poll)(struct file *, struct poll_table_struct *); // ioctl long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long); // 目录操作 int (*iterate)(struct file *, struct dir_context *); ... };Linux操作系统 基于不同文件系统的实现// ext4 的普通文件操作 const struct file_operations ext4_file_operations { .llseek ext4_llseek, .read ext4_file_read_iter, .write ext4_file_write_iter, .open ext4_file_open, .release ext4_release_file, .fsync ext4_sync_file, .mmap ext4_file_mmap, ... }; // ext4 的目录操作 const struct file_operations ext4_dir_operations { .llseek ext4_dir_llseek, .read generic_read_dir, // 目录不能 read返回错误 .iterate ext4_readdir, // 用 iterate 遍历目录 .open ext4_dir_open, .release ext4_release_dir, .fsync ext4_sync_file, ... }; // NFS 的文件操作 const struct file_operations nfs_file_operations { .llseek nfs_file_llseek, .read nfs_file_read, .write nfs_file_write, .open nfs_file_open, .release nfs_file_release, .fsync nfs_file_fsync, ... }; // tmpfs 的文件操作 const struct file_operations shmem_file_operations { .mmap shmem_mmap, .read shmem_file_read_iter, .write shmem_file_write_iter, ... };struct file_operations与VFS的关系file_operations 是 VFS 的接口定义 1. VFS 层只定义有哪些操作read/write/open...具体文件系统定义怎么实现这些操作read/write/open... 2.VFS 调用时 file-f_op-read(file, buf, size, pos) - 它不关心 read 到底是从磁盘读、从网络读、还是从内存读 - 它只知道调用 f_op 里的 read 函数指针1.5 实例解析VFS1.5.1 read调用全流程完整调用细节如下read(3, buf, 4096) ← 用户空间glibc │ │ syscall 指令 ▼ sys_read(3, buf, 4096) ← 系统调用入口 │ ▼ ksys_read(3, buf, 4096) ← VFS 通用层 │ ├── fdget_pos(3) ← 【进程侧】fd → struct file │ current-files-fdt-fd[3] → file │ get_file(file) ← f_count │ ├── 安全检查 │ file-f_mode FMODE_READ ? │ access_ok(buf, 4096) ? │ ├── 构造 kiocb iov_iter │ kiocb.ki_pos file-f_pos ← 取出当前偏移量 │ iov_iter → 描述用户缓冲区 buf │ ├── file-f_op-read_iter(kiocb, iter) ← 【多态分发点】 │ │ │ │ 如果 f_op ext4_file_operations │ ▼ │ ext4_file_read_iter() ← 【实现侧】 │ ├── 查 Page Cache │ │ 命中 → copy_to_user → 返回 │ │ 未命中 → 磁盘 I/O → 填充 Cache → copy_to_user │ └── return 实际读取字节数 │ ├── file-f_pos kiocb.ki_pos ← 更新偏移量 │ ├── fdput_pos(f) ← f_count-- │ └── return n ← 返回用户空间1.5.2 open调用全流程完整调用细节如下fd open(/home/test.txt, O_RDWR, 0644) │ │ ▼ sys_open ──▶ 系统调用入口 │ │ ▼ do_sys_openat2 │ ├─ getname(/home/test.txt) ──▶ struct filename │ ├─ build_open_flags() ──▶ 构造 open_flags │ ├─ get_unused_fd_flags() ──▶ 从 fdtable 中取一个空闲 fd (如 fd5) │ ├─ do_filp_open(opath, op) ──▶ 核心创建 struct file │ │ │ └─ path_openat() ──▶ 路径查找 打开 │ │ │ ├─ 1. 路径解析 (link_path_walk) │ │ / ──▶ root dentry │ │ home ──▶ 查 dcache → 未命中 → ext4_lookup │ │ test.txt ──▶ 查 dcache → 未命中 → ext4_lookup │ │ │ │ │ ▼ │ │ dentry ──▶ inode │ │ │ ├─ 2. 权限检查 (may_open) │ │ └─ inode_permission() ──▶ 检查 rwx 权限 │ │ │ ├─ 3. 填充 struct file │ │ └─ alloc_file() │ │ ├─ f_inode inode │ │ ├─ f_path {mnt, dentry} │ │ ├─ f_op inode-i_fop (如 ext4_file_operations) │ │ ├─ f_mapping inode-i_mapping │ │ └─ f_pos 0 │ │ │ └─ 4. 调用底层 open │ └─ f_op-open(inode, file) │ │ │ ▼ │ 底层具体实现 │ - ext4: ext4_file_open (读日志/journal 等) │ - proc: proc_reg_open (构造虚拟内容) │ - socket: 无 open, 用 socket() 创建 │ - char dev: chrdev_open → 找到 cdev → 替换 f_op │ ├─ fd_install(fd, file) ──▶ 将 file 挂到 fdtable.fd[5] │ └─ return fd ( 5) ──▶ 返回用户态二、理解一切皆文件2.1 本质理解一切皆文件 一切皆能通过同一套字节流接口操作的资源它不是在说存储方式而是在说接口与抽象的统一。通过open/read/write/close等接口实现所有外设IO的操作不再为每个外设单独实现一套操作。2.2 历史设计在早期的Unix 等操作系统中这些系统处理打印机、终端、磁盘、磁带、进程间通信时每一类都有一套独立API。例如打开打印机 调用完全不同于 打开磁盘。导致出现如下问题程序员要学 N 套 API不同设备的错误码、缓冲策略、命名空间都各搞一套工具复用极差本来 cat 读文件能否让它读打印机在不断的迭代过程中操作系统通过 把所有资源都设计成可以用 open/read/write/close 操作的字节流对象这样带来的直接好处编写一个 cp 就自动支持普通文件复制、设备数据复制、网络 socket 数据复制cat file和cat /dev/urandom、cat /proc/cpuinfo用的是同一套工具shell 的管道 | 可以连接任意输入/输出根本原因是它们都是fd2.3 四个层次2.3.1 层次 1同一套系统调用// 对普通文件 int fd open(/etc/hostname, O_RDONLY); read(fd, buf, 100); // 对设备 (/dev/random) fd open(/dev/random, O_RDONLY); read(fd, buf, 100); // 字节流也是 read // 对网络 socket伪装成 fd fd socket(AF_INET, SOCK_STREAM, 0); send(fd, buf, n, 0); // 同样是 write 类语义 // 对管道 int p[2]; pipe(p); write(p[1], hello, 5); // 字节流 // 对进程信息 fd open(/proc/self/status, O_RDONLY); read(fd, buf, 4096); // 是的文本格式的进程信息 open/read/write/close/ioctl/poll 这一套核心调用对几乎所有资源都成立。2.3.2 层次 2同一套资源标识所有这些资源都被映射成 fd整数下标。这意味着 - 进程不区分这是文件还是 socket都放在同一个 fd 表。 - 多路复用select/poll/epoll能一次性等待所有种类的资源 - dup2、fork、权限继承都按 fd 一套规则 - 在 Linux 内核里这就是 struct file file_operations 那套机制2.3.3. 层次 3同一套路径命名空间所有具名的资源都挂在 / 这棵树下 - /home、/etc 是文件系统 - /dev 是设备 - /proc 是进程元数据 - /sys 是硬件与内核对象 - /run 是运行时数据 - /tmp 临时文件特殊之处是别人也能用 这意味着系统管理、配置、调试工具通过同一套 shell 文本流协议就能完成。 echo 100 /sys/class/backlight/acpi_video0/brightness # 写 LED 亮度 echo nameserver 8.8.8.8 /etc/resolv.conf # 写配置文件 cat /proc/net/dev | grep eth0 # 读网络统计2.3.4. 层次 4同一套字节流语义read/write 是顺序的字节流接口区别于消息record或 结构化对象structured。 虽然实际资源可能是 - 块设备的扇区block-based - 网络套接字的报文message-based - 内核对象的有格式文本formatted text - 管道的有界缓冲区buffer-based 用户态看到的接口都被规整成读 N 个字节 / 写 N 个字节。由此 cat、dd、tee、grep、sed 这些工具对所有资源通用。2.4 内核实现内核实现把所有资源都设计成可以用 open/read/write/close 操作的字节流对象内核实现系统调用流程图以read为例用户调用read() │ └─▶ 系统调用陷入内核 │ └─▶ 内核通过fd找到struct file │ └─▶ 通过file-f_op找到操作函数表 │ └─▶ 调用file-f_op-read │ └─▶ 执行具体设备驱动的read实现内核实现系统调用流程细节以read为例用户空间 │ ├─ int fd open(/dev/sda, O_RDONLY); // 打开磁盘设备 │ └─ read(fd, buf, 1024); // 读取操作 │ ▼ 内核空间 │ ├─ struct task_struct (当前进程) │ │ │ └─ struct files_struct *files // 进程的文件描述符表 │ │ │ └─ struct fdtable *fdt // 文件描述符表 │ │ │ └─ struct file *fd[1024] // 具体的文件对象数组 │ │ │ └─ fd[3] struct file { │ f_mode FMODE_READ, │ f_pos 0, │ f_inode ext4_inode, │ f_op ext4_file_operations, // 关键 │ f_path { ... } │ } │ └─ struct file_operations ext4_file_operations { .read ext4_file_read, // 实际调用的函数 .write ext4_file_write, .llseek ext4_file_llseek, .mmap ext4_file_mmap, .open ext4_file_open, .release ext4_release_file, ... }2.5 总结1.一切皆文件 不是设备或数据的存储形式而是 Linux 用 open/read/write/close 文件描述符 树形路径命名空间 把磁盘、设备、内核对象、进程信息、IPC通道、网络套接字统一抽象成可寻址的字节流对象的架构原则。 2. 内核层面的落地方式是 VFS file_operations 多态表——所有不同的资源都注册 file_operations被同一套系统调用调度。
返回列表