linux内核vmalloc原理与实现

说实话,Linux内核的vmalloc机制非常有趣。
我之前就想过这个问题,但不明白为什么这么复杂。

直接映射0~8 9 6 MB物理内存的区域。
直接在虚拟地址上加上3 GB就可以找到物理地址了。
这很简单,也很残酷。
但8 9 6 MB的限制太可笑了,后来我用vmalloc解决了这个问题。
不进行低地址映射;在更高地址空间动态创建映射关系。
这就像向内核内存添加一个扩展设备一样。

vmalloc的实现分几个步骤。
第一步是申请虚拟内存并调用get_vm_area函数。
内核首先找到vm_struct结构来存储地址信息,然后在vmlist链表中寻找连续的空闲虚拟地址。
此链接列表从 VMALLOC_START 到 VMALLOC_END。
您必须找到您想要的尺寸。
查找到后,维护全局地址管理;将新分配的vm_struct添加到后面。

第二步是映射物理内存,称为vmalloc_area_pages。
内核必须运行init进程的页表;因为init进程的页表是全局的共享。
一个单独的操作是锁定init_mm的页目录项,然后pmd和pte要逐步分配。
最后,通过alloc_area_pmd分配物理页框,将虚拟地址与物理地址关联起来。
这一步非常重要,因为记忆是无法量化的。

第三步是缺页异常处理。
当进程第一次访问vmalloc内存时。
页表尚未初始化,将触发页错误。
内核通过cr2 寄存器得到了错误的虚拟地址。
如果这个地址在vmalloc区域内。
将init进程的整个pgd/pmd/pte复制到当前进程页表中;然后同步地图。

查看代码; vmalloc 调用链如下所示。
要调整内存大小,首先调用__vmalloc,然后调用get_vm_area分配虚拟地址。
如果vmalloc_area_pages成功;该地址将被返回,如果失败,则会生成它。
该函数还采用 GFP_KERNEL 和 __GFP_HIGHMEM 参数;这意味着优先考虑从高级内存进行分配。

页表同步机制是在do_page_fault中实现的。
如果发生页面错误;内核检查错误代码和地址。
如果条件满足,这就是init复制进程的页表。
这就好比init进程是内存映射的主人,并向其他进程学习。

vmalloc的特点是应用条件多。
例如,内核模块可用于加载大型驱动程序;或者网络缓冲区可以处理大数据包。
但价格略低于性能;这是因为动态映射比直接映射需要更多的精力并且访问速率更慢。
另外,缺页处理也会延迟。
内存碎片也是一个问题。
虚拟地址是连续的,但物理地址可以是连续的。
您必须依靠合作伙伴系统来管理物理页面。

简而言之,vmalloc动态分配虚拟地址,绕过直接映射区域的限制,从而延迟物理内存映射。
core使用vmlist来管理虚拟地址;它依靠init进程页表作为模板来与其他进程同步。
牺牲一些性能换取大内存分配能力,非常适合频繁内核访问的情况。

深入理解 Linux I/O 系统

Linux I/O 系统是操作系统中处理数据输入和输出的部分。
简单来说,它可以让计算机更快地读写文件和网络。

首先我们来谈谈传统的 I/O,它使用 read() 和 write() 等系统调用。
这件事很麻烦。
它必须复制数据四次并更改状态四次。
这是极其低效的。
要读取数据,必须从磁盘到内核,然后从内核到用户空间,然后写入数据,反之亦然。
这段时间,处理器承受极大的压力,效率很差。

后来Linux想到了一个方法,开发了copyless技术,直接在内核中处理数据传输,不需要来回复制,效率高很多。
还有复用技术,可以同时处理多个连接,减少切换,适合网络服务。

I/O堆栈分为三层。
文件系统层负责用户调用,块层处理磁盘I/O,设备层直接处理硬件。

有几种缓冲机制,例如缓冲I/O,其中数据首先存储在内核缓冲区中,然后再读取或写入; mmap,直接将内存映射到文件,减少副本;直接I/O,绕过内核缓冲区,直接读写设备,从而减少副本。

最后,用户数据到磁盘的路径相当复杂,涉及多层缓冲,例如PageCache和BufferCache。

总而言之,Linux I/O 系统通过分层和缓冲机制提高了性能和可靠性。
开发者应根据具体情况选择合适的I/O模式。
例如,零拷贝用于传输大文件,多路复用用于网络服务,直接I/O用于数据库操作。
在这方面,你需要对I/O堆栈和缓冲机制有深入的了解,才能更好地优化它。
你会亲眼看到,如何使用这些技术取决于实际情况。

十分钟让你像大佬一样了解Linux内核

上周我读了一篇关于Linux内核源代码的文章。


太棒了,太棒了。
它是一个核心操作系统。

我注意我的行为。
比如CPU、内存、硬件。
还提供账户服务。

看源码,要有基础。

首先我们来谈谈C语言。
必须继续。
您需要了解索引、结构和宏。

推荐《C程序设计语言(第二版·新版)》这本书。
作者是 Dennis M. Ritchie 和 Brian W. Kernighan。
编译也是必要的。
您应该了解 AT&T 语法。

阅读《原理汇编(第2 册版)》。
俗称“龙之书”。

还需要操作系统知识。
处理、内存、文件系统 I/O 等。

选择架构。
ARM 或 X8 6
CPU机制必须了解。
寄存器、缓存、分页、中断、多核同步。

选择内核版本。
v5 .1 1 .7 稳定。

从 kernel.org 下载源代码。

剪切代码。
例如,如果选择ARM架构,则删除其他架构代码。

配置编译环境。
一个交叉部署链工具。

菜单配置配置。

编译。
zImage 导出图像。
这些工具很棒。

SourceInsight 很好用。
跳过代码,请求的功能有效。

Vim+Emacs+Ctags 也可以。
光调试技巧就可以学到。

在古代,printk是用来输出信息的。
使用 dmesg 查看日志。

编写核心模块。

/proc/kallsyms 搜索符号。

kgdb或QEMU远程调试。

必须添加代码注释。
变量、宏、函数等
复杂的逻辑需要付出更多的努力。
比如锁机制和内存分配。

渐渐地。
模块定位、代码切割、动态跟踪。

可以查看官方文档。
例如,“Linux 设备驱动程序”。

经典子系统可以解释。
比如CFS调度器和Ext4 文件系统。

就是这样。

Linux 内核 rcu(顺序) 锁实现原理与源码解析

等等,昨天我整理了服务器日志,在内核中看到了RCU标记。
然后我突然想到解决内存泄漏问题。
这是一个双核处理器。
凌晨三点,我在键盘上打字时手指抽筋。
当使用链接的 RCU 列表时,读取任务会像幽灵一样四处移动,而写入任务会默默地复制和修改。
当时我并不太明白这背后是什么。
rcu_assign_pointer 函数逐行翻转,我的头发在灯下飞得到处都是。
等等,还有一件事。
2 01 1 版本的内核中似乎加入了宽限期机制来解决这个老问题。
今天的处理器核心很容易超过数百个。
这个RCU怎么能工作得这么稳定呢?