当前位置: 首页 > 编程日记 > 正文

内核中的内存申请:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages【转】

转自:http://www.cnblogs.com/yfz0/p/5829443.html

在内核模块中申请分配内存需要使用内核中的专用API:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages;当然,设备驱动程序也不例外;
对于提供了MMU功能的处理器而言,Linux提供了复杂的内存管理系统,使得进程所能访问到的地址空间可以达到4GB;而这4GB的空间又被划分为两个部分:0GB~3GB(PAGE_OFFSET,x86中的值是0xC0000000)的区域被用作进程的用户空间,3GB~4GB的区域被用作内核空间;
在内核空间中,从3GB到vmalloc_start之间的这段地址区域作为物理内存映射区使用,该段映射区域内包含了内核镜像、物理页框表mem_map等等,比如,我们使用的系统物理内存为160MB,那么,3GB~3GB+vmalloc_start之间的区域就应该是映射的物理内存;在物理内存映射区域之后,就是虚拟内存vmalloc区域;对于160MB的系统而言,vmalloc_start的位置就应该在3GB+160MB位置附近(在物理内存映射区与vmalloc_start位置之间还存在一个8M的gap来防止越界),vmalloc_end的位置接近4GB的位置(系统会在最后的位置处保留一片128KB大小的区域专用于页面映射);
一、kmalloc
#include <linux/slab.h>
static inline void *kmalloc(size_t size, gfp_t flags);
参数:size:指定要分配的块的大小,单位是字节;flags:指定分配内存时的控制方式;
该函数用于在内核空间中分配内存使用,它的返回速度快(除非被阻塞),并且对其分配的内存不进行任何初始化(清零)操作,分配的内存区域仍然保留有他原有的内容;
kmalloc申请得到的是物理内存,位于物理内存映射区,而且在物理地址上是连续的;但是kmalloc返回的内存地址却是虚拟地址(线性地址),返回的这个虚拟地址(线性地址)与真实的物理地址之间仅仅相差一个固定的偏移值;因此,kmalloc申请得到的物理内存块的首地址与其返回的虚拟地址之间存在着比较简单的转换关系;通过内核提供的函数virt_to_phys()可以实现该虚拟地址到真实的内核物理地址之间的转换:
#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
static inline unsigned long virt_to_phys(volatile void* address)
{
 return __pa(address);
}
参数address是kmalloc返回的一个虚拟地址;该转换过程就是虚拟地址减去3GB(PAGE_OFFSET=0xC0000000);
一般情况下,PAGE_OFFSET=3*1024*1024*1024=0xC0000000(3G);
与之对应的函数就是phys_to_virt()用于把内核物理地址转换为虚拟地址:
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
static inline void * phys_to_virt(unsigned long address)
{
 return __va(address);
}
这两个函数都定义在include/asm-i386/io.h中;
kmalloc()函数用于小块内存的申请,最小可以申请的内存是32字节或64字节,最大可以申请的内存是128KB-16,其中,被减掉的16个字节用于存储页描述符结构;这些都依赖于体系架构所使用的页面大小;kmalloc申请的内存在物理地址上是连续的,这对于要进行DMA传输的设备来说,是非常重要的;
kmalloc()的内存分配是基于slab机制实现的,slab机制是为分配小内存而提供的一种高效的机制;但是slab机制也不是独立的,它本身也是在页分配器的基础上来划分更细粒度的内存供调用者使用;也就是说,系统先使用页分配器分配以页为最小单位的连续物理地址,然后,kmalloc()再在这个基础上根据调用者的需要进行切分的;另外,slab机制分配的内存在物理地址和虚拟地址(线性地址/逻辑地址)上都是连续的;
对于kmalloc()申请的内存,需要使用kfree()函数来释放;
备注:kmalloc是基于slab机制实现的;
二、get_free_pages
#include <asm/pages.h>
fastcall unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
{
 struct page * page;
 page = alloc_pages(gfp_mask, order);
 if (!page)
  return 0;
 return (unsigned long) page_address(page);
}
参数gfp_mask用于指定申请内存时的控制方式,order用于指定申请的页数;它申请的内存位于(PAGE_OFFSET,HIGH_MEMORY)之间;
__get_free_pages()函数是页面分配器提供给调用者的最底层的内存分配函数,它申请的内存也是连续的物理内存,同样位于物理内存映射区;它是基于buddy机制实现的;在使用buddy机制实现的物理内存管理系统中,最小的分配粒度(单位)也是以页为单位的;在__get_free_pages()内部通过调用alloc_pages()来分配物理内存页;
__get_free_page()函数分配的是连续的物理内存,处理的是连续的物理地址,但是返回的也是虚拟地址(线性地址);如果想要得到正确的物理地址,也需要使用virt_to_phys()可进行转换;
对于__get_free_pages()函数申请的内存,需要使用__free_pages()函数来释放;
备注:__get_free_pages是基于buddy机制实现的;
三、vmalloc
#include <linux/vmalloc.h>
void* vmalloc(unsigned long size)
{
 return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
}
void* __vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
{
 return __vmalloc_node(size, gfp_mask, prot, -1);
}
void* __vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
{
 struct vm_struct *area;
 size = PAGE_ALIGN(size);
 if(!size || (size >> PAGE_SHIFT) > num_physpages)
   return NULL;
 area = get_vm_area_node(size, VM_ALLOC, node);
 if(!area)
   return NULL;
 return __vmalloc_area_node(area, gfp_mask, prot, node);
}
void* __vmalloc_area_node(struct vm_struct* area, gfp_t gfp_mask, pgprot_t prot, int node);
void* __vmalloc_area(struct vm_struct* area, gfp_t gfp_mask, pgprot_t prot)
{
 return __vmalloc_area_node(area, gfp_mask, prot, -1);
}
vmalloc()函数也是用于申请内存的,但是它申请的内存是位于vmalloc_start到vmalloc_end之间的虚拟内存;它申请的内存在虚拟地址(线性地址/逻辑地址)上是连续的,但是并不要求在物理地址上连续,并且返回的地址与物理地址之间没有简单的转换关系;
vmalloc()函数适用于大块内存的申请环境中;但是它申请的内存不能直接用于DMA传输;因为DMA传输需要使用物理地址连续的内存块;
对于vmalloc()申请的内存,需要使用vfree()函数来释放;
备注:vmalloc是基于slab机制实现的;
四、比较
1).kmalloc/__get_free_pages申请的内存块都在物理内存映射区,即在(PAGE_OFFSET,HIGH_MEMORY)之间,处理的都是物理地址,且保证在物理地址空间上是连续的;二者返回的都是虚拟地址,如果需要得到正确的物理地址,需要使用virt_to_phys()进行转换;但是,kmalloc和vmalloc都是以字节为单位进行申请,而__get_free_pages()则是以页为单位进行申请;
2).vmalloc函数申请的内存块位于虚拟内存映射区,即在(VMALLOC_START,VMALLOC_END)之间,处理的都是虚拟内存,且保证在虚拟地址空间上是连续的,但是在物理地址空间上不要求连续;一般作为交换区、模块的内存使用;
3).kmalloc和vmalloc都是基于slab机制实现的,但是kmalloc的速度比vmalloc的速度快;__get_free_pages是基于buddy机制实现的,速度也较快;
4).kmalloc用于小块内存的申请,通常,一次所能申请的内存块的大小在(32/64字节,128KB-16)之间;而vmalloc可以用于分配大块内存的场合;
5).kmalloc申请的内存块在物理地址空间上是连续的,所以它申请的内存块可以直接用于DMA传输;vmalloc申请的内存块在虚拟地址空间上连续,但是在物理地址空间上不要求连续,所以它申请的内存块不能直接用于DMA传输;
6).kmalloc申请的内存块用kfree释放;vmalloc申请的内存块用vfree释放;__get_free_pages申请的内存页用__free_pages释放;
7).kmalloc申请得到的地址称为内核逻辑地址,vmalloc申请得到的地址称为内核虚拟地址;
五、其它函数
1).static inline void *kzalloc(size_t size, gfp_t flags);
 该函数比kmalloc多了一个功能,就是会把申请得到的内存块初始化为0;
2).static inline void* kcalloc(size_t n, size_t size, gfp_t flags)
   {
     if(n != 0 && size > ULONG_MAX / n)
        return NULL;
     return kzalloc(n * size, flags);
   }
   该函数用于申请一个数组的内存空间,并把申请得到的内存都初始化为0;
六、GFP标记
kmalloc、kzalloc、kcalloc、vmalloc、get_free_pages函数在调用时都有一个gfp_t类型的控制标记flags;这个标记用于控制申请内存时的内存分配控制方式; #include <linux/gfp.h>
GFP的标记有两种:带双下划线前缀的和不带双下划线前缀的;
不带双下划线前缀的GFP标志:
GFP_ATOMIC:用于在中断上下文和进程上下文之外的其它代码中分配内存;从不睡眠;
GFP_KERNEL:内核正常分配内存;可能睡眠;
GFP_USER  :用于为用户空间页分配内存;可能睡眠;
GFP_HIGHUSER:如同GFP_USER,但它是从高端内存中申请;
GFP_NOIO和GFP_NOFS:功能如同GFP_KERNEL,但是它俩增加限制到内核能做的来满足请求;GFP_NOFS分配不允许进行任何文件系统调用,而GFP_NOIO分配根本不允许进行任何IO初始化;它俩主要用于文件系统和虚拟内存代码,那里允许一个分配睡眠,但是递归的文件系统调用会是个坏主意;
带有双下划线前缀的GFP标志:
__GFP_DMA:这个标志要求分配的内存在能够进行DMA的内存区;平台依赖的;
__GFP_HIGHMEM:这个标志指示分配的内存可以位于高端内存区;平台依赖的;
__GFP_COLD:正常地,内存分配器尽力返回"缓冲热"的页---可能在处理器缓冲中找到的页;相反,这个标志请求一个"冷"页---在一段时间内没被使用的页;它对分配页做DMA读是很有用的,此时在处理器缓冲中出现是没用的;
__GFP_NOWARN:这个标志用于分配内存时阻止内核发出警告,当一个分配请求无法满足时;
__GFP_HIGH:这个标志标识了一个高优先级请求,它被允许来消耗甚至被内核保留给紧急状况的最后的内存页;
__GFP_REPEAT:分配器的动作;当分配器有困难满足一个分配请求时,通过重复尝试的方式来"尽力尝试",但是分配操作仍然有可能失败;
__GFP_NOFAIL:分配器的动作;当分配器有困难满足一个分配请求时,这个标志告诉分配器不要失败,尽最大努力来满足分配请求;
__GFP_NORETRY:分配器的动作;当分配器有困难满足一个分配请求时,这个标志告诉分配器立即放弃,不再做任何尝试;
通常,一个或多个带双下划线前缀的标记相或,即可得到对应的不带双下划线前缀的标记;
最常用的标记就是GFP_KERNEL,它的意思就是当前的这个分配代表运行在内核空间的进程而进行的;换句话说,这意味着调用函数是代表一个进程在执行一个系统调用;使用GFP_KERNEL标记,就意味着kmalloc能够使当前进程在少内存的情况下通过睡眠来等待一个内存页;因此,一个使用GFP_KERNEL的函数必须是可重入的,且不能在原子上下文中运行;当当前进程睡眠,内核采取正确的动作来定位一些空闲的内存页,或者通过刷新缓存到磁盘或者交换出去一个用户进程的内存页;
如果一个内存分配动作发生在中断处理或内核定时器的上下文中时,当前进程就不能被设置为睡眠,也就不能再使用GFP_KERNEL标志了,此时应该使用GFP_ATOMIC标志来代替;正常地,内核试图保持一些空闲页以便来满足原子的分配;当使用GFP_ATOMIC标志时,kmalloc标志能够使用甚至最后一个空闲页;如果这最后一个空闲页不存在,那分配就会失败;

相关文章:

以实例说明如何使用C#从数据库中提取数据,按要求自动生成定制的Excel表格

最近因为需要学习了一下使用C#操作Excel表格&#xff0c;现在把我使用C#如何定制表格的过程提供给需要的兄弟&#xff1a; /*从数据库提取数据*/ string strconn"packet size4096;user idsa;data sourcelocalhost;persist security infoTrue;initial catalogDatabase;pa…

7_7_2013 E.Function

2019独角兽企业重金招聘Python工程师标准>>> Problem E: Function Time Limit: 1 Sec Memory Limit: 32 MB Submit: 52 Solved: 26 [ Submit][ Status][ Web Board] Description Define a function f(n)(f(n-1)1)/f(n-2). You already got f(1) and f(2). Now…

快手日入数据量超 5120TB,数据管治如何做?

​近日&#xff0c;快手大数据团队联合“快手中学”&#xff0c;举办“快手数据管治技术交流会”&#xff0c;各行业数据相关开发者报名参与。在海量的 UGC 数据、业务数据、用户数据背后&#xff0c;支撑快手数据业务的快手大数据平台&#xff0c;秉承“以领先的大数据技术&am…

LINUX创建www的用户组和用户,并且不允许登录权限:

# id www id: www&#xff1a;无此用户 # groupadd www # useradd -g www -s /sbin/nologin www # id www uid501(www) gid501(www) 组501(www) 转载于:https://www.cnblogs.com/cnbing/p/6957239.html

GridView自定义分页导航

自己做的一个项目中所运用到的技术&#xff1a;| 1. 日历控件&#xff08;带时分秒&#xff09;2. GridView 批量删除,自定义分页,定位页码3. GridView 修改4. GridView 鼠标经过改变行的颜色效果如下&#xff1a;HTML&#xff1a;<% Page L…

一文看懂AI数据采集标注未来三年的发展和趋势

影响人工智能发展的三大要素分别是数据、算法、算力&#xff0c;限于篇幅&#xff0c;本篇内容将重点分析未来几年内人工智能所需要的数据趋势及探讨数据服务商发展方向。 作为AI数据采集标注的领先企业&#xff0c;云测数据分析认为人工智能在经历了算法研究、技术扩张和商业落…

HTTP 状态代码及其定义

相关文章&#xff1a;http://ruby-china.org/topics/12384 所有 HTTP 状态代码及其定义。  代码 指示 2xx 成功 200 正常&#xff1b;请求已完成。 201 正常&#xff1b;紧接 POST 命令。 202 正常&#xff1b;已接受用于处理&#xff0c;但处理尚未完成。 203 正常&#xff1…

html5地理定位数据

2019独角兽企业重金招聘Python工程师标准>>> <html><head><meta charset"UTF-8"/> <meta name"viewport" content"widthdevice-width, initial-scale1.0"><script type"text/javascript">fu…

GridView控件修改、删除示例(修改含有DropDownList控件)

GridView控件修改、删除例子&#xff0c;修改时含有DropDownList控件。示例运行效果图&#xff1a;GridViewUp.aspx文件代码&#xff1a; <% Page Language"C#"AutoEventWireup"true"CodeFile"GridViewUp.aspx.cs"Inherits"gridview_Gri…

国产AI芯片加速,鲲云携手浪潮推出数据流AI服务器

近日&#xff0c;鲲云科技携手浪潮基于星空X3加速卡推出新一代的数据流AI服务器&#xff0c;定位高性能图像视频智能分析的AI计算加速&#xff0c;支持智慧城市、智能制造、智慧油田、智慧工地、智算中心等典型AI应用场景&#xff0c;这是双方“元脑生态计划”战略签约后推进的…

织梦dedecms如何快速使用拼音首字母做栏目名称

织梦默认使用拼音为保存目录的时候使用的是中文全拼&#xff0c;当遇到栏目名称比较长的时候目录名称看起来有点冗长&#xff0c;这时候大多数站长喜欢使用拼音首字母作为栏目的保存目录&#xff0c;那么有没有什么快速的办法能让我们快速的使用首字母作为栏目名称呢&#xff1…

移动应用AI化成新战场?详解苹果最新Core ML模型构建基于机器学习的智能应用...

Google刚刚息鼓&#xff0c;苹果又燃战火&#xff01;这一战&#xff0c;来自移动应用的AI化之争。近日&#xff0c;苹果发布专为移动端优化的Core ML后&#xff0c;移动开发者对此的需求到底有多强烈&#xff1f;去年大获成功的AI应用Prisma又能告诉我们什么&#xff1f;苹果的…

mysql5.1 与mysql5.5 字符集设置区别

在mysql5.1版本中设置字符集[mysqld]default-character-setutf8在mysql-5.5中设置字符集[mysqld]character_set_serverutf8转载于:https://blog.51cto.com/enable/1247132

@所有技术社区,年度”社区之星“开选,快来盘点各家技术大佬

活动简介那些积极探索技术边界并持续对社区做出贡献的开发者是真正的技术英雄&#xff0c;是开发者的学习榜样&#xff0c;也是各个技术社区发展的生命力&#xff01;2021年伊始&#xff0c;CSDN 为所有技术社区特别准备了一份年终福利&#xff01;CSDN 向所有技术社区&#xf…

Gridview][UpdateCommand的写法要点]

在ASP.NET2.0中的GridView为我们浏览更新数据提供了一个方便的途径。我们只需要添加一个sqldatasouce控件和一个GridView&#xff0c;再为sqldatasource写上正确的UpdateCommand语句就可以达到自动更新数据的目的。基本上无需手写更新代码:但在写UpdateCommand语句时&#xff0…

BZOJ 1040 ZJOI2008 骑士 树形DP

题目大意&#xff1a;给定一个基环树林&#xff0c;每一个点上有权值&#xff0c;要求选择一个权值和最大的点集&#xff0c;要求点集中的随意两个点之间不能直接相连 最大点独立集……考虑到n<100W&#xff0c;网络流铁定跑不了&#xff0c;于是我们考虑树形DP 对于每棵基环…

在GridView内访问特定控件

本文我将为你演示如何访问GridView中的特定控件。我们会看到怎样去访问TextBox控件&#xff0c;DropDownList控件以及ListBox控件。 添加控件到GridView&#xff1a; 你可以简单地使用 <ItemTemplate>选项在GridView控件中增加不同的控件。 填充列表框和下拉框控件 第…

解决 apache 2.4.1 无法解析shtml中的expr指令问题

2019独角兽企业重金招聘Python工程师标准>>> apache 2.4.1上运行内嵌<!--#if expr${mobile}-->的shtml格式文件&#xff0c;在页面上提示一下错误&#xff1a; [an error occurred while processing this directive] SSI对shtml格式的配置是正常的&#xff0c…

新华三发布H3C Workspace数字工作空间:只需一张屏,程序员也能随时随地在云上写代码

随着数字时代的不断发展&#xff0c;工作模式经历了面对面的“纸笔”模式、办公室场景下的“PC电脑”模式、以及远程办公场景下的“手机协同软件”模式&#xff0c;如今&#xff0c;尤其伴随2020年的疫情催生&#xff0c;更迎来以“云屏”架构为核心的未来工作模式。 “云屏”…

centos 安装 NTFS支持

2019独角兽企业重金招聘Python工程师标准>>> 参考的原文网址&#xff1a; centos安装完之后&#xff0c;默认是不支持NTFS磁盘格式的&#xff0c;解决的方法之一就是安装NTFS-3G模块&#xff0c;但是默认的软件源是没有这个依赖库的&#xff0c;我们需要额外的阿里云…

linux基本命令详解の第一季

linux常用命令一&#xff1a;文件名命名规则1&#xff09;除了/之外&#xff0c;所有的字符都合法。 2)有些字符最好不用&#xff0c;如&#xff0c;空格、制表符、退格符和字符#&#xffe5;%&#xff08;&#xff09;-等。 3)避免使用.作为普通文件名的第一个字符,在linux系统…

asp.net 2.0中一次性更新所有GRIDVIEW的记录

在asp.net 2.0中&#xff0c;gridview控件是十分不错的控件。有的时候&#xff0c;可能一个GRIDVIEW控件中 的各行都是文本框&#xff0c;如何一次性更新所有修改过的记录呢&#xff1f;有两种方法&#xff0c;一种是使用sqldatasource来更新 所有记录&#xff0c;但这个方法比…

清华 CVer 对自监督学习的一些思考

来源 | Jack Cui责编 | 晋兆雨头图 | CSDN 下载自视觉中国众所周知&#xff0c;机器学习大致可分为有监督学习和无监督学习。自监督学习作为无监督学习的一个特例&#xff0c;可以理解它是一种没有人工标注标签的监督学习&#xff0c;即没有人类参与的监督学习。但标签仍然存在…

使用 HTML5 时如何改进移动 Web 应用开发

因为 HTML 有易于维护&#xff0c;更好的互动性&#xff0c;更快的开发等优点&#xff0c;被越来越多的应用在移动 Web 应用程序的开发中。HTML5 是一种标记语言&#xff0c;与 Javascript 和 CSS 一起&#xff0c;构成了每个开发者的核心技术&#xff0c;HTML5 拥有自己的 API…

hibernate.cfg.xml详细配置

<!--标准的XML文件的起始行&#xff0c;version1.0表明XML的版本&#xff0c;encodinggb2312表明XML文件的编码方式--> <?xml version1.0 encodinggb2312?> <!--表明解析本XML文件的DTD文档位置&#xff0c;DTD是Document Type Definition 的…

ASP.NET2.0中用Gridview控件操作数据

在ASP.NET 2.0中&#xff0c;加入了许多新的功能和控件&#xff0c;相比asp.net 1.0/1.1&#xff0c;在各方面都有了很大的提高。其中&#xff0c;在数据控件方面&#xff0c;增加了不少控件&#xff0c;其中的Gridview控件功能十分强大。在本文中&#xff0c;将探讨Gridview控…

人工智能在消费领域,都做了哪些事?

来源 | 人民数字FINTECH责编 | 晋兆雨头图 | CSDN 下载自视觉中国#AI正在融入生活的方方面面近日&#xff0c;北京一购物中心开业&#xff0c;在开业当天迎宾的并不是“人”而是“机器人”。在这里你可以体验到高科技高颜值的机器人“迎宾团”给你带来的贵宾级的服务&#xff0…

poj 1681 Painter#39;s Problem(高斯消元)

http://poj.org/problem?id1681 求最少经过的步数使得输入的矩阵全变为y。 思路&#xff1a;高斯消元求出自由变元。然后枚举自由变元&#xff0c;求出最优值。注意依据自由变元求其它解及求最优值的方法。 #include <stdio.h> #include <algorithm> #include <…

ASP.NET 2.0中GRIDVIEW排序

在 headertemplate中加一张UP.GIF和DOWN.GIF(就是升序&#xff0c;倒序的示意图&#xff09; % Page Language"C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html…

基础篇9-python基本数据结构-列表

基础篇9-python基本数据结构-列表一.列表&#xff1a;1.有序的集合2.通过偏移来索引&#xff0c;从而读取数据3.支持内嵌a [[1,2,3],[4,5,6]]4.可变类型a[0][1] 7二.切片a [1,2,3,4,5,6,7]a[0:3:1]0 索引开始3 索引结束1 间隔(默认1)正向索引 它是从左往右索引假如要取出1234…