文章目录
-
- 概述
- 调用链全景图
- [第一阶段:用户空间 - glibc 库处理](#第一阶段:用户空间 - glibc 库处理)
-
- [1.1 应用程序调用](#1.1 应用程序调用)
- [1.2 glibc 内部实现](#1.2 glibc 内部实现)
- [1.3 宏展开过程](#1.3 宏展开过程)
- [1.4 ARM64 汇编生成](#1.4 ARM64 汇编生成)
- [第二阶段:内核入口 - 异常处理](#第二阶段:内核入口 - 异常处理)
-
- [2.1 硬件自动处理](#2.1 硬件自动处理)
- [2.2 异常向量表跳转](#2.2 异常向量表跳转)
- [2.3 kernel_ventry 宏](#2.3 kernel_ventry 宏)
- [2.4 el0_sync 处理](#2.4 el0_sync 处理)
- 第三阶段:系统调用分发
-
- [3.1 异常分发器](#3.1 异常分发器)
- [3.2 系统调用处理](#3.2 系统调用处理)
- [3.3 系统调用表查找](#3.3 系统调用表查找)
- [第四阶段:VFS 层处理](#第四阶段:VFS 层处理)
-
- [4.1 sys_openat 实现](#4.1 sys_openat 实现)
- [4.2 核心打开函数](#4.2 核心打开函数)
- [4.3 文件系统层处理](#4.3 文件系统层处理)
- [4.4 路径解析和打开](#4.4 路径解析和打开)
- 第五阶段:驱动层调用
-
- [5.1 VFS 到驱动的桥梁](#5.1 VFS 到驱动的桥梁)
- [5.2 关键分发点:do_dentry_open](#5.2 关键分发点:do_dentry_open)
- [5.3 设备驱动示例](#5.3 设备驱动示例)
- 调用链总结表
- 关键数据结构
-
- [1. file_operations 结构](#1. file_operations 结构)
- [2. file 结构](#2. file 结构)
- [3. inode 结构](#3. inode 结构)
概述
在 Linux 系统中,当应用程序调用 open() 函数打开一个设备文件时,会触发一系列复杂而精密的调用过程。这个过程横跨用户空间和内核空间,涉及库函数、系统调用、异常处理、虚拟文件系统(VFS)以及最终的设备驱动。本文将以 ARM64 架构为例,详细剖析从 glibc 库的 open() 到设备驱动 open() 方法的完整调用链。
调用链全景图
text
应用层 open() → glibc库 → 系统调用触发 → 异常处理 → VFS层 → 驱动层
│ │ │ │ │ │
EL0 EL0 EL0→EL1 EL1 EL1 EL1
用户态 用户态 特权切换 内核态 内核态 内核态
第一阶段:用户空间 - glibc 库处理
1.1 应用程序调用
c
int fd = open("/dev/mydevice", O_RDWR);
1.2 glibc 内部实现
在 glibc 2.42 中,open() 实际上是 __libc_open() 的别名:
c
// glibc-2.42/sysdeps/unix/sysv/linux/open64.c
int __libc_open(const char *file, int oflag, ...)
{
int mode = 0;
// 处理可变参数(文件创建时的权限)
if (__OPEN_NEEDS_MODE(oflag)) {
va_list arg;
va_start(arg, oflag);
mode = va_arg(arg, int);
va_end(arg);
}
// 通过宏展开调用 openat 系统调用
return SYSCALL_CANCEL(openat, AT_FDCWD, file, oflag, mode);
}
1.3 宏展开过程
c
// 宏展开链
SYSCALL_CANCEL(openat, AT_FDCWD, file, oflag, mode)
→ INLINE_SYSCALL_CALL(openat, AT_FDCWD, file, oflag, mode)
→ __INLINE_SYSCALL4(openat, AT_FDCWD, file, oflag, mode)
→ INTERNAL_SYSCALL(openat, 4, AT_FDCWD, file, oflag, mode)
1.4 ARM64 汇编生成
最终生成的 ARM64 汇编代码:
assembly
// 参数设置
mov x0, #AT_FDCWD // 第一个参数:目录描述符
mov x1, 文件路径指针 // 第二个参数:文件路径
mov x2, 打开标志 // 第三个参数:打开标志
mov x3, 权限模式 // 第四个参数:权限模式
mov x8, #56 // 系统调用号:__NR_openat = 56
// 触发系统调用
svc #0 // 陷入内核,触发同步异常
第二阶段:内核入口 - 异常处理
2.1 硬件自动处理
当执行 svc #0 指令时,ARM64 硬件自动执行:
- 异常识别:识别为同步异常(Synchronous Exception)
- 来源判断:来自 EL0(用户态)
- 模式判断:64位执行模式
- 计算偏移:同步异常 + EL0 + 64位 = 向量表偏移 0x400
2.2 异常向量表跳转
c
// arch/arm64/kernel/entry.S
// 异常向量表定义
.align 11
SYM_CODE_START(vectors)
// ... 其他向量 ...
kernel_ventry 0, sync // 同步异常,64位EL0(偏移0x400)
// ... 其他向量 ...
SYM_CODE_END(vectors)
// VBAR_EL1 寄存器指向 vectors
// 跳转到 vectors + 0x400 处执行
2.3 kernel_ventry 宏
assembly
.macro kernel_ventry, el, label, regsize=64
.if \el == 0
// EL0 特殊处理
.endif
sub sp, sp, #S_FRAME_SIZE // 分配栈空间
b el\()\el\()_\label // 跳转到处理函数
.endm
2.4 el0_sync 处理
assembly
SYM_CODE_START_LOCAL_NOALIGN(el0_sync)
kernel_entry 0 // 保存用户态上下文
mov x0, sp // pt_regs 指针作为参数
bl el0_sync_handler // 调用C处理函数
b ret_to_user // 返回用户空间
SYM_CODE_END(el0_sync)
第三阶段:系统调用分发
3.1 异常分发器
c
// arch/arm64/kernel/entry-common.c
asmlinkage void noinstr el0_sync_handler(struct pt_regs *regs)
{
unsigned long esr = read_sysreg(esr_el1); // 读取异常症状寄存器
switch (ESR_ELx_EC(esr)) { // 根据异常类别分发
case ESR_ELx_EC_SVC64: // 0x15 = 64位系统调用
el0_svc(regs); // 处理系统调用
break;
case ESR_ELx_EC_DABT_LOW: // 数据中止异常
el0_da(regs, esr);
break;
// ... 其他异常处理 ...
}
}
3.2 系统调用处理
c
static void noinstr el0_svc(struct pt_regs *regs)
{
enter_from_user_mode(); // 用户模式进入内核准备
do_el0_svc(regs); // 执行系统调用
}
void do_el0_svc(struct pt_regs *regs)
{
sve_user_discard(); // 处理SVE状态
el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table);
}
3.3 系统调用表查找
c
// arch/arm64/kernel/sys.c
void *sys_call_table[NR_syscalls] = {
[0 ... NR_syscalls-1] = sys_ni_syscall,
#include <asm/unistd.h> // 包含系统调用定义
};
// uapi/asm-generic/unistd.h
#define __NR_openat 56
__SYSCALL(__NR_openat, sys_openat)
第四阶段:VFS 层处理
4.1 sys_openat 实现
c
// fs/open.c
SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename,
int, flags, umode_t, mode)
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(dfd, filename, flags, mode);
}
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
struct open_how how = build_open_how(flags, mode);
return do_sys_openat2(dfd, filename, &how);
}
4.2 核心打开函数
c
static long do_sys_openat2(int dfd, const char __user *filename,
struct open_how *how)
{
struct open_flags op;
int fd;
// 1. 构建打开标志
fd = build_open_flags(how, &op);
if (fd) return fd;
// 2. 获取内核文件名
struct filename *tmp = getname(filename);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
// 3. 分配文件描述符
fd = get_unused_fd_flags(how->flags);
if (fd >= 0) {
// 4. 实际打开文件
struct file *f = do_filp_open(dfd, tmp, &op);
if (!IS_ERR(f)) {
fsnotify_open(f);
fd_install(fd, f);
} else {
put_unused_fd(fd);
fd = PTR_ERR(f);
}
}
putname(tmp);
return fd;
}
4.3 文件系统层处理
c
struct file *do_filp_open(int dfd, struct filename *pathname,
const struct open_flags *op)
{
struct nameidata nd;
struct file *filp;
set_nameidata(&nd, dfd, pathname);
// 尝试RCU快速路径
filp = path_openat(&nd, op, flags | LOOKUP_RCU);
if (unlikely(filp == ERR_PTR(-ECHILD)))
filp = path_openat(&nd, op, flags); // 慢速路径
restore_nameidata();
return filp;
}
4.4 路径解析和打开
c
static struct file *path_openat(struct nameidata *nd,
const struct open_flags *op,
unsigned flags)
{
struct file *file = alloc_empty_file(op->open_flag, current_cred());
if (unlikely(file->f_flags & __O_TMPFILE)) {
error = do_tmpfile(nd, flags, op, file);
} else if (unlikely(file->f_flags & O_PATH)) {
error = do_o_path(nd, flags, file);
} else {
// 普通文件打开
const char *s = path_init(nd, flags);
while (!(error = link_path_walk(s, nd)) &&
(s = open_last_lookups(nd, file, op)) != NULL)
;
if (!error)
error = do_open(nd, file, op);
terminate_walk(nd);
}
return file;
}
第五阶段:驱动层调用
5.1 VFS 到驱动的桥梁
c
static int do_open(struct nameidata *nd, struct file *file,
const struct open_flags *op)
{
// ... 权限检查、审计等 ...
// 关键调用:vfs_open
error = vfs_open(&nd->path, file);
return error;
}
int vfs_open(const struct path *path, struct file *file)
{
file->f_path = *path;
return do_dentry_open(file, d_backing_inode(path->dentry), NULL);
}
5.2 关键分发点:do_dentry_open
c
static int do_dentry_open(struct file *f,
struct inode *inode,
int (*open)(struct inode *, struct file *))
{
// 1. 设置基础信息
f->f_inode = inode;
f->f_mapping = inode->i_mapping;
// 2. 获取文件操作函数表
f->f_op = fops_get(inode->i_fop); // ⭐ 从inode获取f_op ⭐
// 3. 安全检查
error = security_file_open(f);
if (error) goto cleanup_all;
// 4. 文件锁处理
error = break_lease(locks_inode(f), f->f_flags);
// 5. ⭐⭐ 关键:调用设备驱动的open方法 ⭐⭐
if (!open)
open = f->f_op->open; // 获取驱动注册的open函数指针
if (open) {
error = open(inode, f); // 调用驱动open方法
if (error) goto cleanup_all;
}
// 6. 标记文件已打开
f->f_mode |= FMODE_OPENED;
return 0;
}
5.3 设备驱动示例
c
// 字符设备驱动示例
static const struct file_operations mydevice_fops = {
.owner = THIS_MODULE,
.open = mydevice_open, // 驱动注册的open方法
.read = mydevice_read,
.write = mydevice_write,
.release = mydevice_release,
// ... 其他操作 ...
};
static int mydevice_open(struct inode *inode, struct file *filp)
{
// 驱动特定的打开操作:
// 1. 硬件初始化
// 2. 资源分配
// 3. 权限检查
// 4. 私有数据设置
struct mydevice_data *data = kmalloc(sizeof(*data), GFP_KERNEL);
filp->private_data = data;
// 初始化硬件
writel(INIT_VALUE, device_base + CONTROL_REG);
return 0; // 成功返回0
}
// 设备注册
static int __init mydevice_init(void)
{
dev_t dev = MKDEV(MAJOR_NUM, MINOR_NUM);
// 注册字符设备
register_chrdev_region(dev, 1, "mydevice");
// 创建设备节点
cdev_init(&mydevice_cdev, &mydevice_fops);
cdev_add(&mydevice_cdev, dev, 1);
// 自动创建设备文件
device_create(mydevice_class, NULL, dev, NULL, "mydevice");
return 0;
}
调用链总结表
| 阶段 | 关键函数/节点 | 所属层级 | 核心功能 |
|---|---|---|---|
| 用户空间 | open() |
应用层 | 应用程序标准接口 |
__libc_open() |
glibc库 | 参数处理,封装系统调用 | |
svc #0 |
硬件指令 | 触发系统调用,用户态→内核态切换 | |
| 异常处理 | vectors + 0x400 |
异常向量表 | 同步异常入口 |
el0_sync |
异常处理 | 保存上下文,调用分发器 | |
el0_sync_handler() |
异常分发 | 根据ESR寄存器分发异常类型 | |
| 系统调用 | el0_svc() |
系统调用处理 | SVC指令专门处理 |
el0_svc_common() |
系统调用核心 | 通用处理逻辑 | |
invoke_syscall() |
系统调用执行 | 查表调用具体系统调用 | |
| VFS层 | sys_openat() |
系统调用实现 | open系统调用的内核实现 |
do_sys_openat2() |
VFS核心 | 处理打开标志,分配文件描述符 | |
do_filp_open() |
文件系统接口 | 创建file结构,路径解析 | |
path_openat() |
路径处理 | 路径遍历,符号链接处理 | |
do_open() |
打开操作 | 权限检查,审计处理 | |
vfs_open() |
VFS统一入口 | 连接路径和文件操作 | |
do_dentry_open() |
VFS→驱动桥梁 | 关键分发点,获取f_op | |
| 驱动层 | f->f_op->open |
驱动接口 | 调用驱动注册的open方法 |
xxx_open() |
驱动实现 | 设备特定的打开操作 |
关键数据结构
1. file_operations 结构
c
struct 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 *); // ⭐ 驱动open方法 ⭐
int (*release) (struct inode *, struct file *);
// ... 其他方法 ...
};
2. file 结构
c
struct file {
struct path f_path;
struct inode *f_inode;
const struct file_operations *f_op; // ⭐ 关键:文件操作表 ⭐
spinlock_t f_lock;
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
loff_t f_pos;
void *private_data; // 驱动私有数据
// ... 其他字段 ...
};
3. inode 结构
c
struct inode {
umode_t i_mode;
const struct inode_operations *i_op;
const struct file_operations *i_fop; // ⭐ 设备类型对应的操作表 ⭐
struct address_space *i_mapping;
// ... 其他字段 ...
};