Linux内核put_user函数用法详解
Linux内核put_user的用法
put_user
是 Linux 内核中用于将内核空间的数据安全复制到用户空间的关键函数。它在系统调用、设备驱动等场景中广泛使用,确保内核数据能安全传递给用户态程序。以下是详细用法指南:
函数原型
1 | int put_user(type val, type __user *addr); |
- **
val
**:要写入的内核数据(标量类型:char
、int
、long
等)。 - **
addr
**:目标用户空间地址(需用__user
标记,用于静态检查)。 - 返回值:成功返回
0
;失败返回-EFAULT
(地址无效)。
使用场景
在需要向用户空间传递数据的场景中使用,例如:
- 系统调用实现(如
read
处理函数)。 - 设备驱动中的
ioctl
或read
方法。 - 内核模块与用户程序交互时。
使用步骤与示例
示例:将内核整型变量写入用户空间
1 |
|
关键注意事项
地址有效性检查:
put_user
内部自动检查用户地址是否有效(可写且在用户空间)。- 无需手动调用
access_ok()
,但复杂场景建议前置检查:1
2if (!access_ok(VERIFY_WRITE, addr, sizeof(int)))
return -EFAULT;
支持的数据类型:
- 支持基本标量类型(1/2/4/8 字节):
1
char, short, int, long, long long
- 不支持结构体/数组!需改用
copy_to_user()
。
- 支持基本标量类型(1/2/4/8 字节):
原子性:
- 可在中断上下文安全使用(不睡眠)。
错误处理:
- 必须检查返回值,失败时返回
-EFAULT
。
- 必须检查返回值,失败时返回
与 copy_to_user()
的区别
函数 | 适用场景 | 性能 |
---|---|---|
put_user(x, ptr) |
单个标量数据(如 int , char ) |
高效(内联汇编) |
copy_to_user() |
结构体、数组等大数据块 | 相对较慢 |
常见错误
未检查返回值:
1
put_user(data, addr); // 错误!未处理可能的失败
传递内核地址:
1
2
3int *kernel_ptr = kmalloc(...);
put_user(*kernel_ptr, user_ptr); // 正确:解引用后传值
put_user(kernel_ptr, user_ptr); // 错误!传递的是内核地址误用结构体:
1
2
3struct my_struct s;
put_user(s, user_ptr); // 错误!不支持结构体
copy_to_user(user_ptr, &s, sizeof(s)); // 正确
内部机制
put_user
通过内联汇编实现,核心步骤:
- 检查用户地址是否属于用户空间(非内核地址)。
- 触发页面错误异常处理(若地址未映射)。
- 通过特殊指令(如 x86 的
mov
)执行数据复制。 - 捕获错误并返回状态码。
总结
- 何时使用:向用户空间传递单个标量值(整数、字符等)。
- 必须操作:
- 包含头文件
<linux/uaccess.h>
。 - 检查返回值并处理
-EFAULT
。 - 确保目标地址用
__user
标记。
- 包含头文件
- 替代方案:复制大数据块时使用
copy_to_user()
。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Helloeuler!