Linux系统中kernel module(内核模块)的加载机制是怎样工作的?
内核模块原理相关解答
加载内核模块的完整流程
- 首先由用户态工具(最常见的是
insmod、modprobe)读取后缀为.ko的模块二进制文件,调用内核提供的init_module系统调用将模块内容传入内核 - 内核首先做合法性校验:匹配模块编译时对应的内核版本和当前运行内核版本是否兼容,检查模块依赖的内核符号是否都存在,校验加载权限(一般需要root权限才能加载模块)
- 校验通过后,内核会在内核专属的虚拟地址空间中分配对应大小的内存,分别存放模块的可执行代码段(text段)、已初始化数据段(data段)、零初始化数据段(bss段)
- 完成符号重定位:把模块代码里引用的内核函数、全局变量的占位符,替换成内核实际的虚拟地址
- 执行模块开发者通过
module_init()宏注册的初始化函数,完成模块自身的业务初始化,比如注册设备驱动、创建sys/proc文件节点、初始化硬件设备等 - 最后将模块加入内核全局的已加载模块链表,后续可以通过
lsmod命令查看到该模块的加载状态、依赖关系
内核模块的运行形态
内核模块不是独立进程,加载完成后就属于内核代码的一部分,和内核原生的核心组件享有完全相同的权限,全部运行在内核态。
它没有独立的进程上下文,也没有专属的进程控制块和地址空间:触发模块代码执行时,如果是响应用户进程的系统调用,就运行在当前触发请求的用户进程的内核上下文中;如果是响应中断、软中断这类异步事件,就运行在对应的内核中断上下文中。
内核模块的段内存分配位置
已加载模块的所有内存段都分配在内核虚拟地址空间范围内,不会和任何用户态进程的地址空间重叠:
- text段(可执行代码)会被分配到内核地址空间中标记为「只读+可执行」的模块专属内存区域,现代开启了KASLR(内核地址空间布局随机化)的系统会随机化该区域的起始地址,降低漏洞被利用的风险
- data段(存储已初始化的全局变量、静态变量)和bss段(存储未初始化的全局变量、静态变量)会被分配到内核地址空间中标记为「可读写」的模块专属内存区域
对于32位Linux系统,内核虚拟地址空间一般是3GB~4GB的地址范围;对于64位Linux系统,内核虚拟地址空间一般是高地址的特定范围,完全独立于低地址的用户态地址空间。
内容的提问来源于stack exchange,提问作者bharath
相关产品推荐
相关产品推荐

