当前位置: 首页 > news >正文

D2000平台上Centos使用mmap函数遇到的陷阱

----------原创不易,欢迎点赞收藏。广交嵌入式开发的朋友,讨论技术和产品-------------

在飞腾D2000平台上,安装了麒麟linux系统,我写了个GPIO点灯的程序,在应用层利用mmap函数将内核空间映射到用户态,然后直接操作GPIO方向和数据寄存器即可控制LED管脚的电压,实现闪灯功能。

核心代码如下,利用/dev/mem设备,内核将硬件地址空间映射到用户态空间

    if ((fd_mem = open("/dev/mem", O_RDWR, 0755)) < 0){printf("\r\n open /dev/mem err: %d-%s. \n", errno, strerror(errno));return 0;}mapped_gpio = (volatile unsigned char *)mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd_mem, GPIO1_BASE_ADDR);if (tmp == (void*)-1){	printf("mmap error : %d-%s. \n", errno, strerror(errno));close(fd_mem);return 1;}

然后交叉编译后,将目标二进制文件上传到单板上运行,闪灯正常。

于是乎把这源码和可执行文件发给客户使用,结果报错。打印如下信息,提示非法参数
在这里插入图片描述
我将mmap的几个参数都研究了一下:
1、应该不是权限问题,对方用的是root账户执行的;
2、查看了单板上内核空间,看是否有GPIO物理地址的定义
#define GPIO1_BASE_ADDR 0x28005000 //PHYTIUM D2000 GPIO
cat /proc/iomem 执行结果如下,有0x28005000的空间
在这里插入图片描述
3、PROT_READ|PROT_WRITE这两个参数应该没有问题;
4、想到重要的一个因素,mmap必须以页表大小对齐的起始地址,才能做映射。于是查看当前系统的页表大小,用如下命令:getconf PAGESIZE。
在这里插入图片描述
在麒麟系统查到结果是4KB的小页面,在centos下查询得到的是65536,64KB的页面。这个参数在内核编译的时候可以设定的。肯定是centos没有设定小页面模式。关于MMU和内存页的知识,本文不叙述。有兴趣的朋友自己去研究学习。

于是乎将代码修改了一下,考虑不管多大页面都能兼容

    int pagesize;u32 mmap_base;u32 mmap_offset;volatile unsigned char *tmp;pagesize = sysconf(_SC_PAGESIZE);if (pagesize == -1) {printf("Error, sysconf for _SC_PAGESIZE \n");return 1;} else {printf("Page size: %d bytes \n", pagesize);}mmap_base   = GPIO1_BASE_ADDR & ~(pagesize - 1);mmap_offset = GPIO1_BASE_ADDR & (pagesize - 1);printf("Hardware address = 0x%x,  mmap_base = 0x%x, mmap_offset = 0x%x \n", GPIO1_BASE_ADDR, mmap_base, mmap_offset);if ((fd_mem = open("/dev/mem", O_RDWR, 0755)) < 0){printf("\r\n open /dev/mem err: %d-%s. \n", errno, strerror(errno));return 0;}tmp = (volatile unsigned char *)mmap(0, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd_mem, mmap_base);if (tmp == (void*)-1){	printf("mmap error : %d-%s. \n", errno, strerror(errno));close(fd_mem);return 1;}mapped_gpio = (volatile unsigned int *)((unsigned long)tmp + mmap_offset);printf(" mapped_base = %p, mapped_gpio = %p \n", tmp, mapped_gpio);printf("GPIO_SWPORTA_DR  = 0x%x \n",    *mapped_gpio);    //GPIO_SWPORTA_DRprintf("GPIO_SWPORTA_DDR = 0x%x \n\n",  *(mapped_gpio+1)); //GPIO_SWPORTA_DDR

重新编译后在多个版本上验证可行。当初写这个功能程序,太匆忙,没有考虑太细致。不过也很快定位解决了。这也告诫搞研发的朋友,虽然同一套代码不修改,在不同环境上也不一定成功执行。陷阱无处不在,大家谨慎小写,写出健壮可靠的代码。

相关文章:

  • 多模态大语言模型arxiv论文略读(九十六)
  • MCU与CPU时钟概念详解:从基础到面试高频问题
  • 嵌入式学习笔记 - 新版Keil软件模拟时钟Xtal灰色不可更改的问题
  • 回归算法模型之线性回归
  • 【Qt开发】布局管理器
  • 《活法》
  • 沉浸式 VR 汽车之旅:汽车虚拟展厅与震撼试驾体验
  • 途景VR智拍APP:开启沉浸式VR拍摄体验
  • ASP.NET Core OData 实践——Lesson6使用Action(C#)
  • 【Mysql开启慢查询日志】
  • 常见的C语言段错误实例及原因分析
  • C++代码随想录刷题知识分享-----三数之和(3Sum)全解:双指针 + 去重技巧一网打尽
  • Python 的 `next()`函数
  • 文档整合自动化
  • 深入探讨集合与数组转换方法
  • 解锁5月游戏新体验 高速电脑配置推荐
  • uni-app学习笔记十五-vue3页面生命周期(二)
  • C# Socket对象创建方式详解
  • Microsoft的在word中选择文档中的所有表格进行字体和格式的调整时的解决方案
  • leetcode 3372. 连接两棵树后最大目标节点数目 I
  • 党建网站建设技术方案/百度网址
  • wordpress聊天室/seo快速排名百度首页
  • 便捷的大连网站建设/域名大全查询
  • wordpress用户发文/搜索引擎排名优化方案
  • 如何建立网站快捷方式到桌面/seo做什么网站赚钱
  • 做网站一般长宽多少/宁波seo网络推广外包报价