malloc内核实现原理

ioctl compat_ioctl unlock_ioctl <br />区别:<br />ioctl 和 unlock_ioctl <br />ioctl 不会lock_kernel()<br /> <br />compat_ioctl被使用在用户空间为32位模式,而内核运行在64位模式时。这时候,需要将64位转成32位。<br /> <br />引用<br />http://blog.chinaunix.net/u1/38994/showart_2248151.html<br />对几个ioctl执行顺序的分析<br /> <br />关于ioctl,unlock 阅读详情

考虑这样一种常见的情况:用户进程调用malloc()动态分配了一块内存空间,再对这块内存进行访问。这些用户空间发生的事会引发内核空间的那些反映?本文将简单为您解答。

1.brk系统调用服务例程

malloc()是一个API,这个函数在库中封装了系统调用brk。因此如果调用malloc,那么首先会引发brk系统调用执行的过程。brk()在内核中对应的系统调用服务例程为SYSCALL_DEFINE1(brk, unsigned long, brk),参数brk用来指定heap段新的结束地址,也就是重新指定mm_struct结构中的brk字段。

brk系统调用服务例程首先会确定heap段的起始地址min_brk,然后再检查资源的限制问题。接着,将新老heap地址分别按照页大小对齐,对齐后的地址分别存储与newbrk和okdbrk中。

brk()系统调用本身既可以缩小堆大小,又可以扩大堆大小。缩小堆这个功能是通过调用do_munmap()完成的。如果要扩大堆的大小,那么必须先通过find_vma_intersection()检查扩大以后的堆是否与已经存在的某个虚拟内存重合,如何重合则直接退出。否则,调用do_brk()进行接下来扩大堆的各种工作。

1SYSCALL_DEFINE1(brk, unsigned long, brk)
2{
3        unsigned long rlim, retval;
4        unsigned long newbrk, oldbrk;
5        struct mm_struct *mm = current->mm;
6        unsigned long min_brk;
7 
8        down_write(&mm->mmap_sem);
9 
10#ifdef CONFIG_COMPAT_BRK
11        min_brk = mm->end_code;
12#else
13        min_brk = mm->start_brk;
14#endif
15        if (brk < min_brk)
16                goto out;
17 
18        rlim = rlimit(RLIMIT_DATA);
19        if (rlim < RLIM_INFINITY && (brk - mm->start_brk) +
20                        (mm->end_data - mm->start_data) > rlim)
21 
22        newbrk = PAGE_ALIGN(brk);
23        oldbrk = PAGE_ALIGN(mm->brk);
24        if (oldbrk == newbrk)
25                goto set_brk;
26 
27        if (brk brk) {
28                if (!do_munmap(mm, newbrk, oldbrk-newbrk))
29                        goto set_brk;
30                goto out;
31        }
32 
33        if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
34                goto out;
35 
36        if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
37                goto out;
38set_brk:
39        mm->brk = brk;
40out:
41        retval = mm->brk;
42        up_write(&mm->mmap_sem);
43        return retval;
44}

brk系统调用服务例程最后将返回堆的新结束地址。

2.扩大堆

用户进程调用malloc()会使得内核调用brk系统调用服务例程,因为malloc总是动态的分配内存空间,因此该服务例程此时会进入第二条执行路径中,即扩大堆。do_brk()主要完成以下工作:

1.通过get_unmapped_area()在当前进程的地址空间中查找一个符合len大小的线性区间,并且该线性区间的必须在addr地址之后。如果找到了这个空闲的线性区间,则返回该区间的起始地址,否则返回错误代码-ENOMEM;

2.通过find_vma_prepare()在当前进程所有线性区组成的红黑树中依次遍历每个vma,以确定上一步找到的新区间之前的线性区对象的位置。如果addr位于某个现存的vma中,则调用do_munmap()删除这个线性区。如果删除成功则继续查找,否则返回错误代码。

3.目前已经找到了一个合适大小的空闲线性区,接下来通过vma_merge()去试着将当前的线性区与临近的线性区进行合并。如果合并成功,那么该函数将返回prev这个线性区的vm_area_struct结构指针,同时结束do_brk()。否则,继续分配新的线性区。

4.接下来通过kmem_cache_zalloc()在特定的slab高速缓存vm_area_cachep中为这个线性区分配vm_area_struct结构的描述符。

5.初始化vma结构中的各个字段。

6.更新mm_struct结构中的vm_total字段,它用来同级当前进程所拥有的vma数量。

7.如果当前vma设置了VM_LOCKED字段,那么通过mlock_vma_pages_range()立即为这个线性区分配物理页框。否则,do_brk()结束。

可以看到,do_brk()主要是为当前进程分配一个新的线性区,在没有设置VM_LOCKED标志的情况下,它不会立刻为该线性区分配物理页框,而是通过vma一直将分配物理内存的工作进行延迟,直至发生缺页异常。

3.缺页异常的处理过程

经过上面的过程,malloc()返回了线性地址,如果此时用户进程访问这个线性地址,那么就会发生缺页异常(Page Fault)。整个缺页异常的处理过程非常复杂,我们这里只关注与malloc()有关的那一条执行路径。

当CPU产生一个异常时,将会跳转到异常处理的整个处理流程中。对于缺页异常,CPU将跳转到page_fault异常处理程序中:

1//linux-2.6.34/arch/x86/kernel/entry_32.S
2ENTRY(page_fault)
3        RING0_EC_FRAME
4        pushl $do_page_fault
5        CFI_ADJUST_CFA_OFFSET 4
6        ALIGN
7error_code:
8        …………
9        jmp ret_from_exception
10        CFI_ENDPROC
11END(page_fault)

该异常处理程序会调用do_page_fault()函数,该函数通过读取CR2寄存器获得引起缺页的线性地址,通过各种条件判断以便确定一个合适的方案来处理这个异常。

3.1.do_page_fault()

该函数通过各种条件来检测当前发生异常的情况,但至少do_page_fault()会区分出引发缺页的两种情况:由编程错误引发异常,以及由进程地址空间中还未分配物理内存的线性地址引发。对于后一种情况,通常还分为用户空间所引发的缺页异常和内核空间引发的缺页异常。

内核引发的异常是由vmalloc()产生的,它只用于内核空间内存的分配。显然,我们这里需要关注的是用户空间所引发的异常情况。这部分工作从do_page_fault()中的good_area标号处开始执行,主要通过handle_mm_fault()完成。

1//linux-2.6.34/arch/x86/mm/fault.c
2dotraplinkage void __kprobes
3do_page_fault(struct pt_regs *regs, unsigned long error_code)
4{
5…… ……
6good_area:
7        write = error_code & PF_WRITE;
8 
9        if (unlikely(access_error(error_code, write, vma))) {
10                bad_area_access_error(regs, error_code, address);
11                return;
12        }
13        fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
14…… ……
15}
3.2.handle_mm_fault()

该函数的主要功能是为引发缺页的进程分配一个物理页框,它先确定与引发缺页的线性地址对应的各级页目录项是否存在,如何不存在则分进行分配。具体如何分配这个页框是通过调用handle_pte_fault()完成的。

1int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
2                unsigned long address, unsigned int flags)
3{
4        pgd_t *pgd;
5        pud_t *pud;
6        pmd_t *pmd;
7        pte_t *pte;
8        …… ……
9        pgd = pgd_offset(mm, address);
10        pud = pud_alloc(mm, pgd, address);
11        if (!pud)
12                return VM_FAULT_OOM;
13        pmd = pmd_alloc(mm, pud, address);
14        if (!pmd)
15                return VM_FAULT_OOM;
16        pte = pte_alloc_map(mm, pmd, address);
17        if (!pte)
18                return VM_FAULT_OOM;
19          return handle_pte_fault(mm, vma, address, pte, pmd, flags);
20}
3.3.handle_pte_fault()

该函数根据页表项pte所描述的物理页框是否在物理内存中,分为两大类:

请求调页:被访问的页框不再主存中,那么此时必须分配一个页框。

写时复制:被访问的页存在,但是该页是只读的,内核需要对该页进行写操作,此时内核将这个已存在的只读页中的数据复制到一个新的页框中。

用户进程访问由malloc()分配的内存空间属于第一种情况。对于请求调页,handle_pte_fault()仍然将其细分为三种情况:

1static inline int handle_pte_fault(struct mm_struct *mm,
2                struct vm_area_struct *vma, unsigned long address,
3                pte_t *pte, pmd_t *pmd, unsigned int flags)
4{
5        …… ……
6        if (!pte_present(entry)) {
7                if (pte_none(entry)) {
8                        if (vma->vm_ops) {
9                                if (likely(vma->vm_ops->fault))
10                                        return do_linear_fault(mm, vma, address,
11                                                pte, pmd, flags, entry);
12                        }
13                        return do_anonymous_page(mm, vma, address,
14                                                 pte, pmd, flags);
15                }
16                if (pte_file(entry))
17                        return do_nonlinear_fault(mm, vma, address,
18                                        pte, pmd, flags, entry);
19                return do_swap_page(mm, vma, address,
20                                        pte, pmd, flags, entry);
21        }
22…… ……
23}

1.如果页表项确实为空(pte_none(entry)),那么必须分配页框。如果当前进程实现了vma操作函数集合中的fault钩子函数,那么这种情况属于基于文件的内存映射,它调用do_linear_fault()进行分配物理页框。否则,内核将调用针对匿名映射分配物理页框的函数do_anonymous_page()。

2.如果检测出该页表项为非线性映射(pte_file(entry)),则调用do_nonlinear_fault()分配物理页。

3.如果页框事先被分配,但是此刻已经由主存换出到了外存,则调用do_swap_page()完成页框分配。

由malloc分配的内存将会调用do_anonymous_page()分配物理页框。

3.4.do_anonymous_page()

此时,缺页异常处理程序终于要为当前进程分配物理页框了。它通过alloc_zeroed_user_highpage_movable()来完成这个过程。我们层层拨开这个函数的外衣,发现它最终调用了alloc_pages()。

1static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
2                unsigned long address, pte_t *page_table, pmd_t *pmd,
3                unsigned int flags)
4{
5…… ……
6        if (unlikely(anon_vma_prepare(vma)))
7                goto oom;
8        page = alloc_zeroed_user_highpage_movable(vma, address);
9        if (!page)
10                goto oom;
11…… ……
12}

经过这样一个复杂的过程,用户进程所访问的线性地址终于对应到了一块物理内存。

malloc 函数背后的实现原理——内存池 相对于栈而言,堆这片内存面临着一个稍微复杂的行为模式:在任意时刻,程序可能发出请求,要么申请一段内存,要么释放一段已经申请过的内存,而且申请的大小从几个字节到几个 GB 都有可能,我们不能假设程序一次申请多少堆空间,因此,堆的管理显得较为复杂。内存池的研究重点不是向操作系统申请内存,而是对已申请到的内存的管理,这涉及到非常复杂的算法,是一个永远也研究不完的课题,除了 C 标准库自带的 malloc(),还有一些第三方的实现,比如 Goolge 的 tcmalloc和 jemalloc。 阅读详情

相关推荐

malloc的一点点东西

提问:malloc是不是线程安全的? 提问:malloc是如何实现的? 线程安全:需要解决多个线程调用函数时访问共享资源的冲突 可重入:需要不再函数内部使用静态或全局数据,不反悔静态或全局数据,也不调用不可重入函数 malloc函数线程安全但不可重入   malloc函数在用户空间要自己管理各进程共享的内存链表,由于有共享资源访问,本身会造成线程不安全。为了做到线程安全,加锁保护。递归锁,如果当程...

phonycat的博客 600

Linux系统调用源码分析(三)

前文讲了Linux系统调用的定义、函数实现及系统处理的流程。现在我们继续以ARMv8为例讲一下与系统调用相关的异常处理的内容,包括异常的级别与类型,异常向量表,系统调用的异常处理源码等。 一、异常的级别与类型 按ARM官方文档中的定义,“Exceptions are conditions or system events that require some action by privileged software (an exception handler) to ensure smooth.

weixin_39094034的博客 1048

内存管理相关——malloc,mmap,mlock与unevictable列表

内核内存管理,malloc,brk,mmap,mlock,UNEVICTABLE列表,MIGRATE_TYPE

zhaoxin1989的博客 2597

malloc()之后,内核发生了什么?

考虑这样一种常见的情况:用户进程调用malloc()动态分配了一块内存空间,再对这块内存进行访问。这些用户空间发生的事会引发内核空间的那些反映?本文将简单为您解答。 1.brk系统调用服务例程 malloc()是一个API,这个函数在库中封装了系统调用brk。因此如果调用malloc,那么首先会引发brk系统调用执行的过程。brk()在内核中对应的系统调用服务例程为SYSCALL_DE

Heron——Linux & ARM 5059

Linux内核--内存管理(八)malloc、free 实现原理

本文仔细来介绍一下malloc、free 实现的几种方式,顺便了解一下内存碎片的原因

文艺小少年的博客 942

optee内核中malloc函数的原理介绍

文章目录1、链接文件和汇编文件的对比分析2、malloc和calloc3、optee中的内核栈 1、链接文件和汇编文件的对比分析 可用查看optee的kern.ld.S文件,和下面的反汇编文件对比. out/arm-plat-xxxx/core/tee.elf: file format elf64-littleaarch64 out/arm-plat-xxxx/core/tee.elf architecture: aarch64, flags 0x00000112: EXEC_P, HAS_SYMS

baron-周贺贺-代码改变世界ctw 3219

Linux内存管理:虚拟地址空间布局

每一个进程的虚拟地址空间分为内核虚拟地址空间和用户虚拟地址空间两部分,内核虚拟地址空间为内核态代码和内核堆栈,所有进程的内核虚拟地址空间是复用的;Linux中可以通过cat /proc//maps命令查看指定进程的用户虚拟地址空间的映射,也可以通过pmap -x 查看,该命令实质上是基于/proc//maps实现的。在Linux ARM64中,如果页大小为4KB,使用3级或者4级页表转换,用户虚拟地址空间和内核虚拟地址空间都支持39位(512GB)或者48位(256TB)寻址范围。

m0_74282605的博客 647

从MySQL到数据仓库:手把手教你用Airflow实现自动化ETL流水线

本文详细介绍了如何利用Airflow构建从MySQL到数据仓库的自动化ETL流水线,涵盖增量抽取策略、PySpark转换优化和数据质量监控等实战细节。特别适合中小规模企业构建高效数据仓库,提升数据处理效率和质量。

rnn9storyteller的博客 827

Linux内核malloc()背后的实现原理——内存池

内存池的原理。内存池是一种将对象预先分配到一块连续的内存中,用时直接从这块内存中获取对象的高效内存分配方式。相对于栈而言,堆这片内存面临着一个稍微复杂的行为模式:在任意时刻,程序可能发出请求,要么申请一段内存,要么释放一段已经申请过的内存,而且申请的大小从几个字节到几个GB都有可能,我们不能假设程序一次申请多少堆空间,因此,堆的管理显得较为复杂。

TyearLin的专栏 784

解决Unity打包后粒子特效显示不全的问题

粒子特效在Editor下显示没有问题,但打包后出现部分粒子特效里的Mesh没有显示。

agoo_indie的博客 4381

高斯-约当(Gauss-Jordan)消元法——c++代码实现

具体算法的详细原理请参照此篇博客:高斯-约当(Gauss-Jordan)消元法下面是具体c++代码,不保证程序最优化,提供一个可行的程序,供大家改进,同时感谢大家提意见。——大神勿喷呀,我是一个小菜鸟~void Guss_Jordan(int n,double *a)//阶数n 系数矩阵a { double*a_temp = new double[n*n * 2];//a与单位矩阵的组合

zwz的学习之路 6873

Linux内核的malloc实现(Oracle的cache buffer影子)

分类: Linux Kernel2013-10-18 00:12 853人阅读 评论(1) 收藏 举报 Linux内核c语言 本文原创为freas_1990,转载请标明出处:http://blog.youkuaiyun.com/freas_1990/article/details/12845059   本文介绍一下malloc的原理,对mm感兴趣(或者对Oracle int

pi9nc的专栏 1527

android广告SDK原理详解(附源码)

广大的开发者吃糠咽菜开发了一两款APP,获取了一些流量后自然就会想到流量变现,一般情况下大家会选择到百度联盟或者Google Admob这些广告服务提供商注册开发者账号,在自己的APP里面加上几行广告位代码,集成他们的广告SDK包。然后谋事在人,成事在天,只有每天看报表查查收益。   也有一些开发者为了能够提高自己的变现水平,同时接入了好几家的sdk,哪家出的收益高一点我就出哪家的广告,但是这

rmcgogo的博客 1万+

malloc相关

 _malloc-_free模拟 - 网络开发技术研究,纯技术网 - JavaEye技术网站http://lin-style.javaeye.com/blog/256146求函数malloc的实现源代码~ C-C++ - C语言 - 优快云社区 community_youkuaiyun.comhttp://topic.youkuaiyun.com/t/20051114/18/4392766.html

pocketmatrix的专栏 579

STM32H743实战(三)-- 时钟系统配置

本文详细讲解了STM32H743时钟系统的配置方法,重点解析了为何选择400MHz而非理论极限480MHz作为稳定运行的主频。通过STM32CubeIDE环境,手把手指导如何从外部晶振配置、电源电压设置到PLL锁相环参数调整,最终实现稳定可靠的400MHz系统时钟,并提供了代码验证与常见问题排查指南。

weixin_29164185的博客 368

ubuntu20.04屏幕亮度无法调节(亮度条调节无效)的简单靠谱解决方案及踩坑历程

0 前言   网上关于ubuntu系统亮度无法调节的问题的解决方法有很多,但是绝大部分并没有什么用(这也是我自己的亲身体会),或者并不靠谱。在遍尝各路方法过后,我终于解决了这一棘手的问题(毕竟最大亮度真的非常刺眼,无法长时间直视),在此记录一下踩坑历程以及最后的解决方案。 1 Naive方法   由于使用需要,最近在电脑原有的win10基础上安装了ubuntu双系统。我安装的是当前新版的ubuntu20.04.2.0,靠谱的安装教程可以参考这里。   之前师兄说双ssd安装双系统会有系统启动引导的问题出现,

zeeq的博客 4万+

malloc实现

malloc实现 几个问题 malloc()函数是C语言中内存分配函数, - malloc()函数返回的内存是否马上就分配物理内存? - 如果不考虑libc,malloc分配100Byte,那么实际上内核是为其分配100Byte吗? - vm_normal_page()函数返回的什么样的页面的struct page数据结构?为什么内存管理代码中需要这个函数? - get_user_...

absinjun的博客 689

平头哥面试——芯片工程师面经

有5个成递增关系的32bit地址值,彼此之间的间隔大小不确定,可以把他们当成parameter来输入模块,现有一个32bit输入数据,要求用尽量少资源去的判断这个数处于哪2个地址之间(地址区间),并且输出区间号。a)后续大概会在9月下旬谈具体的薪资,他们现在也没想好今年应届生的薪资该给多少,到时候如果可以签三方的话可能会跳过意向书环节直接发offer,谈薪资肯定是通过电话来。b. 这边的CPU是自己研制的(玄铁),成都这边主要做存储和网络,都有SOC。a. 成都这边的网络部门具体做的业务有哪些?

FPGA/MATLAB学习教程/源码/项目合作开发 2688

Linux内核0.11版 lib/Malloc.c 文件分析

Linux 0.11版 lib/Malloc.c是一个内核内存动态分配模块,实现了内核内存的动态分配与释放。

分享技术,分享知识 1328

在 SAP 中,Primary Cost Element(初级成本要素)与Secondary Cost Element(次级成本要素)的核心区别、作用及业务含义可归纳如下

摘要:SAP中初级成本要素与次级成本要素的核心区别在于:初级要素必须关联FI模块的损益科目,直接影响财务报表,用于记录外部成本(如电费、薪酬);次级要素仅存在于CO模块,用于内部成本分配(如分摊、作业分配),不影响外部报表。初级要素是"外部成本入口",次级要素是"内部成本搬运工",二者共同实现成本的多维度追踪与管理。

金牌架构师 759

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

malloc和free的实现原理解析

malloc和free的实现原理解析 ​ C语言中使用malloc可以分配一段连续的内存空间。在c/c++开发中,因为malloc属于C标准库函数,经常会使用其分配内存。malloc是在堆中分配一块可用内存给用户。作为一个使用频繁的基础函数,理解清楚其实现原理很有必要,因此本文主要探讨malloc的具体实现原理,以及在linux系统中这该函数的实现方式。 进程地址空间 ​ 如上图所示在一个32位系统中,可寻址的空间大小是4G,linux系统下0-3G是用户模式,3-4G是内核模式。而在用户模式下又分为

qq_18059143的博客 1471

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

malloc linux实现

转载: http://blog.youkuaiyun.com/zdl1016/article/details/8696514 这里分析linux 0.11内核源码中, 给内核使用的malloc函数(注意,是给内核使用的,不是给应用程序使用的,不是glibc) 直接上图: 解释:     这个图只举例bucket_size=16的桶列表.    第一个桶描述符中的buf中的obj已经大

dps game 461

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

malloc 的原理与产生内存碎片的原因

https://blog.myzony.com/mallocde-yuan-li-yu-chan-sheng-nei-cun-sui-pian-de-yuan-yin/ 什么是缺页中断? 当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作: 1、检查要访问的虚拟地址是否合法 2、查找/分配一个物理页 3、填充物理页内容(读取磁盘,或者直接置0,或者啥也不干) 4、建立映射关系...

HappyMrSpring 3124

常用内存分配函数kmalloc、vmalloc、malloc和mmap实现原理

c.malloc()分配内存后, 直接写,linux内核进入匿名页面的缺页异常,调用alloc_zeroed_user_highpage_movable分配一个新页面,这个PTE是可写的;mmap只是建立vma,并未实际分配物理页面读取文件内存,当播放器真正读取文件时,会频繁触发缺页异常,再从磁盘读取文件到页面高速缓存中,会导致磁盘读性能较差;当访问未映射的虚拟空间时,触发缺页异常,linxu内核会处理缺页异常,缺页异常服务程序中,会分配物理页,并建立虚拟地址到物理页的映射;第二次触发异常,触发写时复制;

m0_74282605的博客 1616

c语言中long和int的区别_C语言中malloc和kmalloc的区别

基本区别kmalloc和vmalloc是分配的是内核的内存,malloc分配的是用户的内存kmalloc保证分配的内存在物理上是连续的,vmalloc保证的是在虚拟地址空间上的连续,malloc不保证任何东西(这点是自己猜测的,不一定正确)kmalloc能分配的大小有限,vmalloc和malloc能分配的大小相对较大内存只有在要被DMA访问的时候才需要物理上连续vmalloc比kmalloc要慢...

weixin_35725310的博客 327
上一篇: linux 内存管理的slab机制分析
下一篇: 文件系统VFS数据结构
LinuxWorking
博客等级 码龄10年 24粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值