linux设备驱动进阶

上传人:油条 文档编号:46011874 上传时间:2018-06-20 格式:PPT 页数:80 大小:497.50KB
返回 下载 相关 举报
linux设备驱动进阶_第1页
第1页 / 共80页
linux设备驱动进阶_第2页
第2页 / 共80页
linux设备驱动进阶_第3页
第3页 / 共80页
linux设备驱动进阶_第4页
第4页 / 共80页
linux设备驱动进阶_第5页
第5页 / 共80页
点击查看更多>>
资源描述

《linux设备驱动进阶》由会员分享,可在线阅读,更多相关《linux设备驱动进阶(80页珍藏版)》请在金锄头文库上搜索。

1、黄松青Linux设备驱动进阶现实驱动中的问题v如何使用系统内存? v如何使用外设内存/IO或寄存器? v并发和竞争如何处理? v如何进行中断处理? v如何调试驱动程序? v 使用系统内存v系统内存作为重要的资源受系统内存管理的子 系统的统一管理 v由于MMU的存在,我们不能直接的使用物理内 存 v外设和驱动程序对内存有不同的要求,如连续 性和硬件对地址的要求 v要避免内存碎片的产生使用系统内存vkmalloc()原型 #include void *kmalloc(size_t size, int flags); 特性-与用户程序中malloc有相似之处-分配的区也是在物理内存中连续 -分配的内

2、存要求不能大于128K-分配过程中有可能阻塞,这取决于参数-这个函数快(除非它阻塞)并且不清零它获得的内存 -成功返回一个虚拟地址, 失败返回NULL使用系统内存v Kmalloc()参数解释size -是要分配的块的大小flags 分配的指示,解释如下GFP_ATOMIC 用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡 眠.GFP_KERNEL 内核内存的正常分配. 可能睡眠.GFP_USER 用来为用户空间页来分配内存; 它可能睡眠.GFP_HIGHUSER 如同 GFP_USER, 但是从高端内存分配 GFP_NOIO GFP_NOFS -这个标志功能如同 GFP_KERN

3、EL, 但是它们增加限制到内核能做的 来满足请求. 一个 GFP_NOFS 分配不允许进行任何文件系统调用, 而 GFP_NOIO 根本不允许任何 I/O 初始化. 它们主要地用在文件系 统和虚拟内存代码, 那里允许一个分配睡眠, 但是递归的文件系统调 用会是一个坏注意.使用系统内存v kmalloc()参数解释 上面列出的这些分配标志可以是下列标志的相或来作为参数, 这些标志改变这些分配 如何进行: _GFP_DMA 这个标志要求分配在能够 DMA 的内存区. 确切的含义是平台依赖的 _GFP_HIGHMEM 这个标志指示分配的内存可以位于高端内存. _GFP_COLD 正常地, 内存分配器

4、尽力返回“缓冲热“的页 - 可能在处理器缓冲中找到的页. 相反 , 这个标志请求一个“冷“页, 它在一段时间没被使用. _GFP_NOWARN 这个很少用到的标志,当一个分配无法满足阻止内核来发出警告(使用 printk ). _GFP_HIGH 这个标志标识了一个高优先级请求, 它被允许来消耗甚至被内核保留给紧急状况 的最后的内存页. _GFP_REPEAT _GFP_NOFAIL _GFP_NORETRY 这些标志修改分配器如何动作, 当它有困难满足一个分配. _GFP_REPEAT 意思 是“ 更尽力些尝试“ 通过重复尝试 - 但是分配可能仍然失败. _GFP_NOFAIL 标 志告诉分

5、配器不要失败; 它尽最大努力来满足要求. 使用 _GFP_NOFAIL 是强烈 不推荐的; 可能从不会有有效的理由在一个设备驱动中使用它. 最后, _GFP_NORETRY 告知分配器立即放弃如果得不到请求的内存.使用系统内存v进程的空间使用系统内存v内存区- linux将物理内存分三个区:DMA,NORMAL,HIGHMEM区,如何分是与平台相关的- DMA区,外设可以在这里进行 DMA 存取. 在大部分的健全的平台, 所有的内存都在这个区. 在 x86, DMA 区用在 RAM 的前 16 MB, 这里传统的 ISA 设备可以进行 DMA; PCI 设备没有这个限制- NORMAL区,li

6、nux内核能访问的地址的物理内存区域, 1G以下- HIGHMEM区,linux不能直接映射访问的1G以上的区域使用系统内存vkfree()#include void kfree(void *obj); 释放kmalloc分配的内存, obj参数是 kmalloc返回的指针使用系统内存vvmalloc()和vfree()原形:#include void *vmalloc(unsigned long size); void vfree(void * addr); 特性:- 分配的内存物理上不连续 - 可以分配系统允许的大块内存,没有大小限制- 成功返回一个虚拟地址, 失败返回NULL使用系统内存

7、v get_free_page()及相关函数 get_zeroed_page(unsigned int flags); 返回一个指向新页的指针并且用零填充了该页 flags 参数同 kmalloc 的用法相同 _get_free_page(unsigned int flags); 类似于 get_zeroed_page, 但是没有清零该页. _get_free_pages(unsigned int flags, unsigned int order); 分配并返回一个指向一个内存区第一个字节的指针, 内存区可能是几 个(物理上连续)页长但是没有清零.order 是你在请求的或释放的页 数的以

8、2 为底的对数(即, log2N),如果 order 太大(没有那个大小的 连续区可用), 页分配失败, order 允许的最大值是 10 或者 11 (对应 于 1024 或者 2048 页), 依赖于体系. 但是, 一个 order-10 的分配在 除了一个刚刚启动的有很多内存的系统中成功的机会是小的 对应的释放函数 void free_page(unsigned long addr); void free_pages(unsigned long addr, unsigned long order); 使用系统内存v 为频繁使用的结构对象分配,避免内存碎片 #include kmem_ca

9、che_t *kmem_cache_create(const char *name, size_t size, size_t offset, unsigned long flags, void (*constructor)(void *, kmem_cache_t *, unsigned long flags), void (*destructor)(void *, kmem_cache_t *, unsigned long flags); int kmem_cache_destroy(kmem_cache_t *cache); 这个函数创建一个新的可以驻留任意数目全部同样大小的内存区的缓 存

10、对象, 大小由 size 参数指定. name 参数和这个缓存关联并且作为 一个在追踪问题时有用的管理信息; 通常, 它被设置为被缓存的结构类 型的名子. 这个缓存保留一个指向 name 的指针, 而不是拷贝它, 因 此驱动应当传递一个指向在静态存储中的名子的指针(常常这个名子只 是一个文字字串). 这个名子不能包含空格.使用系统内存v offset 是页内的第一个对象的偏移; 它可被用来确保一个对被分 配的对象的特殊对齐, 但是你最可能会使用 0 来请求缺省值. v flags 控制如何进行分配并且是下列标志的一个位掩码: SLAB_NO_REAP 设置这个标志保护缓存在系统查找内存时被削减

11、. 设置这个标 志通常是个坏主意; 重要的是避免不必要地限制内存分配器的 行动自由. SLAB_HWCACHE_ALIGN 这个标志需要每个数据对象被对齐到一个缓存行; 实际对齐依 赖主机平台的缓存分布. 这个选项可以是一个好的选择, 如果在 SMP 机器上你的缓存包含频繁存取的项. 但是, 用来获得缓存 行对齐的填充可以浪费可观的内存量. SLAB_CACHE_DMA 这个标志要求每个数据对象在 DMA 内存区分配.使用系统内存v函数的 constructor 和 destructor 参数 是可选函数( 但是可能没有 destructor, 如 果没有 constructor ); 前者可

12、以用来初始 化新分配的对象, 后者可以用来“清理“对象在 它们的内存被作为一个整体释放回给系统之前 . 使用系统内存v 一旦一个对象的缓存被创建, 你可以通过调用 kmem_cache_alloc 从它分配对象. void *kmem_cache_alloc(kmem_cache_t *cache, int flags); 这里, cache 参数是你之前已经创建的缓存; flags 是你会传递 给 kmalloc 的相同,返回被分配对象的指针v void kmem_cache_free(kmem_cache_t *cache, const void *obj); 释放一个对象,当驱动代码用完

13、这个缓存, 典型地当模块被卸载, 它应当如下释放它的缓存:使用系统内存v物理地址和虚拟地址的转换 virt_to_phys(), _pa()- 将虚拟地址转换成物理地址- 不能转化vmalloc()的地址- 实现与机器相关phys_to_virt(), _va()- 将物理地址转换成虚拟地址使用外设内存v 统一编址和IO编址的体系- 统一编址指的是外设的内存的地址与系统内存的地 址是属于统一空间的,系统没有提供另外的指令 (in/out)去专门访问次类内存,而是使用普通的访存 指令,这种体系在嵌入式中较多,如ARM,MIPS- IO编址是指外设的内存和系统内存并不属于同一 地址空间,系统提供了

14、专门的指令去访问外设的内存, 也叫IO端口.这种体系主要是x86的体系- 有IO端口的体系,操作系统也需要进行统一的管理使用外设内存v I/O 端口分配#include struct resource *request_region(unsigned long first, unsigned long n, const char *name); -这个函数告诉内核, 你要使用 n 个端口, 从 first 开始. name 参数 应当是你的设备的名子. 如果分配成功返回值是非 NULL. 如果你从 request_region 得到 NULL, 你将无法使用需要的端口. -当你用完一组 I/O

15、 端口(在模块卸载时, 也许), 应当返回它们给系统, 使用:void release_region(unsigned long start, unsigned long n); 使用外设内存v 操作 I/O 端口 unsigned inb(unsigned port); void outb(unsigned char byte, unsigned port); 读或写字节端口( 8 位宽 ). inb 的返回类型是跨体系而不同的 unsigned inw(unsigned port); void outw(unsigned short word, unsigned port); 这些函数存取

16、 16-位 端口( 一个字宽 ); unsigned inl(unsigned port); void outl(unsigned longword, unsigned port); 这些函数存取 32-位 端口使用外设内存v 使用 I/O 内存,分配 I/O 内存区必须在使用前分配. 分配内存区的接口是 struct resource *request_mem_region(unsigned long start, unsigned long len, char *name); -这个函数分配一个 len 字节的内存区, 从 start 开始. 如果 一切顺利, 一个 非NULL 指针返回; 否则返回值是 NULL. 所有 的 I/O 内存分配来 /proc/iomem

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号