Linux内核深度解析之copy_from_user全路径调用流程与实战(一百零一)
简介: CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址: Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址: 多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

🌻1. 前言
本篇目的:Linux 内核深度解析之 copy_from_user 全路径调用流程与实战。
🌻2. 用法与应用场景
copy_from_user 是内核态安全访问用户态地址空间的标准化接口。
- 用法:
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n); - 返回值:成功返回 0;失败返回 剩余未拷贝字节数。
- 应用场景:
- 系统调用数据载入:如
write、ioctl等系统调用将用户配置或数据下发至内核。 - 网络协议栈:应用层数据包拷贝至内核缓冲区进行封装。
- 设备驱动控制:用户态通过
write向驱动下发控制指令。
🌻3. 调用流程剖析
copy_from_user 的调用链路贯穿了通用逻辑层、安全校验层和硬件架构汇编层,每一个层级都承载了关键职责。
3.1 核心调用栈深度解析(以 ARM64 为例)
copy_from_user(to, from, n)[Macro]
- 定义在
include/linux/uaccess.h。 - 职责:作为统一入口,首先进行
access_ok(from, n)校验。检查用户指针是否位于用户空间范围(),防止用户程序诱导内核访问内核空间。
_copy_from_user(to, from, n)
- 职责:中间分发层。
might_fault():触发内核调试检测,确认当前上下文是否允许阻塞(因为用户态页可能不在内存,拷贝可能导致缺页中断并引起进程睡眠)。
check_object_size(to, n, true)
- 职责:如果内核开启了
CONFIG_HARDENED_USERCOPY,此步会校验to(内核目标地址)是否属于合法的 slab 或堆栈对象,防止缓冲区溢出攻击。
instrument_copy_from_user(to, from, n)
- 职责:为 KASAN(内核地址分析器)提供桩函数,用于检测潜在的内存访问违规。
raw_copy_from_user(to, from, n)
- 职责:进入具体的架构抽象层。
arch_copy_from_user(to, from, n)
- 职责:在
arch/arm64/include/asm/uaccess.h中定义,跳转至汇编实现。
__arch_copy_from_user[Assembly]
- 源文件:
arch/arm64/lib/copy_from_user.S。 - 职责:使用
ldp/stp指令进行高性能搬运。
Exception Table & Fixup Logic
- 职责:如果在汇编搬运期间触发 Page Fault(如地址非法或页在 swap 中),内核会捕获该异常。
异常表机制详解:
内核在编译时会将__arch_copy_from_user的指令地址记录在.section __ex_table中。当硬件报错时,内核异常处理函数do_page_fault会查找此表。如果是来自该函数的异常,内核不会 Panic,而是将 PC 指针跳转到修复段(Fixup code),该段代码负责清理现场并返回未拷贝的字节数。
🌻4. 涉及核心时序图
🌻5. 驱动实战 Demo
此 Demo 演示了在字符设备驱动中,如何通过上述全路径接收用户数据。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h> // 包含核心调用栈头文件
#include <linux/slab.h>
#define DRV_NAME "full_stack_copy"
static char *k_mem;
static ssize_t my_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
unsigned long res;
// 1. 动态限速/限容
if (count > 2048) count = 2048;
// 2. 内核空间准备
if (k_mem) kfree(k_mem);
k_mem = kzalloc(count + 1, GFP_KERNEL);
if (!k_mem) return -ENOMEM;
/*
* 3. 核心调用:依次通过 access_ok -> might_fault ->
* check_object_size -> raw_copy_from_user -> 汇编
*/
res = copy_from_user(k_buffer, buf, count);
if (res == 0) {
pr_info("%s: Successfully copied %zu bytes\n", DRV_NAME, count);
return count;
} else {
// 如果 res > 0,说明在 A 层触发了异常表跳转
pr_err("%s: Partial copy! %lu bytes failed. Potential bad address.\n", DRV_NAME, res);
return -EFAULT;
}
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.write = my_drv_write,
};
static int __init my_init(void) {
register_chrdev(242, DRV_NAME, &fops);
return 0;
}
static void __exit my_exit(void) {
unregister_chrdev(242, DRV_NAME);
kfree(k_mem);
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
🌻6. 用法总结
| 调用层级 | 核心职责 | 关键报错/结果 |
|---|---|---|
| 通用层 (Macro) | access_ok 地址合法性检查 | 若不合法直接返回 n |
| 内核安全层 | check_object_size 溢出检查 | 可能触发 BUG() 或告警 |
| 调试层 | might_fault 睡眠检查 | 在不可睡眠处调用会触发 Kernel Dump |
| 架构层 (ASM) | 指令级拷贝与异常锚点 | 通过 CPU 异常机制捕获非法访问 |
| 异常表层 | Fixup 代码恢复现场 | 确保内核不崩溃,仅返回错误 |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)