随便说说 alloc 和 init

先抛出一个小问题,然后延伸引入接下来要说的

zuyuClass *z = [zuyuClass alloc];

zuyuClass *z1 = [z init];

zuyuClass *z2 = [z init];

NSLog(@”%p – %p – %p”,z,z1,z2);

思考一下是控制台的输出是怎么样的

思考过后代码试一下

z,z1,z2三个对象的地址是完全一样的

alloc 做了些什么?init 做了些什么?

OK我们现在正是开始今天的内容

想知道 alloc 都干了些什么.最简单的方式就是看源码对吧.来,咱们瞄一眼源码去

image.png

1

继续往下

image.png

2

继续

image.png

3

好,到这里简单说一下,会走到红色选中的位置,为什么会走到这儿呢? 通过下符号断点,显示的汇编中看出来,这咱们就简单的一句带过,不详细说.

image.png

简单说明

咱们继续往下

class_createInstance

image.png

4

继续往下

image.png

5

好了 划重点了开始

重点1.      size_t size = cls->instanceSize(extraBytes);     6518行

创建对象首先最重要的大家应该都知道啦  ,  内存 , 对吧. 这个 size 就是获取该对象需要多大的空间,从而进行字节对齐

(字节对齐简单来说就是 ,比如改对象需要12个字节, 那么就返回16字节 .如果该对象需要23个字节,那么就返回24字节 . 为什么要对齐?  玩过逆向,汇编或者研究过底层的朋友就比较理解这个,在这咱们就不多说了)

image.png

5.1获取该对象所需要的空间

image.png

5.2调用算法计算出所需要的内存空间

这是 alloc 做的第一件事 .计算并进行字节对齐.

咱们继续抛出一个问题,比如现在有个 zuyuClass 的类.  有NSString 类型的 name(8字节) 和 int 类型的 age(4字节) .根据字节对齐. 这个类的对象占用了多少字节的内存?

脑袋里边想着是16的,可以用代码输出一下 看一下 , 是24 .问题又来了 ,为什么是24个字节

重点2.        obj->initInstanceIsa(cls, hasCxxDtor)   6525行.

创建一个 isa.  isa 占用了8个字节 . 万物皆对象,对象皆有 isa

image.png

对象皆有 isa

ok.这个时候大家是不是就以为zuyuClass在内存中就占用24个字节了?

NO,NONONO .重点3来了  ——> obj = (id)calloc(1, size);   6523行

这个 calloc 最终会走到这里(下图)

image.png

calloc进行的内存对齐

image.png

16倍数对齐

所以最终 zuyuClass 的这个对象内存中占用的空间是32字节而不是24字节

alloc我们就说到这里

接下来简单说说 init. 为什么要简单说说呢,因为没办法详细说啊

来,我们看一眼源码

image.png

init源码

image.png

rootinit

看到了吧 .init 干嘛了?init 啥都没干!只是返回了自己!

所以也就解释了一开始抛出的问题  .是不是看到这里已经颠覆了你的 alloc 是开辟空间,init 是初始化的理解?

可能有同学要问一下,init 既然啥都没干,为啥还要 init 这个方法,要它何用?

一句话回答, 自定义初始化. 也就是重写 init.  

那么现在最后抛出一个问题

在创建某一个类的对象的时候,alloc 和 init 分别起到了什么作用. 是不是瞬间感觉脑海里的回答比之前高大上了许多 

作者:_zuyu  作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这是一个我的iOS交流群:869685378,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

<think>我们正在讨论Linux内核中的`page_alloc_init`函数。但是请注意,在提供的引用中并没有直接提到`page_alloc_init`函数,而是提到了`page_ext_init_flatmem``init_debug_pagealloc`等函数。实际上,在内核中并没有一个名为`page_alloc_init`的函数,可能是用户误写或者是指与页分配初始化相关的机制。 根据用户的问题,我们推测用户可能是想了解与页分配器初始化相关的函数或机制。在Linux内核中,页分配器(page allocator)的初始化主要是在`mm_init`函数中完成的,该函数在启动过程中被调用。在`mm_init`函数中,会调用多个与内存管理初始化相关的函数,其中与页分配直接相关的是`kmem_cache_init`(用于初始化slab分配器,而slab分配器建立在页分配器之上)`mem_init`(该函数会初始化页分配器的数据结构,如伙伴系统)。 但是,用户提到了`page_alloc_init`,这可能是对页分配初始化过程的一个统称,或者是指`bootmem_init`(在ARM64架构中,该函数在启动早期被调用,用于初始化bootmem,然后迁移到伙伴系统)等函数。 由于用户的问题中出现了`page_alloc_init`,而引用中并没有这个函数,我们可以认为用户可能是指`page_alloc`相关的初始化,即伙伴系统的初始化。在Linux内核中,伙伴系统的初始化是在`mem_init`函数中完成的。 因此,我们将围绕`mem_init`函数以及与之相关的页分配初始化过程进行解释。 ### Linux内核页分配初始化过程 在Linux内核启动过程中,内存管理子系统的初始化是一个关键步骤。页分配器(伙伴系统)的初始化主要发生在`mem_init`函数中,该函数通常定义在体系结构相关的代码中。例如,在x86架构中,`mem_init`函数位于`arch/x86/mm/init_64.c`中;在ARM64架构中,位于`arch/arm64/mm/init.c`中。 #### `mem_init`函数的主要任务 1. **释放不再需要的引导内存(bootmem)**:在启动早期,内核使用bootmem分配器来管理内存。当伙伴系统初始化完成后,bootmem分配器就不再需要了,因此会释放其占用的内存。 2. **初始化伙伴系统**:通过调用`free_all_bootmem`或类似函数,将可用内存添加到伙伴系统中。 3. **打印内存信息**:例如,打印系统内存的总量、可用内存等。 4. **设置内存热插拔**:如果配置了内存热插拔,则进行相关初始化。 5. **初始化高端内存(如果存在)**:在32位系统中,如果物理内存超过内核直接映射范围,则需要初始化高端内存。 #### 示例:x86架构的`mem_init` 以下是一个简化的x86架构的`mem_init`函数示例(来自Linux内核源码): ```c void __init mem_init(void) { /* 释放所有bootmem,将内存交给伙伴系统 */ free_all_bootmem(); /* 设置内存模型为SPARSE(如果配置) */ mem_init_print_info(); /* 其他初始化,如设置页表等 */ } ``` #### 在`mm_init`中的调用 在系统初始化过程中,`mem_init`函数在`mm_init`函数中被调用。如引用中所示: ```c static void __init mm_init(void) { page_ext_init_flatmem(); init_debug_pagealloc(); report_meminit(); mem_init(); // 这里初始化页分配器(伙伴系统) kmem_cache_init(); kmemleak_init(); pgtable_init(); debug_objects_mem_init(); vmalloc_init(); ioremap_huge_init(); init_espfix_bsp(); pti_init(); } ``` #### 调试页分配器初始化 在`mm_init`中,还有一个函数`init_debug_pagealloc`,它用于初始化调试页分配的功能(如果配置了`CONFIG_DEBUG_PAGEALLOC`)。该功能用于检测内存访问错误,例如释放后使用等。 ### 相关问题 1. Linux内核中伙伴系统初始化的主要步骤是什么? 2. 在Linux内核启动过程中,`mem_init`函数的作用是什么? 3. 如何调试Linux内核的页分配器? 4. `bootmem`分配器与伙伴系统之间的关系是什么? 5. 在ARM64架构中,页分配器的初始化有什么不同? ### 总结 虽然用户直接提到了`page_alloc_init`函数,但该函数在内核中并不存在。实际上,页分配器的初始化主要是通过`mem_init`函数完成的,该函数在内存管理初始化函数`mm_init`中被调用。`mem_init`负责将启动期间使用的bootmem分配器迁移到伙伴系统,并初始化伙伴系统的数据结构。同时,`mm_init`函数中还有其他与内存管理相关的初始化函数,如`kmem_cache_init`(slab分配器初始化)等。 如果用户需要更具体的信息,建议提供更准确的内核版本或函数名,以便进一步查找。 [^1]: 引用内容来自Linux内核源码(linux-5.6.18),具体位置在`init/main.c`的`mm_init`函数定义。</think>### Linux内核中页分配初始化机制解析 在Linux内核中,`page_alloc_init`并非直接存在的函数名,而是指代页分配器(page allocator)的初始化过程。该机制的核心函数是`mm_init()`(位于`init/main.c`),其中包含页分配相关的关键初始化步骤[^1]。 #### 页分配初始化流程 1. **内存基础结构初始化** ```c page_ext_init_flatmem(); // 初始化页扩展元数据结构 init_debug_pagealloc(); // 启用页分配调试功能(如CONFIG_DEBUG_PAGEALLOC) report_meminit(); // 报告内存初始化状态 ``` - `page_ext_init_flatmem()`:为每个物理页初始化扩展元数据(如页迁移跟踪、内存泄漏检测等) - `init_debug_pagealloc()`:当启用调试选项时,会映射保护页检测越界访问 2. **伙伴系统初始化** ```c mem_init(); // 核心页分配器初始化(伙伴系统) ``` - 释放启动内存(bootmem)到伙伴系统 - 建立`free_area`数据结构管理空闲页 - 计算并初始化内存区域(ZONE_DMA/ZONE_NORMAL等) 3. **高层分配器构建** ```c kmem_cache_init(); // 初始化SLAB/SLUB分配器(基于页分配器) vmalloc_init(); // 初始化虚拟内存分配子系统 ``` #### 关键机制详解 1. **伙伴系统(Buddy System)** - 将物理内存分成2^n大小的块(order 0 = 4KB) - 通过位图记录相邻空闲块,实现快速合并/拆分 - 分配流程: ```mermaid graph TD A[分配请求] --> B{所需大小} B -->|小尺寸| C[SLAB分配器] B -->|大块| D[伙伴系统] D --> E[查找对应order空闲链表] E --> F{找到?} F -->|是| G[分配页面] F -->|否| H[拆分更高order块] ``` 2. **调试机制(init_debug_pagealloc)** - 功能:检测常见内存错误 - 释放后使用(use-after-free):通过页表解除映射 - 越界访问:在分配页周围添加保护页 - 依赖`CONFIG_DEBUG_PAGEALLOC`内核配置 3. **NUMA优化** - 多节点系统通过`pgdat_init()`初始化(未在示例显示) - 每个NUMA节点维护独立伙伴系统 #### 典型应用场景 1. **内核模块加载**:分配模块代码/数据页 2. **进程创建**:为task_struct内核栈分配页 3. **文件缓存**:为page cache分配页 4. **设备驱动**:DMA缓冲区分配(需ZONE_DMA内存) #### 调试技巧 1. 查看伙伴系统状态: ```bash cat /proc/buddyinfo ``` 2. 检测页分配错误: ```bash dmesg | grep -i 'page allocation failure' ``` 3. 调试选项启用: ```kconfig CONFIG_DEBUG_PAGEALLOC=y CONFIG_PAGE_POISONING=y ``` ### 相关问题 1. 伙伴系统如何处理内存碎片问题? 2. Linux内核如何区分ZONE_DMAZONE_NORMAL的内存分配? 3. 当系统内存不足时,页分配器会触发哪些回收机制? 4. 如何在内核模块中安全地分配大块连续物理内存? 5. `CONFIG_DEBUG_PAGEALLOC`对系统性能有何影响? [^1]: `mm_init()`函数定义位置:`linux-5.6.18\init\main.c#795`,包含页分配核心初始化逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值