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

网站开发与设计500强公司wordpress主题分类目录主题

网站开发与设计500强公司,wordpress主题分类目录主题,深圳高端写字楼,宝山手机网站制作公司14 地址映射 1、地址划分2、相关函数2.1 ioremap/iounmap2.2 mmap地址映射 3、总结 1、地址划分 明确#xff1a;在linux系统中,不管是应用程序还是驱动程序#xff0c;都不允许直接访问外设的物理地址,要想访问必须将物理地址映射到用户虚拟地址或者内核虚拟地址#xff0… 14 地址映射 1、地址划分2、相关函数2.1 ioremap/iounmap2.2 mmap地址映射 3、总结 1、地址划分 明确在linux系统中,不管是应用程序还是驱动程序都不允许直接访问外设的物理地址,要想访问必须将物理地址映射到用户虚拟地址或者内核虚拟地址一旦映射完毕,应用或者内核程序访问映射的虚拟地址就是在访问实际的物理地址在linux系统中,4G虚拟地址空间的划分 用户虚拟地址范围0x00000000~0xBFFFFFFF 内核虚拟地址范围0xC0000000~0xFFFFFFFF如何将物理地址映射到内核虚拟地址- 利用ioremap函数如何将物理地址映射到用户虚拟地址- 利用mmap一个物理地址可以有多个虚拟地址一个虚拟地址不能有多个物理地址 2、相关函数 2.1 ioremap/iounmap void *ioremap(unsigned long phy_address, unsigned long len) - 功能将物理地址映射到内核虚拟地址 - 参数- phy_address:传递要映射的起始的物理地址- len:传递要映射的物理地址空间的大小 - 返回值返回映射的起始内核虚拟地址 void iounmap(void *vir_address) - 功能解除物理地址和内核虚拟地址的映射关系 - 参数vir_address:传递映射好的起始内核虚拟地址案例使用地址映射操作gpio 寄存器 物理地址 内核虚拟地址 GPIOCALTFN0 0xC001C020 gpiocaltfn0 GPIOCOUTENB 0xC001C004 gpiocoutenb GPIOCOUTENB 0xC001C000 gpiocout //地址映射,两种方案 方案1unsigned long *gpiocout, *gpiocoutenb, *gpiocaltfn0;gpiocout ioremap(0xC001C000, 4)gpiocoutenb ioremap(0xC001C004, 4)gpiocaltfn0 ioremap(0xC001C020, 4)方案2由于寄存器的物理地址空间都是连续的,所以连续映射void *gpiobase;unsigned long *gpiocout, *gpiocoutenb, *gpiocaltfn0;gpiobase ioremap(0xC001C000, 0x24);//地址换算gpiocout (unsigned long *)(gpiobase 0x00);gpiocoutenb (unsigned long *)(gpiobase 0x04);gpiocaltfn0 (unsigned long *)(gpiobase 0x20);//配置模式 *gpiocaltfn0 ~(3 24); *gpiocaltfn0 | (1 24);//使能 *gpiocoutenb | (1 12);//输出 *gpiocout | (1 12); *gpiocout ~(1 12);案例实现一个软件能够编辑处理器任意一个寄存器 驱动代码 #include linux/init.h #include linux/module.h #include linux/fs.h #include linux/miscdevice.h #include linux/io.h //ioremap/iounmap #include linux/uaccess.h //描述寄存器操作信息的结构体 struct reg_info {unsigned long phy_address; //寄存器物理地址unsigned long data; //寄存器数据 };#define REG_WRITE 0x100001 //写寄存器命令 #define REG_READ 0x100002 //读寄存器命令 static long reg_ioctl(struct file *file,unsigned int cmd,unsigned long buf) {unsigned long *gpiobase;struct reg_info kreg;copy_from_user(kreg,(struct reg_info *)buf,sizeof(kreg));//结果kreg.phy_address物理地址,kreg.data?//将寄存器物理地址映射到内核虚拟地址gpiobase ioremap(kreg.phy_address, 4);if(gpiobase NULL) {printk(ioremap failed.\n);return -1;}switch(cmd) {case REG_WRITE:*gpiobase kreg.data;break;case REG_READ:kreg.data *gpiobase;copy_to_user((struct reg_info *)buf,kreg, sizeof(kreg));break;}//解除地址映射iounmap(gpiobase);return 0; } static struct file_operations reg_fops {.unlocked_ioctl reg_ioctl }; static struct miscdevice reg_misc {.name reg,.minor MISC_DYNAMIC_MINOR,.fops reg_fops }; static int reg_init(void) {misc_register(reg_misc);return 0; }; static void reg_exit(void){misc_deregister(reg_misc); };module_init(reg_init); module_exit(reg_exit); MODULE_LICENSE(GPL);应用程序 #include stdio.h #include sys/types.h #include sys/stat.h #include fcntl.h struct reg_info {unsigned long phy_address; //寄存器物理地址unsigned long data; //寄存器数据 }; #define REG_WRITE 0x100001 //写寄存器命令 #define REG_READ 0x100002 //读寄存器命令 int main(int argc, char *argv[]) {int fd;struct reg_info reg; //暂存寄存器的操作信息if((argc ! 3) (argc ! 4)) {printf(Usage: %s w address data\n, argv[0]);printf(%s r address\n, argv[0]);return -1;}fd open(/dev/reg, O_RDWR);if(fd 0) {printf(open reg test device failed.\n);return -1;}if(!strcmp(argv[1], w)) {reg.phy_address strtoul(argv[2], NULL, 0);reg.data strtoul(argv[3], NULL, 0);ioctl(fd, REG_WRITE, reg);} else if(!strcmp(argv[1], r)) {reg.phy_address strtoul(argv[2], NULL, 0);ioctl(fd, REG_READ, reg);printf(%#x:%#x\n,reg.phy_address, reg.data);}close(fd);return 0; }测试 ./btn_test w 0xC001C000 0xC84 # 开灯 ./btn_test w 0xC001C000 0x1C84 # 关灯 字符串转整形标准C库函数strtoul unsigned long strtoul(char *pstr, char **ppstr, int n); - 功能字符串转整形,例如:100-100, 0100-0100, 0x100-0x100 - 参数 - pstr: 传递转换的字符串首地址- ppstr:记录字符串中非整形数的子字符串首地址- n:指定转换进制 - 返回值返回转换的整形数例如 char *pstr 100; int a strtoul(pstr, NULL, 0); //结果a100 char *pstr 0100; int a strtoul(pstr, NULL, 0); //结果a0100 char *pstr 0x100; int a strtoul(pstr, NULL, 0); //结果a0x100 char *pstr 100; int a strtoul(pstr, NULL, 16); //结果a0x64 char *pstr 100abcd; char *pstr1 NULL; int a strtoul(pstr, pstr1, 0); //结果a100,pstr1 abcd2.2 mmap地址映射 mmap就是完成物理地址映射到用户虚拟地址用的 用户3G虚拟地址空间划分 0x00000000-------------------------------------------------------0xBFFFFFFF代码段 数据段 BSS段 堆区 MMAP虚拟内存区 栈区---- ------ ----系统调用函数原型 void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); - 功能将物理地址空间映射到用户虚拟内存空间上 - 参数- addr:给NULL,让linux内核帮你在用户虚拟内存区域找一块空间内存用来映射物理地址- length让linux内核帮你找空闲用户虚拟内存的大小;切记大小必须是页面大小(4KB)的整数倍- prot:描述内核帮你找的空闲用户虚拟内存的访问权限一般指定为PROT_READ|PROT_WRITE- flags其余属性,一般指定为MAP_SHARED- fd:硬件外设- offset:偏移量,一般给0 - 返回值linux内核将空闲的用户虚拟内存的首地址进行返回,这个起始用户虚拟地址同样也是4KB整数倍 参考代码 void *addr; int fd open(a.txt, O_RDWR); addr mmap(NULL, 0x1000, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0); 说明将文件a.txt映射到以addr起始的用户虚拟内存上,将来访问映射的用户虚拟内存就是访问文件 //向映射的用户虚拟内存拷贝字符串数据 本质是向文件a.txt写入数据 memcpy(addr, hello,world, 12);了解mmap系统调用函数所做的工作 应用程序调用mmap,首先跑到C库的mmap函数定义C库的mmap函数作两件事 1.保存mmap系统调用号到r7寄存器 2.调用swi/svc指令触发软中断异常一旦触发软中断异常,CPU核立马处理软中断异常 CPU核硬件自动做四件事:… 软件进一步处理软中断异常(在内核空间完成)最后进程跑到内核空间继续运行,跑到软中断异常的入口地址运行,做三件事 1.保护现场 2.调用软中断异常处理函数,而此函数又做两件事 1.从r7寄存器中取出mmap系统调用号 2.以mmap系统调用号为下标在内核的系统调用表中找到mmap对应的内核函数sys_mmap,而内核的sys_mmap做三件事 1. 内核的sys_mmap首先在当前进程的3G虚拟地址空间中找一块空闲的用户虚拟内存,将来用于和物理地址做映射 2. 一旦找到空闲的用户虚拟内存,并且用户mmap本身也给用户虚拟内存指定了一堆的属性(大小,权限等)所以内核用struct vm_area_struct数据结构定义初始化一个对象来描述空闲的用户虚拟内存的属性 3. 最后内核的sys_mmap调用底层驱动的mmap接口,并且内核sys_mmap将第2步创建的对象的地址也传递给底层驱动的mmap接口 3.底层驱动mmap执行完毕,然后恢复现场状态恢复和跳转返回,至此mmap调用结束 struct vm_area_struct { unsigned long vm_start; //空间用户虚拟内存的起始地址 等于mmap的返回值addr unsigned long vm_end; //结束地址vm_start大小 pgprot_t vm_page_prot; //等于mmap传递的PROT_READ|PROT_READ|PROT_WRITE unsigned long vm_flags; //等于mmap传递的MAP_SHARED unsigned long vm_pgoff; //等于mmap传递的0 … }; 3.对应的底层驱动的mmap接口 c struct file_operations {int (*mmap) (struct file *file, struct vm_area_struct *vma); }; - 功能永远只能唯一做一件事将已知的物理地址和已知的用户虚拟地址做映射,由于用户虚拟地址在用户空间,所以将来访问操作都是在应用程序完成,而不是在内核驱动完成,访问映射的用户虚拟地址就是在访问物理地址 - file跟fd亲戚关系 - vma:指向内核sys_mmap创建的一个对象,此对象来描述空闲的用户虚拟内存的各种属性,将来底层驱动mmap接口利用此指针可以获取到用户虚拟内存的属性vma-vm_start //获取起始用户虚拟地址vma-vm_end vma-vm_flagsvma-vm_page_prot问底层驱动的mmap接口到底如何最终完成映射呢 因为已知物理地址可以看手册获取到,已知的用户虚拟地址通过vma指针能够获取到如何将两者关联在一起呢 答只需调用以下函数完成关联映射 int remap_pfn_range(struct vm_area_struct *vma,unsigned long addr,unsigned long pfn, unsigned long size,pgprot_t prot); - 功能完成最终的地址映射 - 参数- vma:传递内核sys_mmap创建的对象地址也就是传递驱动mmap接口的第二个参数- addr:传递空闲的用户虚拟内存的首地址也就是传递vma-vm_start- pfn传递起始的物理地址12 切记此物理地址大小必须是4KB(0x1000)整数倍例如0xC001C00012:合法0xC001C00412:不合法- size传递映射的用户虚拟内存的大小也就是传递vma-vm_end - vma-vm_start - prot:传递用户虚拟内存的访问权限也就是传递:vma-vm_page_prot案例利用mmap实现开关灯操作 驱动代码 #include linux/init.h #include linux/module.h #include linux/miscdevice.h #include linux/fs.h #include linux/mm.hstatic int btn_mmap(struct file *file,struct vm_area_struct *vma){remap_pfn_range(vma,// 指向用户虚拟内存属性vma-vm_start,// 起始用户虚拟地址0xC001C000 12,// 起始物理地址 (4KB为单位)vma-vm_end - vma-vm_start,// 大小vma-vm_page_prot // 读写访问权限);return 0; }static struct file_operations btn_fops {.mmap btn_mmap // 地址映射接口 };static struct miscdevice btn_device{.name mybtn,.minor MISC_DYNAMIC_MINOR,.fops btn_fops }; static int btn_init(void){misc_register(btn_device);return 0; } static void btn_exit(void){misc_deregister(btn_device); }module_init(btn_init); module_exit(btn_exit); MODULE_LICENSE(GPL);应用程序 #include stdio.h #include sys/types.h #include sys/stat.h #include fcntl.h #include sys/mman.h int main(int argc,char *argv[]){int fd;void* gpiobase;unsigned long *gpiocout;unsigned long *gpiocoutend;unsigned long *gpiocaltfn0;fd open(/dev/mybtn,O_RDWR);if(fd0){printf(open mybtn failed\n);return -1;}//将内核地址映射到用户虚拟地址gpiobase mmap(NULL,0x1000,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);// 地址换算gpiocout (unsigned long *)(gpiobase 0x00);gpiocoutend (unsigned long *)(gpiobase 0x04);gpiocaltfn0 (unsigned long *)(gpiobase 0x20);// 配置输出*gpiocaltfn0 ~(324);*gpiocaltfn0 |(124);// 使能*gpiocoutend |(112);// 输出为1*gpiocout |(112);while(1){*gpiocout |(112);sleep(5);*gpiocout ~(112);sleep(5);} }注意如果出现了操作了寄存器但是硬件不同步的现象需要手动关闭cache功能 static int btn_mmap(struct file *file,struct vm_area_struct *vma){// 关闭cache功能vma-vm_page_prot pgprot_noncached(vma-vm_page_prot);remap_pfn_range(vma,// 指向用户虚拟内存属性vma-vm_start,// 起始用户虚拟地址0xC001C000 12,// 起始物理地址 (4KB为单位)vma-vm_end - vma-vm_start,// 大小vma-vm_page_prot // 读写访问权限);return 0; }3、总结 read,write,ioctl和mmap对比 什么时候用read,write,ioctl什么时候用: mmap read,write,ioctl数据操作流程 对设备读操作read,ioctl 数据流硬件寄存器-----内核缓冲区-------用户缓冲区 gpio_get_value copy_to_user 对设备写操作write,ioctl 数据流用户缓冲区-----内核缓冲区-------硬件寄存器 copy_from_user gpio_set_value 结论read,write,ioctl数据操作势必要经过两次数据拷贝 用户-内核-硬件 硬件-内核-用户 mmap数据操作流程 对设备读操作 应用程序直接以指针形式读取寄存器 data *gpiocout; 对设备写操作 应用程序直接以指针的形式写入寄存器 *gpiocout ~(1 12); 结论mmap数据操作只需一个数据拷贝 用户-硬件 硬件-用户 所以 如果用户对硬件操作访问的数据量比较小,read,write,ioctl的两次数据拷贝对系统性能肯定有影响,但是这种影响几乎可以忽略不计,如果操作的数据量比较大,两次数据拷贝性能的影响是致命的,例如摄像头,LCD显示屏,声卡等如果访问操作的数据量比较大,用read,write,ioctl势必影响系统的性能,务必采用mmap,将两次数据拷贝变成一次提供系统的性能效率由于mmap在使用的时候,分配的用户虚拟内存必须是4KB的整数倍,如果操作的数据量比较小,此时还用mmap即使提高了系统的性能(几乎体会不到),反而是浪费了宝贵的内存资源
http://www.w-s-a.com/news/409529/

相关文章:

  • 网站建设企业号助手贵阳景观设计公司
  • 网站开发第三方建设银行个人网站显示不了
  • 无锡兼职做网站郑州网站建设搜索优化
  • iis禁止通过ip访问网站品牌策划案例ppt
  • 电子商务网站建设实习seo黑帽优化
  • 如何做好网站建设销售闸北集团网站建设
  • 重庆装饰公司北京官网seo推广
  • 深圳网站设计灵点网络品牌网站充值接口
  • 建设书局 网站国内国际时事图片
  • 成都 网站建设培训学校屏蔽wordpress自带编辑器
  • 公司网站制作工作室中天建设集团有限公司第五建设公司
  • 网站的网页设计毕业设计苏州宣传册设计广告公司
  • 商城网站优化方案注册公司制作网站
  • 政务服务网站建设整改报告wordpress的导航代码
  • 图片素材网站建设做教育网站用什么颜色
  • 快站淘客中转页wordpress商业插件
  • 可信网站网站认证免费软件下载网站免费软件下载网站
  • 小学生网站制作最新域名网站
  • 奖励网站代码设计制作ppt时
  • 茂名优化网站建设门户网站和部门网站的区别
  • 一尊网 又一个wordpress站点wordpress获取当前文章名称
  • 营销型网站多少钱新建网站的外链多久生效
  • 网站空间怎么选择tp5企业网站开发百度云
  • 网站建设saas排名成立公司的流程和要求及费用
  • 网站建设共享骨科医院网站优化服务商
  • 肯尼亚网站域名万能进销存软件免费版
  • 做商城网站价格上海做网站建设
  • 广州制作外贸网站公司阿里云网站模板
  • 做网站为什么要买服务器十堰城市建设网站
  • 西安网站seo技术厂家东莞如何制作免费的网页