虚拟地址转物理地址要用__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内核编程的几个注意事项的更多相关文章

  1. Linux内核编程规范与代码风格

    source: https://www.kernel.org/doc/html/latest/process/coding-style.html translated by trav, travmym ...

  2. 初探linux内核编程,参数传递以及模块间函数调用

    一.前言                                  我们一起从3个小例子来体验一下linux内核编程.如下: 1.内核编程之hello world 2.模块参数传递 3.模块间 ...

  3. Linux内核编程-0:来自内核的 HelloWorld

    Linux内核编程一直是我很想掌握的一个技能.如果问我为什么,我也说不上来. 也许是希望有一天自己的ID也出现在内核开发组的邮件列表里?或是内核发行文件的CREDITS文件上? 也许是吧.其实更多的, ...

  4. 宋宝华: Linux内核编程广泛使用的前向声明(Forward Declaration)

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者:宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 前向声明 编程定律 先强调一点:在一切可 ...

  5. linux内核编程入门 hello world

    注意: Makefile 文件的命名注意M需要大写,否则会报错. 在Makefile文件中make命令前应为tab制表符. 下文转载至:https://blog.csdn.net/bingqing07 ...

  6. linux内核编程入门--系统调用监控文件访问

    参考的资料: hello world   https://www.cnblogs.com/bitor/p/9608725.html linux内核监控模块--系统调用的截获  https://www. ...

  7. linux内核编程笔记【原创】

    以下为本人学习笔记,如有转载请注明出处,谢谢 DEFINE_MUTEX(buzzer_mutex); mutex_lock(&buzzer_mutex); mutex_unlock(& ...

  8. Linux内核编程、调试技巧小集

    1. 内核中通过lookup_symbol_name获取函数名称 内核中很多结构体成员是函数,有时可能比较复杂不知道具体使用哪一个函数.这是可以通过lookup_symbol_name来获取符号表名称 ...

  9. Linux内核编程、调试技巧小集【转】

    转自:https://www.cnblogs.com/arnoldlu/p/7152488.html 1. 内核中通过lookup_symbol_name获取函数名称 内核中很多结构体成员是函数,有时 ...

随机推荐

  1. vulnhub-DC:2靶机渗透记录

    准备工作 在vulnhub官网下载DC:1靶机https://www.vulnhub.com/entry/dc-2,311/ 导入到vmware 打开kali准备进行渗透(ip:192.168.200 ...

  2. etcd学习(4)-centos7中部署etcd

    etcd的搭建 前言 单机 集群 创建etcd配置文件 更新etcd系统默认配置 启动 配置ETCD为启动服务 测试下 参考 etcd的搭建 前言 这里记录下如何搭建etcd 单机 在etcd的rel ...

  3. 为什么要学习Netty?

    一.传统的BIO编程 ​ 网络编程的基本模型是 Client/Server 模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的 IP 地址和监听端口),客户端通过连接操作向服务端监听 ...

  4. 嵌套div的onClick事件问题

    嵌套div的onClick事件问题我在下面的代码中的外层div中加了onClick事件,这样当鼠标点击这个div的时候就会跳转了.但是我在图片上加了一些其他效果,所以当鼠标点击中间的img时不能触发跳 ...

  5. 🔥 LeetCode 热题 HOT 100(41-50)

    102. 二叉树的层序遍历 思路:使用队列. /** * Definition for a binary tree node. * public class TreeNode { * int val; ...

  6. Python RPC 不会?不妨看看这篇文章

    1. 前言 大家好,我是安果! RPC,全程为 Remote Procedure Call,是一种进程间的通信方式,它采用「 服务端 / 客户机 」模式,是一种请求响应模型 其中,服务端负责提供服务程 ...

  7. ACM金牌选手讲解LeetCode算法《哈希》

    大家好,我是编程熊. 往期文章介绍了<线性表>中的数组.链表.栈.队列,以及单调栈和滑动窗口. ACM金牌选手讲解LeetCode算法<线性表> ACM金牌选手讲解LeetCo ...

  8. SpringBoot数据访问之整合Mybatis配置文件

    环境搭建以及前置知识回顾 SpringBoot中有两种start的形式: 官方:spring-boot-starter-* 第三方:*-spring-boot-starter Mybatis属于第三方 ...

  9. 5年Android开发诉苦:47天21家面试,半年空档期觉得整个人生都被毁了

    近日,我在逛某社交论坛时,发现一位做了五年的Android开发将自己这段时间的所有面试经历发表了出来,根据网友自己提供的信息显示,主要面试的地点都在北京,上海等地. 微软和亚马逊刚面试完一面,都是以算 ...

  10. PDL语言/ 盒图N-S/ PAD图

    PDL语言 伪码伪代码 基本语法 算法用Begin开始,以End结束(如果只表示中间部分的算法可以不要) 每一条指令,占一行.指令的结束不用任何符号 注释 用"//"表示 用Pri ...