备忘: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函数
备忘:Linux内核编程的几个注意事项的更多相关文章
- Linux内核编程规范与代码风格
source: https://www.kernel.org/doc/html/latest/process/coding-style.html translated by trav, travmym ...
- 初探linux内核编程,参数传递以及模块间函数调用
一.前言 我们一起从3个小例子来体验一下linux内核编程.如下: 1.内核编程之hello world 2.模块参数传递 3.模块间 ...
- Linux内核编程-0:来自内核的 HelloWorld
Linux内核编程一直是我很想掌握的一个技能.如果问我为什么,我也说不上来. 也许是希望有一天自己的ID也出现在内核开发组的邮件列表里?或是内核发行文件的CREDITS文件上? 也许是吧.其实更多的, ...
- 宋宝华: Linux内核编程广泛使用的前向声明(Forward Declaration)
本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者:宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 前向声明 编程定律 先强调一点:在一切可 ...
- linux内核编程入门 hello world
注意: Makefile 文件的命名注意M需要大写,否则会报错. 在Makefile文件中make命令前应为tab制表符. 下文转载至:https://blog.csdn.net/bingqing07 ...
- linux内核编程入门--系统调用监控文件访问
参考的资料: hello world https://www.cnblogs.com/bitor/p/9608725.html linux内核监控模块--系统调用的截获 https://www. ...
- linux内核编程笔记【原创】
以下为本人学习笔记,如有转载请注明出处,谢谢 DEFINE_MUTEX(buzzer_mutex); mutex_lock(&buzzer_mutex); mutex_unlock(& ...
- Linux内核编程、调试技巧小集
1. 内核中通过lookup_symbol_name获取函数名称 内核中很多结构体成员是函数,有时可能比较复杂不知道具体使用哪一个函数.这是可以通过lookup_symbol_name来获取符号表名称 ...
- Linux内核编程、调试技巧小集【转】
转自:https://www.cnblogs.com/arnoldlu/p/7152488.html 1. 内核中通过lookup_symbol_name获取函数名称 内核中很多结构体成员是函数,有时 ...
随机推荐
- tomcat默认端口
关于tomcat默认端口为8080: 网页浏览器的默认端口为80.
- java中的集合类学习(三)
JAVA中有许多的集合,常用的有List,Set,Queue,Map. 1.其中List,Set,Queue都是Collection(集合),其每个元素都是单独的一个对象,如List<Strin ...
- Skywalking-04:扩展Metric监控信息
扩展 Metric 监控信息 官方文档 Source and Scope extension for new metrics 案例:JVM Thread 增加 Metrics 修改 Thread 的定 ...
- maven 工程构建 之_____<dependencyManagement>标签
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...
- Intouch/ifix语音报警系统制作(4-自动发送邮件提醒)
在近期项目完成后,有遇到情况:类似于语音报警后,中控室人员未及时报告给我们造成了事件的危害升级,以及造成很不好的影响.针对这个情况特此添加语音报警后,自动发送邮件提醒,完善现有的报警机制. 1.函数编 ...
- 自动部署Springboot项目脚本小脚本
#!/bin/bash echo '自动部署Springboot项目脚本...' # aaa.jar 项目jar包 pid=`ps -ef|grep aaa.jar|grep -v grep|grep ...
- thinkphp 初始化
public function _initialize(){ //$top_img = M('adv')->where(array('adv_id'=>1057))->find(); ...
- 2020年度钻石C++C学习笔记(1)《博学谷》
1.C语言概述 1.1 什么是C语言 一提到语言这个词语,自然会想到的是像英语.汉语等这样的自然语言,因为它是人和人交换信息不可缺少的工具. 而今天计算机遍布了我们生活的每一个角落,除了人和人的相互交 ...
- MFC对文件文件夹转移、删除、重命名、复制【转】
HFileOperation()函数主要对文件夹有四种操作:复制,删除,移动,重命名. 写了四个函数.可以很好的对文件夹进行操作. //函数名:MoveFolder //参数:lpszFromPath ...
- Wireshark过滤器详解
Wireshark过滤器详解 1.Wireshark主要提供两种主要的过滤器 捕获过滤器:当进行数据包捕获时,只有那些满足给定的包含/排除表达式的数据包会被捕获 显示过滤器:该过滤器根据指定的表达式用 ...