资讯专栏INFORMATION COLUMN

嵌入式编程中应如何使用 mmap 访问 CPU 寄存器

dcr309duan / 2493人阅读

摘要:本文地址详解驱动虚拟地址和物理地址的映射嵌入式应用程序如何读取修改芯片寄存器的值和大家都知道,是用来做内存映射的,可以将一个文件描述符映射到内存当中,实现对该文件描述符的直接读写。

之前忘了在哪了,看到一个面试题:在 Linux 中如果不允许你写内核驱动,但是要访问内核寄存器,那应该怎么做?
答案就是使用 mmap() 系统调用,搭配 Linux 的一个设备节点 /dev/mem

本文地址:https://segmentfault.com/a/1190000008381626


Reference

/dev/mem
mmap详解
Linux驱动虚拟地址和物理地址的映射
嵌入式 Linux应用程序如何读取(修改)芯片寄存器的值
file - C - Bus error when using mmap - Stack Overflow

mmap() 和 /dev/mem

mmap() 大家都知道,是用来做内存映射的,可以将一个文件描述符映射到内存当中,实现对该文件描述符的直接读写。

/dev/mem 则关注的人比较少。这个设备节点在 Linux 中都有,是物理内存的完整映像。这个厉害了,如果你访问这个节点,实际可以这么说:你就取得了对整个系统的最高权限。在用户空间中实现设备驱动,实际上就是通过这个节点实现的。

很多跑 Linux 的芯片,会将自己的寄存器映射到内存空间的一个段中。这些相对于 /dev/mem 来说就是绝对地址上的一段内存空间而已,因此通过 mmap 对应的地址段,就可以实现对指定寄存器的访问。

Bus error

道理是这么讲,但是实际上写代码,直接尝试去映射 /dev/mem 的时候,内核却直接抛出 “Bus error” 的错误信息,把我的程序中止了。查阅资料,才知道很重要的一个信息:内存映射必须以页对齐

这个时候就需要用 getpagesize() 来获取页的大小啦。思路就是:当上层调用需要获取某段地址的时候,程序首先找到其对应的段起始地址,mmap 了之后再找偏移。

代码

废话少说,直接上代码(很短):

static int read_global_mem(void *out, size_t length, off_t offset)
{
        uint8_t *data = NULL;
        int ret = -1;
        off_t pageSize = getpagesize();
        size_t actualLen = 0;
        off_t actualOffset = 0;

        if (NULL == out || 0 == length) {
                errno = EINVAL;
                goto ENDS;
        }

        int fd = open("/dev/mem", O_RDWR | O_SYNC);
        if (fd < 0) {
                goto ENDS;
        }

        actualOffset = offset & ~(pageSize - 1);           // 找到对应的段起始
        actualLen    = length + (offset - actualOffset);   // 判断实际需要 mmap 的长度

        data = mmap(NULL, actualLen, PROT_READ, MAP_SHARED, fd, actualOffset);
        if (NULL == data) {
                goto ENDS;
        }

        memmove(out, data + (offset - actualOffset), length);

        /* success */
        ret = 0;
ENDS:
        if (fd) {
                close(fd);
                fd = -1;
        }
        if (data) {
                munmap(data, length);
                data = NULL;
        }
        return ret;
}

寄存器写

上面的代码是关于寄存器读的。寄存器写的代码基本上是差不多的,不同的有几个:

mmap() 的时候,改成 “PROT_READ | PROT_WRITE

在共享内存中修改了指定的数据段之后,调用 msync() 将被修改了的寄存器值写回内核

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/9760.html

相关文章

  • java性能优化-关于cache

    摘要:前面了解存储结构对性能优化是非常关键的,不管是数据库,消息中间件,负载均衡器等性能优化的道理都是相通的,比如说性能优化,那么我们也需要从内部的存储和体系结构出发,分析树,块缓存,算法,逻辑读,,分析统计数据等,在分析逻辑读的时候需要考虑访问 前面 了解存储结构对性能优化是非常关键的,不管是数据库,消息中间件,负载均衡器,api gateway等性能优化的道理都是相通的,比如说Oracl...

    testbird 评论0 收藏0
  • 【Nginx源码分析】Nginx中的锁与原子操作

    摘要:源码目录下等文件针对不同操作系统实现了若干原子性操作函数。函数最后返回标志寄存器位。总结本文简要介绍了中锁的实现原理,多核高速缓存冲突问题,内联汇编简单语法,以及原子比较交换操作和原子累加操作的实现。 李乐 问题引入 多线程或者多进程程序访问同一个变量时,需要加锁才能实现变量的互斥访问,否则结果可能是无法预期的,即存在并发问题。解决并发问题通常有两种方案:1)加锁:访问变量之前加锁,只...

    aboutU 评论0 收藏0
  • linux进程地址空间布局浅析

    摘要:堆栈的用途分三种为函数内部声明的局部变量非静态局部变量分配内存空间。代码段通常属于只读,以防止其他程序意外地修改其指令对该段的写操作将导致段错误。 此文章是围绕该文章 的思路进行总结的,原文记录的笔记详尽,在此基础上,我总结了自己的一下(想对浅显)。如果对具体细节想要了解的,可以访问这篇文章,在文章最后付了一段代码,跟该成程序的内存映射图,如果有哪里写的不正确,欢迎补充和指正。 程序是...

    Gilbertat 评论0 收藏0
  • 【PHP7源码分析】PHP内存管理

    摘要:分页管理先说说虚拟内存的概念。每个存在的虚拟页面都保存在某个区域中,不属于任何一个区域的虚拟页是不存在的,不能被进程使用内核为系统中的每个进程维护一个单独的任务结构,任务中的一个字段指向,他描述了虚拟内存的当前状态。 作者: 顺风车运营研发团队 李乐 第一章 从操作系统内存管理说起 程序是代码和数据的集合,进程是运行着的程序;操作系统需要为进程分配内存;进程运行完毕需要释放内存;内存管...

    waltr 评论0 收藏0
  • 【腾讯开源】iOS爆内存问题解决方案-OOMDetector组件

    摘要:内存泄漏检测除了爆内存堆栈监控,还集成了内存泄漏检测功能,能够检测内存块和对象的无主内存泄漏。所谓无主内存泄漏是指内存块在进程内已经没有引用却无法正常释放的内存块。 组件介绍 OOMDetector是手Q自研的IOS内存监控组件,腾讯内部目前已有多个App接入了OOMDetector,它主要有以下两个功能: 爆内存堆栈统计:负责记录进程内存分配堆栈和内存块大小,在爆内存时Dump堆栈...

    liaosilzu2007 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<