《Linux Device Drivers》第八章 分配内存——note

本章主要介绍Linux内核的内存管理。

  • kmalloc函数的内幕

    • 不正确所获取的内存空间清零
    • 分配的区域在物理内存中也是连续的
    • flags參数
      • <linux/slab.h>
      • <linux/gfp.h>
        • GFP_KERNEL

          • 在空暇内存较少时把当前进程转入休眠以等待一个页面
          • 分配内存的函数必须是可重入的
        • GFP_ATOMIC
          • 用于在中断处理例程或其它执行于进程上下文之外的代码中分配内存,不会休眠
        • GFP_USER
          • 用于为用户空间页分配内存。可能会休眠
        • GFP_HIGHUSER
          • 类似于GFP_USER,只是假设有高端内存的话就从那里分配
        • GFP_NOIO, GFP_NOFS
          • 这两个标志的功能类似于GFP_KERNEL,可是为内核分配内存的工作方式加入了一些限制。具有GFP_NOFS标志的分配不同意运行不论什么文件系统调用。而GFP_NOIO禁止不论什么I/O的初始化。

            这两个标志主要在文件系统和虚拟内存代码中使用,这些代码中的内存分配可休眠。但不应该发生递归的文件系统调用
        • __GFP_DMA
          • 该标志请求分配发生在可进行DMA的内存区段中
        • __GFP_HIGHHEM
          • 这个标志表明要分配的内存可位于高端内存
        • __GFP_COLD
          • 这个标志请求尚未使用的“冷”页面,对于DMA读取的页面分配。可使用这个标志
        • __GFP_NOWARN
          • 非常少使用。能够避免内核在无法满足分配请求时产生警告
        • __GFP_HIGH
          • 标记了一个高优先级的请求,它同意为紧急善而消耗由内核保留的最后一些页面
        • __GFP_REPEAT, __GFP_NOFAIL, __GFP_NORETRY
          • 告诉分配器在满足分配请求而遇到困难时应该採取何种行为
          • __GFP_REPEAT表示“努力再尝试一次”,它会又一次尝试分配。但仍有可能失效
          • __GFP_NOFAIL标志告诉分配器始终不返回失败。它会努力满足分配请求,不鼓舞使用这个标志
          • __GFP_NORETRY告诉分配器,假设所请求的内存不可获得,就马上返回
    • 内存区段
      • __GFP_DMA和__GFP_HIGHHEM的使用与平台相关
      • Linux内核把内存分为三个区段:可用于DMA的内存、常规内存以及高端内存
      • 可用于DMA的内存指存在于特别地址范围内的内存,外设能够利用这些内存运行DMA訪问
      • 高端内存是32位平台为了訪问大量的内存而存在的一种机制
      • 假设没有指定特定的标志。则kmalloc会在常规区段和DMA区段搜索
      • 假设设置了__GFP_HIGHHEM标志。则全部三个区段都会被搜索
      • 内存区段的背后机制在mm/page_alloc.c中实现
    • size參数
      • Linux处理内存分配的方法是。创建一系列的内存对象池,每一个池中的内存块大小是固定一致的。

        处理分配请求时,就直接在包括有足够大的内存块的池中传递一个整块给请求者
      • kmalloc能处理的最小的内存块是32或者64
      • 假设希望代码具有完整的可移植性。则不应该分配大于128KB的内存
  • 后备快速缓存

    • Linux内核的调整缓存管理有时称为“slab分配器”
    • slag分配器实现的快速缓存具有kmem_cache_t类型
    • kmem_cache_t *kem_cache_create(const char *name, size_t size, size_t offset, unsigned long flags, void (*constructor) (void *, keme_cache_t *, unsigned long flags), void (*destructor) (void *, kmem_cache_t *, unsigned long flags));
    • 參数flags控制怎样完毕分配
      • SLAB_NO_REAP

        • 能够保护快速缓存在系统寻找内存的时候不会被降低
      • SLAB_HWCACHE_ALIGN
        • 要求全部数据对象跟调整缓存行(cache line)对齐。实际的操作则依赖于主要平台的硬件调整缓存布局
      • SLAB_CACHE_DMA
        • 要求每一个数据对象都从可用于DMA的内存区段中分配
    • mm/slab.c
    • 能够使用同一个函数同一时候作为constructor和destructor使用,当调用的是一个constructor函数的时候,slab分配器总是传递SLAB_CTOR_CONSTRUCTOR标志
    • void *kmem_cache_alloc(kmem_cache_t *cache, int flags);
    • void kmem_cache_free(kmem_cache_t *cache, const void *obj);
    • int kmem_cache_destroy(kmem_cache_t *cache);
    • 快速缓存的使用统计情况能够从/proc/slabinfo获得
    • 内存池
      • 内存池事实上就是某种形式的后备快速缓存,它试图始终保存空暇的内存,以便把在紧急状态下使用
      • 内存池对象的类型为mempool_t
      • <linux/mempool.h>
        • mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data);
        • typedef void *(mempool_alloc_t) (int gfp_mask, void *pool_data);
        • typedef void (mempool_free_t) (void *element, void *pool_data);
        • void *mempool_alloc(mempool_t *pool, int gfp_mask);
        • void mempool_free(void *element, mempool_t *pool);
        • int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask);
        • void mempool_destroy(mempool_t *pool);
      • example
        • cache = kmem_cache_create(…);
        • pool =- mempool_create(MY_POOL_MINIMUM, mempool_alloc_slab, mempool_free_slab, cache);
      • mempool会分配一些内存块,空暇且不会真正得到使用
      • 应尽量避免在驱动程序代码中使用mempool
  • get_free_page和相关函数
    • get_zeroed_page(unsigned int flags);

      • 返回指向新页面的指针并将页面清零
    • __get_free_page(unsigned int flags);
      • 类似于get_zeroed_page,但不清零页面
    • __get_free_pages(unsigned int flags, unsigned int order);
      • 分配若干(物理连续的)页面,并返回指向该内在区域第一个字节的指针。但不清零页面
      • 參数order是要申请或释放的页面数的以2为底的对数
    • void free_page(unsigned long addr);
    • void free_pages(unsigned long addr, unsigned long order);
    • alloc_pages接口
      • struct page *alloc_pages_node(int nid, unsigned int flags, unsigned int order);

        • nid是NUMA节点的ID号
      • struct page *alloc_page(unsigned int flags, unsigned int order);
      • struct page *alloc_page(unsigned int flags);
      • void __free_page(struct page *page);
      • void __free_pages(struct page *page, unsigned int order);
      • void free_hot_page(struct page *page);
      • void free_code_page(struct page *page);
    • Subtopic 7
  • vmalloc及其辅助函数
    • 分配虚拟地址空间的连续区域,这段区域右物理上可能是不连续的,内核却觉得它们在地址上是连续的
    • vmalloc获得的内存使用起来效率不高
    • <linux/vmalloc.h>
      • void *vmalloc(unsigned long size);
      • void vfree(void *addr);
      • void *ioremap(unsigned long offset, unsigned long size);
      • void iounmap(void *addr);
    • vmalloc能够获得的地址在VMALLOC_START到VMALLOC_END的范围中。这两个符号都在<asm/pgtable.h>中定义
    • 使用vmalloc函数的正确场合是在分配一大块连续的、仅仅在软件中存在的、用于缓冲的内存区域的时候
    • ioremap很多其它用于映射(物理的)PCI缓冲区地址到(虚拟的)内核空间
  • per-CPU变量
    • 当建立一个per-CPU变量时。系统中的每一个处理器都会拥有该变量的特有副本
    • 不须要锁定
    • 能够保存在相应处理器的快速缓存中
    • <linux/percpu.h>
      • DEFINE_PER_CPU(type, name);
      • get_cpu_var(variable);
      • put_cpu_var(variable);
      • per_cpu(variable, int cpu_id);
      • void *alloc_percpu(type);
      • void *__alloc_percpu(size_t size, size_t align);
      • per_cpu_ptr(void *per_cpu_var, int cpu_id);
      • EXPORT_PER_CPU_SYMBOL(per_cpu_var);
      • EXPORT_PER_CPU_SYMBOL_GPL(per_cpu_var);
      • DECLARE_PER_CPU(type, name);
  • 获取大的缓冲区
    • 在引导时获得专用缓冲区

      • <linux/bootmem.h>

        • void *alloc_bootmem(unsigned long size);
        • void *alloc_bootmem_low(unsigned long size);
        • void *alloc_bootmem_pages(unsigned long size);
        • void *alloc_bootmem_low_pages(unsigned long size);
        • void free_bootmem(unsigned long addr, unsigned long size);
      • 这些函数要么分配整个页,要么分配不在页面边界上对齐的内存区
      • 除非使用具有_low后缀的版本号,否则分配的内存可能会是高端内存
时间: 2024-12-16 04:03:06

《Linux Device Drivers》第八章 分配内存——note的相关文章

《Linux Device Drivers》第十章 中断处理——note

概述:系统要及时的感知硬件的状态,通常有两种方式:一种是轮询:一种是通过响应硬件中断.前者会浪费处理器的时间,而后者不会. 准备并口 在没有节设定产生中断之前,并口是不会产生中断的 并口的标准规定设置端口2(px37a.0x27a或者其它端口)的第4位将启用中断报告,0×10 当处于启用中断状态,每当引脚10的电平发生从低到高改变时,并口就会产生一个中断 引脚9是并口数据字节中的最高位 安装中断处理例程 中断信号线是非常珍贵且有限的资源 内核维护了一个中断信号线的注册表,该注册表类似于I/O端口

linux device drivers - debugging之proc

在书籍"linux device drivers"的第四章,专门介绍驱动开发中的debugging技术. printk只是其中一种技术,这种技术要求printk打印消息,并且会写入到磁盘里的文件中,这会拖慢整个代码的执行速度. 还有其中的debugging技术,并且对代码执行速度的影响比prink小. 其中讲到了,我们可以让驱动程序在proc文件系统里面创建文件,并且将需要的debugging信息写入到这个文件里面. linux内核提供了相应的API,来和proc文件系统交互,例如创建

《Linux Device Drivers》第十五章 内存映射和DMA——note

简单介绍 很多类型的驱动程序编程都须要了解一些虚拟内存子系统怎样工作的知识 当遇到更为复杂.性能要求更为苛刻的子系统时,本章所讨论的内容迟早都要用到 本章的内容分成三个部分 讲述mmap系统调用的实现过程 讲述怎样跨越边界直接訪问用户空间的内存页 讲述了直接内存訪问(DMA)I/O操作,它使得外设具有直接訪问系统内存的能力 Linux的内存管理 地址类型 Linux是一个虚拟内存系统,这意味着用户程序所使用的地址与硬件使用的物理地址是不等同的 有了虚拟内存,在系统中执行的程序能够分配比物理内存很

《Linux Device Drivers》第十四章 Linux 设备模型

简介 2.6内核的设备模型提供一个对系统结构的一般性抽象描述,用以支持多种不同的任务 电源管理和系统关机 与用户空间通信 热插拔设备 设备类型 对象生命周期 kobject.kset和子系统 kobject是组成设备模型的基本结构 对象的引用计数 sysfs表述 数据结构关联 热插拔事件处理 kobject基础知识 <linux/kobject.h> 嵌入的kobject 内核代码很少去创建一个单独的kobject对象,kobject用于控制对大型域相关对象的访问 kobject的初始化 首先

第八章 分配内存

一.Kmalloc函数的内幕 kamlloc原型: #include <linux/slab.h> void *kmalloc(size_t size, int flags); size:要分配的块的大小 falgs:标志位,多种方式控制kmalloc行为 常用的标志是GFP_KERNEL,表示内存分配是代表运行在内核空间的进程执行的. GFP_ATOMIC:用于在中断处理例程或其他运行于进程上下文之外的代码中分配内存,不会休眠 GFP_KERNEL:内核内存的通常分配方法, 可能引起休眠 G

《Linux Device Drivers》第十八章 TTY驱动程序——note

简介 tty设备的名称是从过去的电传打字机缩写而来,最初是指连接到Unix系统上的物理或虚拟终端 Linux tty驱动程序的核心紧挨在标准字符设备驱动层之下,并提供了一系列的功能,作为接口被终端类型设备使用 有三种类型的tty驱动程序:控制台.串口和pty /proc/tty/drivers 当前注册并存在于内核的tty设备在/sys/class/tty下都有自己的子目录 小型TTY驱动程序 <linux/tty_driver.h> struct tty_driver tiny_tty_dr

《Linux Device Drivers》第十二章 PCI驱动程序——note

简介 本章给出一个高层总线架构的综述 讨论重点是用于访问Peripheral Component Interconnect(PCI,外围设备互联)外设的内核函数 PCI总线是内核中得到最好支持的总线 本章主要介绍PCI驱动程序如果寻找其硬件和获得对它的访问 本章也会介绍ISA总线 PCI接口 PCI是一组完整的规范,定义了计算机的各个不同部分之间应该如何交互 PCI规范涵盖了与计算机接口相关的大部分问题 PCI架构被设计为ISA标准的替代品,有三个主要目标 获得在计算机和外设之间传输数据时更好的

《Linux Device Drivers》 第十七章 网络驱动程序——note

简介 网络接口是第三类标准Linux设备,本章将描述网络接口是如何与内核其余的部分交互的 网络接口必须使用特定的内核数据结构注册自身,以备与外界进行数据线包交换时调用 对网络接口的常用文件操作是没有意义的,因此在它们身上无法体现Unix的"一切都是文件"的思想 网络驱动程序异步自外部世界的数据包 网络设备向内核请求把外部获得的数据包发送给内核 Linux内核中的网络子系统被设计成完全与协议无关 在网络世界中使用术语"octet"指一组8个的数据位,它是能为网络设备和

《Linux Device Drivers》第六章 高级字符驱动程序操作——note

ioctl 支持的操作,例如 简单数据传输 控制动作,例如用户空间发起弹出介质动作 反馈硬件的状态,例如报告错误信息 参数配置,例如改变波特率 执行自破坏 用户空间的ioctl方法原型:int ioctl(int fd, unsigned long cmd, -);每个ioctl命令就是一个独立的系统调用,而且是非公开的 驱动程序的ioctl方法原型:int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, u