这样理解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防止恶意空连接的方法

转载:http://blog.csdn.net/educast/article/details/13167847 网络服务器通常要应对一些意外情况,如空连接行为,指在遇到客户端连接后不进行任何操作,并很可能在大量空连接情况下导致服务器资源耗尽而无法工作。以下代码主要工作…

赢在中国 (2008-3-19)

赢在中国又换在10点,前面几分钟没有看到。虽然我一直不看好32号老董,但是我还是很好奇看看他是怎么样让自己又一次成为众人的对立面的。 我觉得32号和04号都不是心胸宽广之辈,但至少04号比32号真诚,只是他还没有修炼到32号的厚厚的…

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

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

UVa-401-Palindromes(回文)

这一题的话我们可以把映像字符的内容给放入一个字符串常量里面,然后开辟一个二维的字符串常量数组,里面放置答案。 对于回文实际上是很好求的,对于镜像的话,我们写一个返回char的函数,让它接收一个char。 接收之后进行…

C#中获取当前时间:System.DateTime.Now.ToString()用法

//2008年4月24日System.DateTime.Now.ToString("D");//2008-4-24System.DateTime.Now.ToString("d"); //2008年4月24日 16:30:15System.DateTime.Now.ToString("F");//2008年4月24日 16:30System.DateTime.Now.ToString("f"); //2008-…

基于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 公…

Interesting Finds: 2008.03.24

.NET: C#正则表达式整理备忘 谈谈volatile变量 Other: PTOM: The Open Closed Principle Calculating pi with C# - Calculate PI using C# Regular Expression Workbench - a free and opensource option for RegexBuddy 转载于:https://www.cnblogs.com/gOODiDEA/archive/2…

如何利用openSsl来计算一个文件的md5值?

openssl环境的配置&#xff0c; 我就不再说了。在本文中&#xff0c; 我们来讨论一个文件的md5值&#xff0c; 废话少说&#xff0c; 直接给大家代码&#xff0c; 上点干货&#xff1a; [cpp] view plaincopy #include <iostream> #include <openssl/md5.h> // 如…

qs文档翻译

安装&#xff1a; npm install qs --save-dev 基本用法&#xff1a; 1 let qs require(qs);2 let assert require(assert);3 4 5 //qs.parse(ac)将字符串等式转换为对象6 let obj qs.parse(ac);7 console.log(obj)//{a:c}8 9 //qs.stringify(obj)将对象转化为字符串等式 10 …

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…

利用openssl来计算sha1, sha224, sha256, sha384, sha512

转载&#xff1a;http://blog.csdn.net/stpeace/article/details/42371079 利用openssl来计算sha1, sha224, sha256, sha384, sha512&#xff0c;前提是已经配置了openssl的环境&#xff1a; 代码如下&#xff1a; [cpp] view plaincopy #include <iostream> #include…