Linux open 流程核心数据结构完整串联链路
先记住一条主线进程 → 打开文件表 → struct file → dentry → inode → 驱动 cdev下面分两层讲静态关联关系、open 时动态绑定全过程再配一张串联关系图。一、所有关键结构体基础关系1. 进程struct task_struct每个进程唯一代表一个程序。struct task_struct { // ... struct files_struct *files; // 指向本进程所有打开的文件 }任意进程通过current-files获取自己的文件表。2. 进程打开文件表struct files_struct维护「文件描述符 fd」和「打开实例 struct file」的映射。 内部核心数组fd_array[]下标就是 fd存struct file *。fd0 → 标准输入fd1 → 标准输出fd2 → 标准错误 你 open 成功后内核会找一个空下标存入新 filp 指针。3. 一次打开实例struct file每次调用 open () 都会新建一个独立struct file多次 open 同一设备会生成多个 file。struct file { const struct file_operations *f_op; // 读写open等驱动回调 struct path f_path; // 绑定 dentry inode void *private_data; // 驱动私有数据 fmode_t f_flags; // O_RDWR/O_NONBLOCK等 loff_t f_pos; // 文件读写偏移 };4. struct path打包路径信息struct path { struct vfsmount *mnt; // 文件系统挂载点/dev 属于devtmpfs struct dentry *dentry; // 目录项存文件名字符串 };5. 目录项struct dentry缓存文件名比如/dev/mydev对应的名字 “mydev”做路径快速查找。struct dentry { struct inode *d_inode; // 每个dentry绑定唯一inode char d_name[]; // 文件/设备名称 };6. 索引节点struct inode代表文件真实元数据设备文件的关键struct inode { dev_t i_rdev; // 字符/块设备主次设备号 umode_t i_mode;// 文件类型S_IFCHR/S_IFREG/S_IFBLK // 文件系统inode操作接口 const struct inode_operations *i_op; };判断是否字符设备S_ISCHR(inode-i_mode)7. 字符设备核心struct cdev驱动注册时用cdev_init/cdev_add填充内核全局数组chrdevs[主设备号]管理。struct cdev { struct module *owner; const struct file_operations *ops; // 驱动自己实现的open/read/write dev_t dev; // 主次设备号 };二、open 系统调用动态串联全过程一步一步绑定场景应用执行open(/dev/mydev, O_RDWR)步骤 1进入 sys_openat解析路径字符串内核用copy_from_user拿到用户传的路径开始路径查找。步骤 2路径查找 path_lookup串联 mnt → dentry → inode从根目录/逐层解析找到/dev/mydev对应的struct dentrydentry 里自带d_inode指针拿到设备文件专属struct inodeinode 中i_mode S_IFCHR说明是字符设备i_rdev保存该设备文件的主次设备号。 此时形成链条mnt → dentry → inode步骤 3分配全新 struct file 实例 filp内核申请一块内存创建struct file *filp做初始绑定filp-f_path.mnt 找到的mnt; filp-f_path.dentry 找到的dentry; // 间接绑定inodefilp-f_path.dentry-d_inode inode filp-f_flags O_RDWR; filp-f_pos 0;链条新增filp → path → dentry → inode步骤 4字符设备分支 chrdev_open绑定驱动 cdev 与 f_op内核拿到 inode 里的设备号dev_t dev inode-i_rdev根据主设备号查全局数组chrdevs得到驱动注册的struct cdev将 cdev 里的驱动回调赋值给 filpfilp-f_op cdev-ops; // 赋值你写的mydev_fops调用驱动的 .open 回调你可以在这里static int mydev_open(struct inode *inode, struct file *file) { // 绑定私有数据后续read/write可通过file拿到 file-private_data dev_global; return 0; }链条扩展filp → f_op → cdev(驱动)步骤 5绑定到进程文件表分配 fd获取当前进程current-filesstruct files_struct遍历 fd_array 找最小空闲 fdfiles-fd_array[fd] filp;完整最终串联链条current(task_struct) ↓ task_struct-files (struct files_struct) ↓ fd下标 files_struct-fd_array[fd] → struct file(filp) ↓ filp-f_path struct path → dentry → inode ↓ filp-f_op file_operations → struct cdev(你的驱动)三、读写时如何顺着这条链找到驱动函数应用调用read(fd, buf, len)系统调用sys_read传入 fdcurrent-files-fd_array[fd]取出struct file *filp执行filp-f_op-read(filp, buf, len, filp-f_pos)你的驱动 read 函数被触发参数里自带 file/inode内部用copy_to_user把内核数据拷贝到用户 buf。四、直观结构图文字版用户进程 task_struct└── files : struct files_struct└── fd_array[3] filp_ptr // fd3└── struct file filp├── f_path│ ├── mnt (devtmpfs挂载)│ └── dentry (mydev)│ └── d_inode : inode│ ├── i_mode S_IFCHR│ └── i_rdev 主设备号次设备号├── f_op → 驱动 struct file_operations│ ├── .open│ ├── .read│ └── .write└── private_data → 驱动自定义设备结构体五、关键串联总结fd 只是数组下标桥梁是 files_struct 把数字 fd 映射到 struct filestruct file 是单次打开句柄串联路径dentry/inode和驱动操作集 f_opinode 区分文件类型设备 inode 存放设备号用来匹配内核注册的 cdev 驱动所有驱动回调open/read/write都靠filp-f_op找到是内核调用驱动的核心纽带。