备忘:Linux内核编程的几个注意事项
虚拟地址转物理地址要用__pa
内核程序创建的一段地址连续的共享内存,通过内存映射可以让用户态进程存取。之前在RHEL/CentOS的x86_64架构上工作正常。后来在aarch64架构的银河麒麟(Linux内核版本为4.4.58)上总出现异常问题。
怀疑内存映射环节有问题。从https://elixir.bootlin.com/linux/v4.4.58/source/drivers/char/mem.c#L321上找到4.4.58内核版本的mmap_mem函数实现。与使用的代码相符。在该页面查找对mmap_mem的调用,发现如下代码段:
static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
{
unsigned long pfn; /* Turn a kernel-virtual address into a physical page frame */
pfn = __pa((u64)vma->vm_pgoff << PAGE_SHIFT) >> PAGE_SHIFT; /*
* RED-PEN: on some architectures there is more mapped memory than
* available in mem_map which pfn_valid checks for. Perhaps should add a
* new macro here.
*
* RED-PEN: vmalloc is not supported right now.
*/
if (!pfn_valid(pfn))
return -EIO; vma->vm_pgoff = pfn;
return mmap_mem(file, vma);
}
进而怀疑是物理地址错位引起的问题。检视代码,发现由虚拟地址转换为物理地址的代码如下:
g_ulPa = addr - PAGE_OFFSET;
由上面的__pa找到4.4.58上arm64平台的定义:
https://elixir.bootlin.com/linux/v4.4.58/source/arch/arm64/include/asm/memory.h#L147
#define __pa(x) __virt_to_phys((unsigned long)(x))
进而找到__virt_to_phys的定义:
https://elixir.bootlin.com/linux/v4.4.58/source/arch/arm64/include/asm/memory.h#L78
#define __virt_to_phys(x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET))
在此前支持的x86_64架构上,PHY_OFFSET总是为0,因此上面转换地址的代码是没有问题的。但在arm架构就有问题了。
进一步发现,__pa比__virt_to_phys更为通用。Linux支持的所有CPU架构都有__pa,而__virt_to_phys则不是。
因此,最终改动一行代码,问题得到解决:
g_ulPa = __pa(addr);
内核进程读写用户态进程内存要用copy_from_user和copy_to_user
内核进程通过创建一个proc文件,用户态进程通过这个文件下发指令给内核进程,以实现对内核数据的存取等功能。这就涉及内核进程对用户态进程内存的读写操作。最初的代码实现是直接读写,一开始也没碰到问题。后来在一些新型的服务器上,直接引发了系统卡死的问题,机器重启无法进入系统。
内核程序处理proc指令的函数接口示意如下:
int procCmdHandler(..., const u8* pBuff, int size, ...)
输入输出参数pBuff是指向用户态地址的指针,内核程序不可以直接读写这个指针指向的内容,否则会在CPU指令集做了保护增强的新型服务器上引发系统卡死的问题。正确的做法是:
1、内核态程序读取用户态指针指向的数据,需要使用copy_from_user函数
2、内核态程序更改用户态指针指向的数据,需要使用copy_to_user函数
最新文章
- [LintCode] Trapping Rain Water 收集雨水
- 基于MVC4+EasyUI的Web开发框架经验总结
- [Effective JavaScript 笔记]第51条:在类数组对象上复用通用的数组方法
- js 函数声明方式以及javascript的历史
- kuangbin_ShortPath P (HDU 4725)
- linux各种查看端口号
- 有关java.lang.UnsupportedClassVersionError: Unsupported major.minor version 51.0
- ubuntu 下配置Python wxWidgets (复制自官方网站)
- QT5控件-QPushButton和QFocusFrame(按钮和焦点框)
- extjs tree check 级联选择
- Git安装及基本使用
- 制作简易计算器处理过程Servlet
- NYoj 部分和问题(深搜经典)
- js基础01
- Kafka的特点及使用场景
- 强大核心功能矩阵,详解腾讯云负载均衡CLB高可靠高性能背后架构
- nginx代理部署Vue与React项目
- js中浅拷贝和深拷贝以及深拷贝的实现
- 【浅说】堆(heap)和栈(stack)区别
- 【js】利用闭包消除回调函数启动时值已经发生变化的影响
热门文章
- Matplotlib和Seaborn演示Python可视化
- 如何使用Git Flow 进行hotfix
- js 跨域请求失败
- ;~ 小部分AutoHotkey脚本源代码测试模板样板.ahk
- 京东购物小程序 | Taro3 项目分包实践
- 【算法学习笔记】动态规划与数据结构的结合,在树上做DP
- http request 请求拦截器,有token值则配置上token值
- C++ //继承同名成员处理方式
- 关于修改.net core webapi中null默认返回的状态码。
- Create Shortcut to Get Jar File Meta Information