linux 内核内存管理办法简介(下)

本文介绍了Linux内核中slab分配器的工作原理及其优势。它针对频繁申请和释放的固定大小内存块,通过预分配和缓存机制提高内存管理效率,减少内存碎片。此外,还讨论了通用高速缓存的概念,以及如何根据不同需求选择合适的内存分配策略。
     伙伴系统算法,设和与申请以页框为单位的大块内存请求。而当申请几十字节的内存时,采用伙伴系统,分配一个页框,显然是浪费空间。实际上,内核采用了slab分配器进行管理。

     采用slab分配器,是基于以下假设:
     1> 内核会经常申请若干种同样大小的内存,如分配一个新的进程描述符,为一个磁盘文件分配一个新的inode对象,dentry对象等
     2> 这些对象会经常申请和释放,如进程结束时,文件关闭或被删除时。如果每次申请或释放都将内存交给操作伙伴系统,将会造成抖动,影响性能。
     3> 碎片问题。如果第一次分配了100字节,第二次分配60字节,大小各不相同,当某个对象释放时,将会出现各种碎片,不易于管理。

     基于以上原因,内核采用slab分配器,基本思路为:
     1> 为常用到的内存申请,如inode,dentry,分别申请inode高速缓存和dentry高速缓存,专门用来管理inode和dentry的申请和释放(这就是slab的作用)。
     2> 同一个高速缓存中,申请的空间大小是固定的,如inode高速缓存,每次都申请sizeof(struct inode)大小的空间。
     slab分配器的组成如下图所示。

     高速缓存代表创建的各种高速缓存,如inode高速缓存等,由kmem_cache_t类型的struct来描述;
     slab为高速缓存包含的slab,每个高速缓存包含多个slab,实际上,每个slab都处于满的slab中,空的slab中,部分满的slab中;
     对象为实际分配给应用程序的,如inode对象等,每个slab包含1到多个对象。
     
     从上面的分析中,可以看出,slab可以称得上是专用高速缓存,只能分配固定大小的内存空间。在使用时,过程如下:
      kmem_cache_create(char *name ,size_t size,size_t offset,unsigned long flags,void(*constructor)(***),void(*destructor)(***));
     size:存储区域的大小,为一个固定的值
     offset:页面中第一个对象的偏移量,用来确保对已分配的对象进行某种特殊的对齐,常用之为0
     flag:常用值为SLAB_NO_HEAP(保护高速缓存在系统寻找内存的时候不会被减少。设置该标志通常不是个好主意)
               SLAB_HWCACHE_ALIGN,要求所有数据对象跟高速缓存行对齐,可能会浪费一些空间
               SLAB_CACHE_DMA,要求每个数据对象都从以用于DMA的内存区段中分配
     用法:
          kmem_cache_t *cache;
          cache = kmem_cache_create("tmp",sizeof(struct XXX),0,SLAB_HWCACHE_ALIGN,NULL,NULL);
          
          p=kmem_cache_alloc(cache,GFP_KERNEL);
          
          kmem_cache_free(cache,p);

          kmem_cache_destroy(cache);

     使用slab,可以加快专用内存空间分配速度,并且减少了系统碎片,但是每种对象都采用这种专用高速缓存分配,如需要存放文件名,由于文件名大小不固定,采用专用高速缓存显然是不合理的。内核中提供了普通高速缓存(内核中的kmalloc函数分配空间就是从通用高速缓存中获取的):
     1>内核建立了13个按照几何分布的空闲内存区,大小从32字节到131072字节(即128K,不同的操作系统,这个值可能不一样,这也就解释了为什么malloc申请空间是有上限的)
     2>每次申请空间时,根据程序输入的要分配的地址,找到最接近的空间,如申请33字节,则查找64字节对应的分配区(对于分配区的管理,其实也是按照slab中的方法,因为每个分配区分配的字节数是相同的),进行内存的分配。
     3> 对于每种大小,都有两个高速缓存:一个适应于ISA DMA分配,另一个适用于常规分配。

我们可以通过cat /proc/slabinfo命令查看系统中slab的使用情况。关于slab部分,还有内外部描述符,slab着色(为创建slab高速缓存使用,与页高速缓存类似),这里就不多介绍。
### Linux 内核中的内存映射驱动程序实现 #### 内存映射的概念 在嵌入式系统和操作系统中,内存映射是一种将物理地址空间映射到虚拟地址空间的技术。这种技术允许设备寄存器或其他硬件资源通过标准的内存访问指令来进行读写操作。 #### 使用 `devm_*` API 进行内存分配 对于动态管理的内存分配,在 Linux 内核中推荐使用带有自动释放功能的 `devm_*` 函数族。这些函数会在设备卸载时自动清理所申请的资源,从而减少了开发者手动管理资源的工作量[^3]。 ```c void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp); ``` 此函数用于从内核堆栈中分配指定大小的一块零初始化内存区域,并将其绑定给特定的设备对象;当该设备被移除时,这块内存会自动得到释放。 #### 实现一个简单的内存映射字符设备驱动 下面是一个简化版的例子展示如何创建基于内存映射接口的字符设备: ```c #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #define DEVICE_NAME "memmap_dev" static dev_t memmap_dev; static struct cdev *my_cdev; // 打开/关闭方法省略... static int memmap_mmap(struct file *filp, struct vm_area_struct *vma) { unsigned long pfn = vma->vm_pgoff; // 获取偏移页框号 phys_addr_t phyaddr = (phys_addr_t)pfn << PAGE_SHIFT; if (remap_pfn_range(vma, vma->vm_start, pfn, vma->vm_end - vma->vm_start, vma->vm_page_prot)) { printk(KERN_ALERT "Remapping pages failed\n"); return -EAGAIN; } return 0; } static const struct file_operations fops = { .owner = THIS_MODULE, .open = memmap_open, .release = memmap_release, .mmap = memmap_mmap, }; static int __init init_memmap(void){ alloc_chrdev_region(&memmap_dev, 0, 1, DEVICE_NAME); my_cdev = cdev_alloc(); cdev_init(my_cdev, &fops); cdev_add(my_cdev, memmap_dev, 1); register_chrdev_region(memmap_dev, 1, DEVICE_NAME); return 0; } static void __exit cleanup_memmap(void){ unregister_chrdev_region(memmap_dev, 1); cdev_del(my_cdev); } module_init(init_memmap); module_exit(cleanup_memmap); MODULE_LICENSE("GPL"); ``` 上述代码片段展示了怎样注册一个新的字符设备并提供了一个自定义的 mmap 方法来处理应用程序请求的内存映射。这里的关键在于调用了 `remap_pfn_range()` 来设置进程地址空间内的页面表项指向实际的物理地址范围。 #### 可能遇到的问题及解决方案 - **权限不足**: 如果用户态应用尝试映射受保护或受限于其他安全策略下的内存,则可能会失败。解决办法是在设计阶段就考虑好所需的权限级别以及采取适当的安全措施。 - **不连续的物理地址**: 对某些类型的硬件来说,其内部缓冲区可能是由多个离散部分组成的而非一块连续的空间。此时可以利用 IOMMU 或者构建复合映射关系等方式解决问题。 - **缓存一致性问题**: 当涉及到 DMA 数据传输时,CPU 和外设之间可能存在缓存不同步的情况。可以通过同步机制如 dmac_map_single() 系列API确保数据一致性和完整性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值