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

linux内存相关

Linux内核内存申请的方式有哪些?

内核申请内存的接口,如下介绍。

kmalloc

该函数一般是用于内核申请小于page size的内存,分配的内存是物理连续的,至于kmalloc的具体实现,需要参考内核内存分配器配置的是slab、slob还是slub了。函数原型是void *kmalloc(size_t size, gfp_t flags),传递的参数,除了需要申请的内存大小以外,还有内存的类型。gfp_t flags通常使用下面的配置参数:

类型意义
GFP_ATOMIC不会导致休眠,可以在中断处理函数或者其他关闭中断的临界区调用。这个标识申请内存,如果系统内存不足,将会直接返回失败而不触发内存回收机制。
因为不能触发内存回收机制,通常用于分配较小的内存块,不适用于分配较大内存。
GFP_KERNEL通用的申请类型,该类型申请可能会存在休眠。一般较大内存的申请可使用该类型,内存不足时会触发直接回收。
GFP_KERNEL_ACCOUNT和GFP_KERNEL一样,只是说GFP_KERNEL_ACCOUNT在分配内存时会被kmemcg(‌Kernel Memory Controller,‌内核内存控制器)‌统计。
GFP_NOWAIT不会导致休眠,申请不到内存立刻返回错误
GFP_NOIO用于确保在内存分配过程中不进行任何I/O操作。‌这有助于在需要快速分配内存且不想在此过程中进行I/O操作的情况下使用。‌
GFP_NOFS用于确保在内存分配过程中不进行任何文件系统调用。‌这有助于在文件系统和IO堆栈的代码路径中避免递归死锁。‌
GFP_USER用于用户空间分配,也需要由内核或硬件直接访问。它通常被硬件用于映射到用户空间(例如图形)的缓冲区,硬件仍然可以对其进行DMA。
GFP_DMA标识从ZONE_DMA申请
GFP_DMA32与GFP_DMA一致,只是反馈32bit地址
GFP_HIGHUSER用于可能映射到用户空间的用户空间分配,不需要被内核直接访问,用于从高端内存中分配内存,‌这种内存一旦被分配给用户空间使用,‌就不能再被移动。‌
GFP_HIGHUSER_MOVABLE用于内核不需要直接访问但需要访问时可以使用kmap()的用户空间分配,它们可以通过页面回收或页面迁移来移动
GFP_TRANSHUGE_LIGHT用于THP分配。它们是复合分配,如果内存不可用,通常会很快失败,并且不会在失败时唤醒kswapd/kcompactd。
GFP_TRANSHUGE_LIGHT版本根本不尝试回收/压缩,默认情况下用于页面故障路径,而非light版本由khugepage使用。

vmalloc

用于申请虚拟地址连续、物理地址不连续的高端内存区域内存。函数原型是void *vmalloc(unsigned long size),仅需要传递需要申请的大小。因为vmalloc申请的是物理地址不连续的内存,访问的时候,需要经过IOMMU映射转换,才可以得到连续的虚拟地址内存。

kmalloc()和vmalloc()函数所分配的内存都处在内核空间,即从3GB~4GB;但是位置不一样,kmalloc()分配的内存处于3GB~high_memory之间,而vmalloc()所分配的内存在VMALLOC_START~4GB之间,也就是非连续内存区。
一般情况下,在驱动程序都是调用kmalloc()来给数据结构分配内存,而vmalloc()用在为活动的交换区分配数据结构,为某些I/O驱动程序分配缓冲区,或为模块分配空间。

vmalloc()比kmalloc()要慢?

  1. kmalloc()分配的物理地址与虚拟地址只有一个PAGE_OFFSET偏移,不需要为地址段修改页表;
  2. vmalloc()申请的物理内存地址与虚拟地址不是一一映射的关系,需要通过页表进行转换,所以每次分配都需要对页表进行设置,当然效率比较低;

最重要的,分配大小的问题:如果只是需要小内存,那么一定用kmalloc()。

kmalloc和vmalloc是Linux内核中用于内存分配的两种函数,它们之间存在一些关键的区别。以下是对这些区别的详细解释:

  1. 分配的内存类型与连续性
    • kmalloc:它分配的是物理内存,且保证所分配的内存在物理上是连续的。这种连续性对于某些需要直接访问物理地址或进行DMA(直接存储器访问)操作的硬件设备来说是必要的。
    • vmalloc:它分配的是虚拟内存,这意味着在虚拟地址空间上,所分配的内存是连续的。然而,在物理内存中,这些页可能不是连续的。vmalloc通过映射物理页到连续的虚拟地址空间来实现这种分配。
  2. 使用场景与大小限制
    • kmalloc:通常用于分配较小的内存块,如结构体或链表节点等。由于它在物理内存池中分配,因此其大小受到可用物理内存页的限制。
    • vmalloc:更适用于分配较大的内存块,如大型数组或缓冲区。由于它不要求物理内存的连续性,因此可以跨越多个物理页进行分配,从而满足大内存块的需求。
  3. 分配速度与性能
    • 一般而言,kmalloc的分配速度可能更快,因为它直接从内核的内存池中分配物理连续的内存。而vmalloc由于需要映射物理页到虚拟地址空间,并可能涉及页表的修改,因此其分配速度可能稍慢。
  4. 内存位置
    • kmalloc分配的内存位于低端内存区域(物理内存地址小于896MB),这部分内存可以直接被访问。
    • vmalloc分配的内存可以位于高端内存区域(物理内存地址大于896MB),这些内存需要通过内核映射才能够被访问。

综上所述,kmallocvmalloc的主要区别在于分配的内存类型(物理或虚拟)、连续性(物理或虚拟连续)、使用场景(小块或大块内存)、分配速度以及内存位置。在选择使用哪种分配函数时,需要根据具体的应用需求和性能考虑来做出决策。

alloc_pages

以page为单位的申请物理空间连续的内存,函数原型是struct page *alloc_pages(gfp_t gfp_mask, unsigned int order),参数order表示需要申请的page的数量,为 2order 个page。 alloc_pages是直接从内存管理器中获取页,而不是通过slab分配器。

ioremap

把已知未映射的物理地址映射到虚拟地址,比如设备寄存器地址,函数原型是void __iomem *ioremap(phys_addr_t offset, size_t size),主要传递物理地址和相应的大小,映射后,就可以像访问内存般访问外设寄存器。

dma_alloc_coherent

用于分配一块内存一致的内存,即在DMA操作中对CPU和DMA设备都是一致的,常用于dma操作的buffer,不带cache。函数原型是void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp),dev 标识设备结构体指针,用于指定哪个设备需要分配内存,而size 则是分配内存的大小,dma_handle 是返回的DMA地址,用于设备访问这个内存,gfp则是内存分配标识。

在使用dma buffer的时候,还会有一种使用方式,那就是应用申请了buffer,然后将buffer的fd传递到内核,内核需要通过fd获取dma_buf的句柄再进行访问,相应的一些调用如下:

/* 通过fd或者dma_buf的句柄 */
struct dma_buf *dmabuf = dma_buf_get(dmabuffer_fd);
/* 将dma_buf与设备关联起来,以便它能够进行DMA读写操作 */
struct dma_buf_attachment *attachment = dma_buf_attach(dmabuf, &pdev->dev);
/* 将dma buffer映射到设备 */
struct sg_table *sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL);
/* 获取dma buffer地址 */
dma_addr_t dma_address = sg_dma_address(sgt->sgl);

上面的这个操作,其中的dma_buf_attach关联设备,需要确认关联的设备是正确的,否则设备访问buffer的时候,会出现L1 PageTable Invalid以及xxx is not mapped!的报错,同时,在设备的dts中,也需要配置可访问iommu,增加iommus = <&mmu_aw 0 0>;的配置信息。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 入门 PyQt6 看过来(项目)27 在线购物-商品选购
  • 深入理解 Go 语言并发 map 安全使用
  • 【SpringBoot3】双向实时通讯 websocket
  • 第二章 方法与方法重载
  • 在HFSS中对曲线等结构进行分割(Split)
  • ubuntu 安装两个nginx实例时的坑,非默认nginx实例配置修改总也不生效的问题
  • HTML及CSS面试题4
  • 66_1JSON【浏览器中处理JSON、Java中处理JSON(FastJSON、Jackson)】、Java中的bean
  • XML外部实体注入
  • 使用docxtemplater-image-module-free时支持动态获取图片大小
  • SpringBoot:将单体项目拆分成微服务项目
  • 【PGCCC】pg_bestmatch.rs:使用 BM25 提升您的 PostgreSQL 文本查询#PCA
  • Windows下添加开机启动项
  • Vue.js 中的指令(Vue自定义指令)
  • 在小程序添加公司官网访问
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 《剑指offer》分解让复杂问题更简单
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • CentOS 7 修改主机名
  • Git 使用集
  • java第三方包学习之lombok
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • nginx 负载服务器优化
  • Sass Day-01
  • Zepto.js源码学习之二
  • 对JS继承的一点思考
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 两列自适应布局方案整理
  • 前端相关框架总和
  • 巧用 TypeScript (一)
  • 运行时添加log4j2的appender
  • 走向全栈之MongoDB的使用
  • 白色的风信子
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • 数据库巡检项
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • # .NET Framework中使用命名管道进行进程间通信
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • (+4)2.2UML建模图
  • (1) caustics\
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (20050108)又读《平凡的世界》
  • (Java)【深基9.例1】选举学生会
  • (二)JAVA使用POI操作excel
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (四) 虚拟摄像头vivi体验
  • (四)库存超卖案例实战——优化redis分布式锁
  • (一) storm的集群安装与配置
  • (一)项目实践-利用Appdesigner制作目标跟踪仿真软件
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • .bat批处理(一):@echo off