这样理解mmap,挺有意思!

大概雍正皇帝怎么也不会想到,自己在西历2022年的男生和女生眼里,会是截然不同的两种形象。

9822544567191a0ee87c2b4087731752.jpeg

1

以我对身边同学朋友的观察,男生们大多爱看《雍正王朝》,他们眼中的雍正,大约是个推行了“火耗归公”、“摊丁入亩”等遏制贪腐,减轻税收之类政策的改革家,是个经历了九子夺嫡的惊心动魄、腹黑深沉的政治家,是个登基后也兢兢业业,熬夜加班996的工作狂

而女生们大多爱看《甄嬛传》,她们眼里的雍正,是“大胖橘”,是“大猪蹄子”,是被后宫一众妃嫔玩弄于股掌之中,戴了N顶绿帽,最后还被钮钴禄甄嬛气死的渣男

5a9ce9021c605c415caa202086fae17f.png

我没完整的看过甄嬛传,但是有幸在吃饭的时候陪我家那位看过几集。

正所谓后宫佳丽三千人,铁杵磨成绣花针... (不是

妃嫔太多了,皇帝就一个,难免会互相争风吃醋。位份高的贵妃的仗势欺人,一些小妃嫔无法正面回击,自然会用点别的奇淫技巧,扎小人就是其中一个出现频率较高的方法。

据我总结,扎小人这个技术的核心思想是:用户这边由于无法扎到正主,只能拿个自己身边的布片等物品模拟一个小人出来,在上面画上正主的经筋脉络,写上名字,施以某种魔法,然后用针扎自己手边的小人的某个穴道,远程那位正主的对应部位就会受到同样的折磨。

f599c8693cc46045ed7f96b725572ba8.gif

虽然有点神乎其技,令人羡慕而不可得。但是在linux内核开发里面,却可以用mmap的机制实现类似的效果。

2

mmap的核心思想是:用户这边由于在用户态无法直接操作寄存器的物理地址,于是通过mmap方法进行内存映射,将物理地址映射到用户态的虚拟地址上,然后用户通过读写自己手边的虚拟地址,就可以实现对物理地址的读取/写入。

两者的共同点是,由于无法直接操作目标,所以通过某种方法,将自己能操作的事物和目标建立一种映射关系,从而达到如臂使指,指哪打哪,打哪哪疼的效果。

只要能建立起对目标的映射,我们借此映射能做什么文章,自然有很多想象空间。所以mmap有很多用途,有人用它来实现进程间通信,有人用它搬运数据,对于我们嵌入式工程师来说,我们可以用它来点灯。嘿嘿,想不到吧!

且听我慢慢道来。

2a19197cd91845f201ab26b39ea8ea34.jpeg

3

作为一个嵌入式工程师,花式点灯是必备技能。无论是写裸机代码操作GPIO口,还是通过物联网云端远程控制LED,从硬件的角度讲,核心原理都是找到连接LED的GPIO口,让它输出一个电信号。而从软件的角度讲,最终目的就是找到这个GPIO口对应寄存器的地址,根据实际的电路要求,让CPU给它写入一个1或者0。

裸机开发的时候,我们可以直接找到物理地址进行操作。而在Linux系统里却略有不同。因为在操作系统里有内核空间的存在,我们写的程序都是运行在用户态的,需要经过内核来对硬件进行驱动,无法直接操作物理地址。

你当然可以选择为这个LED写一个驱动,从而在用户空间通过read,write来操作它的状态。不过有些同学一听要写驱动,就想吟一首蜀道难来表达自己的望而却步。所以没了解过驱动的同学你也可以选择用一种更直接的方式:mmap

就好像你可以选择给贵妃下药来控制她,不过下药这种方式需要精通药理、掌控时机,成本较高,难度较大。只要能达到目的,有时候扎个小人或许更加经济适用。

我在上家公司的时候用ARM Cortex-A9芯片做过一个项目,开发过程大概是先和硬件同事约定好一个协议,然后我通过GPIO口的输入输出模拟出这个协议,通过它对寄存器进行读写配置,驱动硬件ADC采样,然后将采回来的数据通过DMA传输,最终到应用层进行分析处理。其中驱动GPIO口的部分就用了mmap。

项目很大,做了半年多,想完全讲明白也不现实,不过我们可以从驱动GPIO口这个点切入,体会一下软件驱动硬件中间这玄妙的过程。聪明的你一定可以举一反三。

4

作为一个软件工程师,拿到板子的时候,硬件工程师一般会给你一份文档,类似这样:

9d573ae1a136adb959d5e3a679a0812f.png

这个文档会指明,如果想操作这个GPIO口的话,你需要用GPIO外设的基地址加上偏移地址找到对应的寄存器地址,再用位操作给指定的bit写入命令。

不过我由于FPGA也会一些,所以我们公司里FPGA的Block Diagram都是我来建的。建好FPGA的硬件工程后做一下综合,从Address Map里就能看到我想用的GPIO口地址了。如图:

7f84e1a40a8f92d88d79a2373676a1d2.png

04d3005831f14c7a0bf7ce0618fedf51.png

无论怎样,你现在拿到这个所谓的硬件寄存器地址了,接下来我们就可以拿小人扎它了。

以上图我拿到的0x43C00000为例,这是寄存器的地址,那我能否直接在应用程序里把0x43C00000赋值给一个指针,然后对它进行读写呢?

在玩裸机的时候确实是这样的。但是上面说了,Linux系统有虚拟内存的存在,就不能这么做了。因为理论上我可以在系统里开100个进程,这100个进程里都有0x43C00000这个地址,那这100个地址哪个是真正的寄存器地址呢?可能都不是。因为进程里的0x43C00000是虚拟的,它真正对应的物理地址在哪里,没人知道。要想把虚拟地址和物理地址对应起来,就得用mmap进行内存映射。

5

mmap的函数接口定义如下:

void mmap(void addr,size_t length,int prot,int flags,int fd,off_t offset);

这里面参数比较多。其中addr一般指定为NULL,prot则用于设置映射区域的权限,比如是否可读可写;flags则用于指定是共享映射还是私有映射;而fd,offset,length这三个参数表示将fd对应的文件,从offset位置起,将长度为length的内容映射到进程的地址空间。

需要注意mmap的操作单元是页,即最后映射的offset参数必须是内存页大小的整数倍,而Linux系统内存页大小一般为4096字节。

一个我在程序中的调用示例:

#define AXI_GPIO_BASEADDR 0x43C00000 int memfd = open("/dev/mem", O_RDWR| O_SYNC); if (-1 == memfd) {   printf("Can't open /dev/mem\n");   return -1; 
} unsigned int* led_gpio =    (unsigned int*)(mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE,MAP_SHARED, memfd, AXI_GPIO_BASEADDR));

调用mmap后,我们拿到一个指针,通过这个指针对指向的地址做任何操作,对应的寄存器物理地址也会有相同的效果。于是我们将它循环赋值0101,相应的寄存器控制的GPIO口输出电信号,于是板卡上的灯成功的闪烁起来,类似奥特曼体力不支时的能量灯。

d524543cada053b5ca259bdd60c28ed1.gif

6

多说两句,除了用来操作GPIO/字符设备外,mmap还有个常用的场景是操作块设备。它和传统的用read,write的区别,最关键的是省一次拷贝

比如要读取磁盘上某个文件的数据,用read write的话,由于会涉及到系统调用,进程是无法直接访问内核的,所以在read系统调用返回前,内核需要将数据从内核复制到进程指定的buffer里。

5e2d41764f64c30273488a9004613011.png

但如果用mmap的话,那么这段数据会首先拷贝到内存中作为页缓存(即page cache)。用mmap将这段内存映射到用户空间,则进程可以通过指针直接读写page cache,不再需要多余的系统调用和内存拷贝。

4b5e3e583dcd2999a1411284fc9efcf2.png

不过虽然少了一次拷贝,但mmap会触发缺页中断(page fault),相比于内存拷贝而言,缺页中断的开销更大。所以性能而言mmap大部分情况下并不会比read/write要好。

说到页缓存,我在上家公司开发项目的时候,还被脏页延迟这玩意坑过。篇幅所限,页缓存涉及到的缺页中断,脏页,延迟写回,sync强制写回等内容,我们下次再详细聊聊。

7

好了,于是我们学会用mmap点亮一个灯了。想象一下接下来的场景:

你跟公司研发部最漂亮的女同事说,

“hi,领导那边给了我们组一个新任务,你写个驱动控制一下LED吧?”

“啊?驱动这么难,我不会啦”

“哦没事,那你用mmap吧”

“诶你怎么骂人呢!”

12b21b5d74c6c6e08fd2b4c73c41c293.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463957.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件开发中的11个系统思维定律

为什么80%的码农都做不了架构师?>>> http://sd.csdn.net/a/20101217/284119.html?1292550154 彼得圣吉在其著作《第五项修炼》中提到的系统思维定律同样适用于软件开发。 1. 今日的问题源于昨日的解决方案(Today’s problems come from yes…

为什么我对流程情有独钟?

写这个标题的原因是我有一个同事兼朋友,他的名字刚好和流程谐音,最近他刚离职回苏州工作,在球场下,他是我的良师益友,在球场上,他是我们可以信任的队友,我们不仅一次把比我们高大、速度比我们快…

基于boost asio实现的支持ssl的通用socket框架

情景分析现已存在一个可用稳定的异步客户端类http_client_base,该类基于boost asio实现了连接服务器,发送请求,获取响应和解析http数据等操作,该类的大致实现框架如下1classhttp_client_base 2{ 3public: 4 http_client_ba…

C#创建简单的验证码

首先&#xff0c;创建一个CLASS类&#xff0c;然后需要add Reference的方式添加 System.Drawing&#xff08;画画的类&#xff09; 方法代码如下&#xff1a; 1/**//**//**//// <summary> 2 /// 定义显示的随机字符 3 /// </summary> 4 /// &…

昨天的事情想说一下

发那篇文章的目的昨天发文章之后&#xff0c;我的一个好朋友微信找我&#xff0c;跟我说了很多关于文章的事情&#xff0c;所以&#xff0c;我自己也思考了许多。关于泄愤这个事情&#xff0c;我还是挺想说的。可能很多人看到了一个不好的东西&#xff0c;然后网上发发这个&…

Multidimensional Queries(二进制枚举+线段树+Educational Codeforces Round 56 (Rated for Div. 2))...

题目链接&#xff1a; https://codeforces.com/contest/1093/problem/G 题目&#xff1a; 题意&#xff1a; 在k维空间中有n个点&#xff0c;每次给你两种操作&#xff0c;一种是将某一个点的坐标改为另一个坐标&#xff0c;一种操作是查询[l,r]中曼哈顿距离最大的两个点的最大…

poj 3342

概率dp&#xff0c;不解释。 View Code #include<iostream>#include<map>#include<cstdio>#include<vector>using namespace std;const int maxn201;int dp[210][2];vector<int>edge[maxn];void dfs(int u,int p){int i,j; dp[u][1]1;dp[u][…

最全是一次I2C总结

博主将 I2C spec 文章总结为一篇&#xff0c;目录如下I2C Introduction I2C Architecture I2C Transfer I2C Synchronization And Arbitration I2C Hs-mode1、I2C Introduction1、I2C 历史I2C&#xff1a;Inter-Integrated Circuit&#xff0c;集成电路总线。I2C 是 Philips 公…

Lync Server 2010标准版系列PART6:启用Lync

在我们花费了众多的精力和时间之后&#xff0c;我们终于完成了Lync Server标准版的搭建&#xff0c;接下来当然是为我们AD中的用户启用Lync&#xff0c;来看下我们的部署成果。首先我们需要在AD中创建两个帐户&#xff0c;这样便于我们后期的测试&#xff0c;在DC上打开AD用户和…

8位MCU跑RTOS有没有意义?

相信大多数人在学习单片机的时候&#xff0c;都是从最基本的8位MCU开始的。一般来说&#xff0c;8位单片机最常见的是三个系列是&#xff1a;51系列、AVR系列、PIC系列。而前段时间&#xff0c;群里讨论了一个问题&#xff1a;在51单片机上跑RTOS有没有意义&#xff1f;关于这个…

ViewState机制由浅入深1

1 ViewState机制是什么&#xff1f; ViewState机制是asp.net中对同一个Page的多次请求&#xff08;PostBack&#xff09;之间维持Page及控件状态的一种机制。在WebForm中每次请求完&#xff0c;Page对象都会被释放&#xff0c;对同一个Page的多次请求之间的状态信息&am…

关于bc中小数点length,scale,(())以及进制转换

这是我在codewar上遇到的一个题&#xff0c;我用我自己的方法做出了解答&#xff0c;如下&#xff1a; 1 #!/bin/bash2 3 distanceecho "$1*10000"|bc|cut -d"." -f14 a05 n16 7 if [ $distance -le 0 ];then8 echo None9 else 10 while [ $n -lt $di…

5V串口接3.3V单片机串口怎么搞?

写在前面&#xff1a;两个单片机由于电平不同&#xff0c;串口通信可能会失败&#xff0c;这时候需要通过电平转换电路来解决&#xff0c;本文给出了两种方法&#xff0c;一种是通过三极管搭建&#xff0c;另一种是MOS管搭建&#xff0c;在硬件工程师的笔试中也经常会出现这样的…

Django之缓存、信号和图片验证码

一、 缓存 1、 介绍 缓存通俗来说&#xff1a;就是把数据先保存在某个地方&#xff0c;下次再读取的时候不用再去原位置读取&#xff0c;让访问速度更快。 缓存机制图解 2、Django中提供了6种缓存方式 1. 开发调试   2. 内存   3. 文件   4. 数据库   5. Memcache缓存&…

这焊接技术在班里排名第一没问题吧?

晚上和朋友讨论PCB LAYOUT&#xff0c;然后自己也动手起来了&#xff0c;刚好看到宇哥的一篇焊接的文章&#xff0c;这焊接技术这么厉害的人&#xff0c;layout那不得是吊炸天啊。作者&#xff1a;晓宇&#xff0c;排版&#xff1a;晓宇微信公众号&#xff1a;芯片之家&#xf…

Yocto,嵌入式开发者不可不知的强大工具【附资料与活动】

各个嵌入式开发团队出于不同的原因&#xff0c;都希望构建适合自己开发需求的嵌入式系统。Yocto正是这样一个工具&#xff0c;任何一个厂商都可以根据Yocto定制属于自己的系统。 Yocto 是什么 Yocto 是一种伞式项目&#xff0c;侧重于通过开放式嵌入内核&#xff0c;打造嵌入式…

第四次过程性考核

https://gitee.com/liuji1/fourth_process_assessment (一) 问题&#xff1a;使用套接写连接编写一个简单的聊天室程序&#xff0c;客户端主函数放在Client_Main.java文件中&#xff0c;服务器端主函数放在Server_Main.java文件中 (二) 要求&#xff1a; 1.客户端从控制台进行…

劝你不要转行

在知乎收到一个咨询&#xff0c;问题如下您好&#xff0c;想向您请教一些问题。想转行做嵌入式工程师个人基本信息29岁&#xff0c;电子与通信工程专业硕士&#xff0c;毕业后就职于某车企&#xff0c;主要从事类似项目管理一职。去年考入某事业单位&#xff0c;业余时间相对较…

linux下DHCP的安装配置

今天在整理以前的资料的时候&#xff0c;看到了这篇过去积攒的资料&#xff0c;过程详细所以拿来给大家一块分享&#xff0c;同时我也在做&#xff0c;提高一下熟练度。 【实验名称】Linux下DHCP服务的配置与安装【实验拓扑】【实验目标】了解DHCP服务的工作原理&#xff0c;掌…

浅谈一下嵌入式中的强符号和弱符号

__attribute__ 是一个编译器指令&#xff0c;其实是 GNU C 的一种机制&#xff0c;本质是一个编译器的指令&#xff0c;在声明的时候可以提供一些属性&#xff0c;在编译阶段起作用&#xff0c;来做多样化的错误检查和高级优化。用于在 C、C、Objective-C 中修饰变量、函数、参…