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

1

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

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

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

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

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

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

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

2

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

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

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

且听我慢慢道来。

3

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

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

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

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

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

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

4

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

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

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

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

以上图我拿到的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口输出电信号,于是板卡上的灯成功的闪烁起来,类似奥特曼体力不支时的能量灯。

6

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

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

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

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

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

7

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

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

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

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

“哦没事,那你用mmap吧”

“诶你怎么骂人呢!”

这样理解mmap,挺有意思!相关推荐

  1. 程序猿有话说:计算机,学着挺有意思的,就是头冷

    程序猿,一个互联网高速发(cui)展(can)下的新生物种,不仅拥有较高的智商,还有无限的忍耐力,日常敲代码,加班已成为了常态,一个高薪与秃头并存的群体- 据说程序员的日常是这样的: 当然大家一提起程 ...

  2. 计算机系专用表情包,计算机学起来挺有意思的表情包 - 计算机学起来挺有意思的微信表情包 - 计算机学起来挺有意思的QQ表情包 - 发表情 fabiaoqing.com...

    这世界挺有意思的没意思的是我!(熊猫头)_挺有_没意思_熊猫_意思_世界表情 这世界挺有意思的没意思的是我(熊猫头)_挺有_没意思_熊猫_意思_世界表情 电子城轨通信计算机:快看!那个学会计的上吊了_ ...

  3. 深入理解mmap函数

    函数简介 在Linux操作系统中,进程是资源分配的基本单位.所以说每个进程间有各自独立的存储空间,但是在某些情况下各进程要相互配合来完成特定任务,这样就使得进程间通信变得非常必要.进程间通信方式有多种 ...

  4. 网易工作经验,这篇博文挺有意思的

    看到一网友发的网易工作经验,感觉挺有意思的,整理如下: 1.先做人,后做事;对事不对人. 人品做好了,自然好办事,事情出问题了,不要针对某人,应当分析问题,找出原因,在事后总结.防范,真相出来来,该承 ...

  5. 彻底理解mmap()

    彻底理解mmap() 原创 Holy_666 最后发布于2019-01-17 22:51:27 阅读数 5056 收藏 发布于2019-01-17 22:51:27 分类专栏: 服务器 版权声明:本文 ...

  6. 一句话,全国方言N种說法,挺有意思。

    一句话,全国方言N种說法,挺有意思. 北京话:"今儿爷就站这儿了,你丫动我一试试.别看你丫个儿不小,逼急了老子拿板砖hai(一声)你丫挺的!" 天津话:"近儿我揍赞借害儿 ...

  7. 医生和强盗,挺有意思

    QQ群里分享的,医生与强盗,挺有意思. 强盗通常只在晚上作案,医生可以全天候抢钱: 强盗风里来雨里去四处流窜,医生冬天暖夏天凉环境优雅: 你把钱交给强盗是为了活命,你为了活命而把钱交给医生: 强盗只能 ...

  8. 一篇挺有意思的文章(转自CSDN)

    今天,逛CSDN看到一篇文章,很有意思,乐意与大家分享.(开发者版本:你属于哪个版本的程序员?) 本文转载自:http://news.csdn.net/n/20080625/116964.html 国 ...

  9. 转载】泡MM与GOF的23种模式(看着挺有意思)

    [转载]泡MM与GOF的23种模式 1.FACTORY?追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说" ...

最新文章

  1. java读取C++结构体,类型转换
  2. 深度搜索剪枝——数的划分
  3. 2分钟 Docker 部署 SprinBoot 集成 Flowable 工作流引擎
  4. 计算机导论123出栈顺序,优·计算机导论复习提纲.doc
  5. 异步线程AsyncTask_2示例(07)
  6. mysql视图,总结
  7. n!的分解 soj 2666
  8. windows_server2012搭建iis并配置http重定向 iis转发
  9. IEquatable「T」和Equal详解
  10. python数据类型:序列(字符串,元组,列表,字典)
  11. 远程医疗作用_是远程医疗保健的未来
  12. c语言共有34种运算符,C语言运算符与表达式
  13. python滤波器处理数据的优点_使用Python对原始信号应用合适的butterworth滤波器
  14. oracle select from dual,代码中误用select xxx from dual案例一则
  15. windows和linux快捷键
  16. 用Castor 处理XML文档
  17. 使用FRP进行内网穿透的最佳实践
  18. 罗技驱动Logitech G HUB一直卡在初始加载界面无法进入的问题
  19. 记录通过台词搜索电影的工具
  20. 实现文本diff比较与展示

热门文章

  1. 单片机开发---ESP32S3移植NES模拟器(二)
  2. oracle 48101 block,oracle数据库的一次异常起停处理。
  3. 【shell命令】拆分、合并、排序、比较文件
  4. 11111111111
  5. 解决linux只有ens33和lo网卡的问题
  6. 十一月的Kemin,不是萧邦 2005
  7. IATF16949认证辅导,对顾客特殊要求进行评估和确定并包含在质量管理体系范围
  8. IIS应用程序池自动回收
  9. 计算机系统基础:bomb炸弹实验
  10. Java三大框架SSH面试题锦集